implement new django-based models and loadmappkg command

This commit is contained in:
Laura Klünder 2016-08-28 17:59:52 +02:00
parent 66661209d2
commit 752b7d6d7d
14 changed files with 318 additions and 196 deletions

View file

@ -10,7 +10,7 @@
var map = L.map('mapeditor', { var map = L.map('mapeditor', {
center: [120, 200], center: [120, 200],
zoom: 2, zoom: 2,
maxBounds: [[0, 0], [{{ map.height }}, {{ map.width }}]], maxBounds: {{ bounds }},
maxZoom: 10, maxZoom: 10,
minZoom: 1, minZoom: 1,
crs: L.CRS.Simple, crs: L.CRS.Simple,
@ -19,10 +19,10 @@ var map = L.map('mapeditor', {
}); });
// Add Source Layers // Add Source Layers
{% for pkg in map.pkgs.values %} {% for pkg in packages %}
L.control.layers([], { L.control.layers([], {
{% for source in pkg.sources %} {% for source in pkg.sources.all %}
"{{ source.name }}": L.imageOverlay('{% url 'map.source' filename=source.filename %}', {{ source.jsbounds }}),{% endfor %} "{{ source.name }}": L.imageOverlay('{% url 'map.source' source=source.name %}', {{ source.jsbounds }}),{% endfor %}
}).addTo(map); }).addTo(map);
{% endfor %} {% endfor %}
@ -33,10 +33,10 @@ L.LevelControl = L.Control.extend({
}, },
onAdd: function (map) { onAdd: function (map) {
var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar leaflet-levels'), link; var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar leaflet-levels'), link;
{% for level in map.levels reversed %} {% for level in levels reversed %}
link = L.DomUtil.create('a', '{% if current_level == level %}current{% endif %}', container); link = L.DomUtil.create('a', '{% if current_level == level %}current{% endif %}', container);
link.href = '{% url "control.editor" level=level %}'; link.href = '{% url "control.editor" level=level.name %}';
link.innerHTML = '{{ level }}'; link.innerHTML = '{{ level.name }}';
{% endfor %} {% endfor %}
return container; return container;
} }

View file

@ -1,8 +1,9 @@
from django.contrib.admin.views.decorators import staff_member_required import json
from django.http import Http404
from django.shortcuts import redirect, render
from ..mapdata import mapmanager from django.contrib.admin.views.decorators import staff_member_required
from django.shortcuts import get_object_or_404, redirect, render
from ..mapdata.models import MapLevel, MapPackage, MapSource
@staff_member_required @staff_member_required
@ -13,10 +14,12 @@ def dashboard(request):
@staff_member_required @staff_member_required
def editor(request, level=None): def editor(request, level=None):
if not level: if not level:
return redirect('control.editor', level=mapmanager.levels[0]) return redirect('control.editor', level=MapLevel.objects.first().name)
if level not in mapmanager.levels:
raise Http404('Level does not exist') level = get_object_or_404(MapLevel, name=level)
return render(request, 'control/editor.html', { return render(request, 'control/editor.html', {
'map': mapmanager, 'bounds': json.dumps(MapSource.max_bounds()),
'packages': MapPackage.objects.all(),
'levels': MapLevel.objects.all(),
'current_level': level, 'current_level': level,
}) })

View file

@ -1,5 +0,0 @@
from .classes import MapManager
default_app_config = 'c3nav.mapdata.apps.MapdataConfig'
mapmanager = MapManager()

View file

@ -1,25 +0,0 @@
from django.apps import AppConfig
from django.conf import settings
from django.core.checks import Warning, register
from . import mapmanager
@register()
def has_map_data_check(app_configs, **kwargs):
if not settings.MAP_DIRS:
return [Warning(
'There are no map data directories configured.',
hint='Add mapdirs=/path/to/directory to your c3nav.cfg.',
id='mapdata.W001',
)]
return []
class MapdataConfig(AppConfig):
name = 'c3nav.mapdata'
verbose_name = 'map data manager'
def ready(self):
for map_dir in settings.MAP_DIRS:
mapmanager.add_map_dir(map_dir)

View file

@ -1,76 +0,0 @@
import json
import os
from collections import OrderedDict
class MapInitError(Exception):
pass
class MapManager:
def __init__(self):
self.main_pkg = None
self.pkgs = OrderedDict()
self.levels = []
self.sources_by_filename = OrderedDict()
def add_map_dir(self, path):
pkg = MapDataPackage(path)
if pkg.name in self.pkgs:
raise MapInitError('Duplicate map package: '+pkg.name)
if pkg.extends is None:
if self.main_pkg is not None:
raise MapInitError('There can not be more than one root map package: tried to add '+pkg.name+', '
'but '+self.main_pkg.name+' was there first.')
self.main_pkg = pkg
self.levels = pkg.levels
self.width = pkg.width
self.height = pkg.height
else:
if pkg.extends not in self.pkgs:
raise MapInitError('map package'+pkg.name+' extends '+pkg.exends+', which was not imported '
'beforehand.')
for source in pkg.sources:
self.sources_by_filename[source.filename] = source
self.pkgs[pkg.name] = pkg
class MapDataPackage:
def __init__(self, path):
self.path = path
main_file = os.path.join(path, 'map.json')
try:
data = json.load(open(main_file))
except FileNotFoundError:
raise MapInitError(main_file+' not found')
except json.decoder.JSONDecodeError as e:
raise MapInitError('Could not decode '+main_file+': '+str(e))
self.name = data.get('name')
if self.name is None:
raise MapInitError('Map package '+path+' has no name in map.json.')
self.extends = data.get('extends')
self.width = data.get('width')
self.height = data.get('height')
self.sources = tuple(MapSource(self, source) for source in data.get('sources', []))
self.levels = data.get('levels')
class MapSource:
def __init__(self, pkg, data):
self.name = data['name']
self.filename = self.name+'.'+data['src'].split('.')[-1]
self.src = os.path.join(pkg.path, data['src'])
self.bounds = data['bounds']
@property
def jsbounds(self):
return json.dumps(self.bounds)

View file

View file

@ -0,0 +1,19 @@
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from ...packageio import MapPackagesIO
class Command(BaseCommand):
help = 'Load the given map packages into the database'
def add_arguments(self, parser):
parser.add_argument('mappkgdir', nargs='+', type=str, help='map package directories')
parser.add_argument('-y', action='store_const', const=True, default=False,
help='don\'t ask for confirmation')
def handle(self, *args, **options):
with transaction.atomic():
MapPackagesIO(options['mappkgdir']).update_to_db()
print()
if input('Confirm (y/N): ') != 'y':
raise CommandError('Aborted.')

View file

@ -1,7 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.9.9 on 2016-08-25 11:37 # Generated by Django 1.9.9 on 2016-08-28 15:52
from __future__ import unicode_literals from __future__ import unicode_literals
import c3nav.mapdata.models
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import parler.models import parler.models
@ -16,72 +17,78 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Bounds', name='MapFeature',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('bottom', models.DecimalField(decimal_places=2, max_digits=6, verbose_name='bottom coordinate')), ('name', models.CharField(help_text='e.g. noc', max_length=50, unique=True, verbose_name='feature identifier')),
('left', models.DecimalField(decimal_places=2, max_digits=6, verbose_name='left coordinate')), ('type', models.CharField(choices=[('building', 'Building'), ('room', 'Room'), ('obstacle', 'Obstacle')], max_length=50)),
('top', models.DecimalField(decimal_places=2, max_digits=6, verbose_name='top coordinate')), ('geometry', models.TextField()),
('right', models.DecimalField(decimal_places=2, max_digits=6, verbose_name='right coordinate')),
], ],
options={
'abstract': False,
},
bases=(parler.models.TranslatableModelMixin, models.Model),
),
migrations.CreateModel(
name='MapFeatureTranslation',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('language_code', models.CharField(db_index=True, max_length=15, verbose_name='Language')),
('title', models.CharField(max_length=50, verbose_name='package title')),
('master', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='mapdata.MapFeature')),
],
options={
'managed': True,
'db_tablespace': '',
'verbose_name': 'map feature Translation',
'db_table': 'mapdata_mapfeature_translation',
'default_permissions': (),
},
), ),
migrations.CreateModel( migrations.CreateModel(
name='MapLevel', name='MapLevel',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(help_text='Usually just an integer (e.g. -1, 0, 1, 2)', max_length=50, unique=True, verbose_name='level name')), ('name', models.CharField(help_text='Usually just an integer (e.g. -1, 0, 1, 2)', max_length=50, unique=True, verbose_name='level name')),
('altitude', models.DecimalField(decimal_places=2, max_digits=6, null=True, verbose_name='level altitude')),
], ],
options={
'abstract': False,
},
bases=(parler.models.TranslatableModelMixin, models.Model),
), ),
migrations.CreateModel( migrations.CreateModel(
name='MapPackage', name='MapPackage',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(help_text='e.g. de.c3nav.33c3.base', max_length=50, unique=True, verbose_name='package identifier')), ('name', models.CharField(help_text='e.g. de.c3nav.33c3.base', max_length=50, unique=True, verbose_name='package identifier')),
('map', models.CharField(help_text='e.g. de.c3nav.33c3', max_length=50, verbose_name='map identifier')), ('bottom', models.DecimalField(decimal_places=2, max_digits=6, null=True, verbose_name='bottom coordinate')),
('bounds', models.OneToOneField(null=True, on_delete=django.db.models.deletion.PROTECT, to='mapdata.Bounds', verbose_name='bounds')), ('left', models.DecimalField(decimal_places=2, max_digits=6, null=True, verbose_name='left coordinate')),
('top', models.DecimalField(decimal_places=2, max_digits=6, null=True, verbose_name='top coordinate')),
('right', models.DecimalField(decimal_places=2, max_digits=6, null=True, verbose_name='right coordinate')),
], ],
options={
'abstract': False,
},
bases=(parler.models.TranslatableModelMixin, models.Model),
),
migrations.CreateModel(
name='MapPackageTranslation',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('language_code', models.CharField(db_index=True, max_length=15, verbose_name='Language')),
('title', models.CharField(max_length=50, verbose_name='package title')),
('master', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='mapdata.MapPackage')),
],
options={
'db_tablespace': '',
'verbose_name': 'map package Translation',
'managed': True,
'db_table': 'mapdata_mappackage_translation',
'default_permissions': (),
},
), ),
migrations.CreateModel( migrations.CreateModel(
name='MapSource', name='MapSource',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.SlugField(unique=True, verbose_name='source name')), ('name', models.SlugField(unique=True, verbose_name='source name')),
('image', models.FileField(upload_to='mapsources/', verbose_name='source image')), ('image', models.FileField(max_length=70, storage=c3nav.mapdata.models.MapSourceImageStorage(), upload_to=c3nav.mapdata.models.map_source_filename, verbose_name='source image')),
('bounds', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to='mapdata.Bounds', verbose_name='bounds')), ('bottom', models.DecimalField(decimal_places=2, max_digits=6, verbose_name='bottom coordinate')),
('package', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='sources', to='mapdata.MapPackage', verbose_name='map package')), ('left', models.DecimalField(decimal_places=2, max_digits=6, verbose_name='left coordinate')),
('top', models.DecimalField(decimal_places=2, max_digits=6, verbose_name='top coordinate')),
('right', models.DecimalField(decimal_places=2, max_digits=6, verbose_name='right coordinate')),
('package', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sources', to='mapdata.MapPackage', verbose_name='map package')),
], ],
), ),
migrations.AddField( migrations.AddField(
model_name='maplevel', model_name='maplevel',
name='package', name='package',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='levels', to='mapdata.MapPackage', verbose_name='map package'), field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='levels', to='mapdata.MapPackage', verbose_name='map package'),
),
migrations.AddField(
model_name='mapfeature',
name='package',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='features', to='mapdata.MapPackage', verbose_name='map package'),
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='mappackagetranslation', name='mapfeaturetranslation',
unique_together=set([('language_code', 'master')]), unique_together=set([('language_code', 'master')]),
), ),
] ]

View file

@ -1,50 +1,122 @@
from django.db import models import json
import os
from django.conf import settings
from django.db import models, transaction
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.dispatch import receiver
from django.core.files.storage import FileSystemStorage
from parler.models import TranslatedFields, TranslatableModel from parler.models import TranslatedFields, TranslatableModel
class Bounds(models.Model): class MapPackage(models.Model):
bottom = models.DecimalField(_('bottom coordinate'), max_digits=6, decimal_places=2)
left = models.DecimalField(_('left coordinate'), max_digits=6, decimal_places=2)
top = models.DecimalField(_('top coordinate'), max_digits=6, decimal_places=2)
right = models.DecimalField(_('right coordinate'), max_digits=6, decimal_places=2)
@property
def __iter__(self):
return iter(((self.bottom, self.left), (self.top, self.right)))
class MapPackage(TranslatableModel):
""" """
A c3nav map package A c3nav map package
""" """
name = models.CharField(_('package identifier'), unique=True, max_length=50, name = models.CharField(_('package identifier'), unique=True, max_length=50,
help_text=_('e.g. de.c3nav.33c3.base')) help_text=_('e.g. de.c3nav.33c3.base'))
map = models.CharField(_('map identifier'), max_length=50, help_text=_('e.g. de.c3nav.33c3'))
bounds = models.OneToOneField('Bounds', null=True, on_delete=models.PROTECT, verbose_name=_('bounds'))
translations = TranslatedFields( bottom = models.DecimalField(_('bottom coordinate'), null=True, max_digits=6, decimal_places=2)
title=models.CharField(_('package title'), max_length=50), left = models.DecimalField(_('left coordinate'), null=True, max_digits=6, decimal_places=2)
) top = models.DecimalField(_('top coordinate'), null=True, max_digits=6, decimal_places=2)
right = models.DecimalField(_('right coordinate'), null=True, max_digits=6, decimal_places=2)
class MapLevel(TranslatableModel): class MapLevel(models.Model):
""" """
A map level (-1, 0, 1, 2) A map level (-1, 0, 1, 2)
""" """
name = models.CharField(_('level name'), max_length=50, unique=True, name = models.CharField(_('level name'), max_length=50, unique=True,
help_text=_('Usually just an integer (e.g. -1, 0, 1, 2)')) help_text=_('Usually just an integer (e.g. -1, 0, 1, 2)'))
package = models.ForeignKey('MapPackage', on_delete=models.PROTECT, related_name='levels', altitude = models.DecimalField(_('level altitude'), null=True, max_digits=6, decimal_places=2)
package = models.ForeignKey('MapPackage', on_delete=models.CASCADE, related_name='levels',
verbose_name=_('map package')) verbose_name=_('map package'))
class Meta:
ordering = ['altitude']
class MapSourceImageStorage(FileSystemStorage):
def get_available_name(self, name, *args, max_length=None, **kwargs):
if self.exists(name):
os.remove(os.path.join(settings.MEDIA_ROOT, name))
return super().get_available_name(name, *args, max_length, **kwargs)
def map_source_filename(instance, filename):
return os.path.join('mapsources', '%s.%s' % (instance.name, filename.split('.')[-1]))
class MapSource(models.Model): class MapSource(models.Model):
""" """
A map source, images of levels that can be useful as backgrounds for the map editor A map source, images of levels that can be useful as backgrounds for the map editor
""" """
name = models.SlugField(_('source name'), max_length=50, unique=True) name = models.SlugField(_('source name'), max_length=50, unique=True)
package = models.ForeignKey('MapPackage', on_delete=models.PROTECT, related_name='sources', package = models.ForeignKey('MapPackage', on_delete=models.CASCADE, related_name='sources',
verbose_name=_('map package')) verbose_name=_('map package'))
image = models.FileField(_('source image'), upload_to='mapsources/')
bounds = models.OneToOneField('Bounds', on_delete=models.PROTECT, verbose_name=_('bounds')) image = models.FileField(_('source image'), max_length=70,
upload_to=map_source_filename, storage=MapSourceImageStorage())
bottom = models.DecimalField(_('bottom coordinate'), max_digits=6, decimal_places=2)
left = models.DecimalField(_('left coordinate'), max_digits=6, decimal_places=2)
top = models.DecimalField(_('top coordinate'), max_digits=6, decimal_places=2)
right = models.DecimalField(_('right coordinate'), max_digits=6, decimal_places=2)
@classmethod
def max_bounds(cls):
result = cls.objects.all().aggregate(models.Min('bottom'), models.Min('left'),
models.Max('top'), models.Max('right'))
return ((float(result['bottom__min']), float(result['left__min'])),
(float(result['top__max']), float(result['right__max'])))
@property
def bounds(self):
return ((self.bottom, self.left), (self.top, self.right))
@property
def jsbounds(self):
return json.dumps(((float(self.bottom), float(self.left)), (float(self.top), float(self.right))))
@receiver(models.signals.post_delete, sender=MapSource)
def delete_image_on_mapsource_delete(sender, instance, **kwargs):
transaction.on_commit(lambda: instance.image.delete(save=False))
@receiver(models.signals.pre_save, sender=MapSource)
def delete_image_on_mapsource_change(sender, instance, **kwargs):
if not instance.pk:
return False
try:
old_file = MapSource.objects.get(pk=instance.pk).image
except MapSource.DoesNotExist:
return False
new_file = instance.image
if map_source_filename(instance, new_file.name) != old_file.name:
transaction.on_commit(lambda: old_file.delete(save=False))
class MapFeature(TranslatableModel):
"""
A map feature
"""
TYPES = (
('building', _('Building')),
('room', _('Room')),
('obstacle', _('Obstacle')),
)
name = models.CharField(_('feature identifier'), unique=True, max_length=50, help_text=_('e.g. noc'))
package = models.ForeignKey('MapPackage', on_delete=models.CASCADE, related_name='features',
verbose_name=_('map package'))
type = models.CharField(max_length=50, choices=TYPES)
geometry = models.TextField()
translations = TranslatedFields(
title=models.CharField(_('package title'), max_length=50),
)

View file

@ -0,0 +1,132 @@
import json
import os
from collections import OrderedDict
from django.core.files import File
from django.core.management.base import CommandError
class PackageIOError(CommandError):
pass
class MapPackagesIO():
def __init__(self, directories):
print('Opening Map Packages…')
self.packages = OrderedDict()
self.levels = OrderedDict()
self.sources = OrderedDict()
for directory in directories:
print('- '+directory)
try:
package = json.load(open(os.path.join(directory, 'pkg.json')))
except FileNotFoundError:
raise PackageIOError('no pkg.json found in %s' % directory)
if package['name'] in self.packages:
raise PackageIOError('Duplicate package name: %s' % package['name'])
if 'bounds' in package:
self._validate_bounds(package['bounds'])
package['directory'] = directory
self.packages[package['name']] = package
for level in package.get('levels', []):
level = level.copy()
if level['name'] in self.levels:
raise PackageIOError('Duplicate level name: %s in packages %s and %s' %
(level, self.levels[level]['name'], package['name']))
if not isinstance(level['altitude'], (int, float)):
raise PackageIOError('levels: %s: altitude has to be int or float.' % level['name'])
level['package'] = package['name']
self.levels[level['name']] = level
for source in package.get('sources', []):
source = source.copy()
if source['name'] in self.sources:
raise PackageIOError('Duplicate source name: %s in packages %s and %s' %
(source['name'], self.sources[source['name']]['name'], package['name']))
self._validate_bounds(source['bounds'], 'sources: %s: ' % source['name'])
source['filename'] = os.path.join(directory, source['src'])
if not os.path.isfile(source['filename']):
raise PackageIOError('Source file not found: '+source['filename'])
source['package'] = package['name']
self.sources[source['name']] = source
def _validate_bounds(self, bounds, prefix=''):
if len(bounds) != 2 or len(bounds[0]) != 2 or len(bounds[1]) != 2:
raise PackageIOError(prefix+'Invalid bounds format.')
if not all(isinstance(i, (float, int)) for i in sum(bounds, [])):
raise PackageIOError(prefix+'All bounds coordinates have to be int or float.')
if bounds[0][0] >= bounds[1][0] or bounds[0][1] >= bounds[1][1]:
raise PackageIOError(prefix+'bounds: lower coordinate has to be first.')
def update_to_db(self):
from .models import MapPackage, MapLevel, MapSource
print('Updating Map database…')
# Add new Packages
packages = {}
print('- Updating packages…')
for name, package in self.packages.items():
bounds = package.get('bounds')
defaults = {
'bottom': bounds[0][0],
'left': bounds[0][1],
'top': bounds[1][0],
'right': bounds[1][1],
} if bounds else {}
package, created = MapPackage.objects.update_or_create(name=name, defaults=defaults)
packages[name] = package
if created:
print('- Created package: '+name)
# Add new levels
print('- Updating levels…')
for name, level in self.levels.items():
package, created = MapLevel.objects.update_or_create(name=name, defaults={
'package': packages[level['package']],
'altitude': level['altitude'],
'name': level['name'],
})
if created:
print('- Created level: '+name)
# Add new map sources
print('- Updating sources…')
for name, source in self.sources.items():
source, created = MapSource.objects.update_or_create(name=name, defaults={
'package': packages[source['package']],
'image': File(open(source['filename'], 'rb')),
'bottom': source['bounds'][0][0],
'left': source['bounds'][0][1],
'top': source['bounds'][1][0],
'right': source['bounds'][1][1],
})
if created:
print('- Created source: '+name)
# Remove old sources
for source in MapSource.objects.exclude(name__in=self.sources.keys()):
print('- Deleted source: '+source.name)
source.delete()
# Remove old levels
for level in MapLevel.objects.exclude(name__in=self.levels.keys()):
print('- Deleted level: '+level.name)
level.delete()
# Remove old packages
for package in MapPackage.objects.exclude(name__in=self.packages.keys()):
print('- Deleted package: '+package.name)
package.delete()

View file

@ -3,5 +3,6 @@ from django.conf.urls import url
from . import views from . import views
urlpatterns = [ urlpatterns = [
url(r'^sources/(?P<filename>[^/]+)$', views.source, name='map.source'), url(r'^sources/(?P<source>[^/]+)$', views.source, name='map.source'),
url(r'^data/add$', views.source, name='map.edit.source'),
] ]

View file

@ -1,19 +1,15 @@
import mimetypes import mimetypes
from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admin.views.decorators import staff_member_required
from django.http import Http404, HttpResponse from django.http import HttpResponse
from django.shortcuts import render from django.shortcuts import get_object_or_404, render
from .models import MapSource
from ..mapdata import mapmanager
@staff_member_required @staff_member_required
def source(request, filename): def source(request, source):
source = mapmanager.sources_by_filename.get(filename) source = get_object_or_404(MapSource, name=source)
if source is None: response = HttpResponse(content_type=mimetypes.guess_type(source.image.name)[0])
raise Http404('Source does not exist') for chunk in source.image.chunks():
response.write(chunk)
response = HttpResponse(content_type=mimetypes.guess_type(source.src)[0])
with open(source.src, 'rb') as f:
response.write(f.read())
return response return response

View file

@ -18,8 +18,6 @@ DATA_DIR = config.get('c3nav', 'datadir', fallback=os.environ.get('DATA_DIR', 'd
LOG_DIR = os.path.join(DATA_DIR, 'logs') LOG_DIR = os.path.join(DATA_DIR, 'logs')
MEDIA_ROOT = os.path.join(DATA_DIR, 'media') MEDIA_ROOT = os.path.join(DATA_DIR, 'media')
MAP_DIRS = tuple(n for n in config.get('c3nav', 'mapdirs', fallback='').split(',') if n)
if not os.path.exists(DATA_DIR): if not os.path.exists(DATA_DIR):
os.mkdir(DATA_DIR) os.mkdir(DATA_DIR)
if not os.path.exists(LOG_DIR): if not os.path.exists(LOG_DIR):