From e8824a02d482416d0041f3feeb3d62829bd16f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Mon, 29 Aug 2016 18:49:24 +0200 Subject: [PATCH] refactor models --- src/c3nav/control/views.py | 12 ++-- src/c3nav/mapdata/migrations/0001_initial.py | 39 ++++++----- src/c3nav/mapdata/models/__init__.py | 4 ++ src/c3nav/mapdata/models/feature.py | 25 +++++++ src/c3nav/mapdata/models/level.py | 16 +++++ src/c3nav/mapdata/models/package.py | 15 +++++ .../mapdata/{models.py => models/source.py} | 66 +++---------------- src/c3nav/mapdata/packageio.py | 1 + src/c3nav/mapdata/views.py | 4 +- 9 files changed, 98 insertions(+), 84 deletions(-) create mode 100644 src/c3nav/mapdata/models/__init__.py create mode 100644 src/c3nav/mapdata/models/feature.py create mode 100644 src/c3nav/mapdata/models/level.py create mode 100644 src/c3nav/mapdata/models/package.py rename src/c3nav/mapdata/{models.py => models/source.py} (51%) diff --git a/src/c3nav/control/views.py b/src/c3nav/control/views.py index 1541ce04..85c83df3 100644 --- a/src/c3nav/control/views.py +++ b/src/c3nav/control/views.py @@ -3,7 +3,7 @@ import json 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 +from ..mapdata.models import Level, Package, Source @staff_member_required @@ -14,12 +14,12 @@ def dashboard(request): @staff_member_required def editor(request, level=None): if not level: - return redirect('control.editor', level=MapLevel.objects.first().name) + return redirect('control.editor', level=Level.objects.first().name) - level = get_object_or_404(MapLevel, name=level) + level = get_object_or_404(Level, name=level) return render(request, 'control/editor.html', { - 'bounds': json.dumps(MapSource.max_bounds()), - 'packages': MapPackage.objects.all(), - 'levels': MapLevel.objects.all(), + 'bounds': json.dumps(Source.max_bounds()), + 'packages': Package.objects.all(), + 'levels': Level.objects.all(), 'current_level': level, }) diff --git a/src/c3nav/mapdata/migrations/0001_initial.py b/src/c3nav/mapdata/migrations/0001_initial.py index e7ec9275..0e151207 100644 --- a/src/c3nav/mapdata/migrations/0001_initial.py +++ b/src/c3nav/mapdata/migrations/0001_initial.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.9 on 2016-08-28 15:52 +# Generated by Django 1.9.9 on 2016-08-29 16:48 from __future__ import unicode_literals -import c3nav.mapdata.models +import c3nav.mapdata.models.source from django.db import migrations, models import django.db.models.deletion import parler.models @@ -17,7 +17,7 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='MapFeature', + name='Feature', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(help_text='e.g. noc', max_length=50, unique=True, verbose_name='feature identifier')), @@ -30,31 +30,34 @@ class Migration(migrations.Migration): bases=(parler.models.TranslatableModelMixin, models.Model), ), migrations.CreateModel( - name='MapFeatureTranslation', + name='FeatureTranslation', 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')), + ('master', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='mapdata.Feature')), ], options={ - 'managed': True, 'db_tablespace': '', - 'verbose_name': 'map feature Translation', - 'db_table': 'mapdata_mapfeature_translation', 'default_permissions': (), + 'db_table': 'mapdata_feature_translation', + 'managed': True, + 'verbose_name': 'feature Translation', }, ), migrations.CreateModel( - name='MapLevel', + name='Level', fields=[ ('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')), ('altitude', models.DecimalField(decimal_places=2, max_digits=6, null=True, verbose_name='level altitude')), ], + options={ + 'ordering': ['altitude'], + }, ), migrations.CreateModel( - name='MapPackage', + name='Package', fields=[ ('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')), @@ -65,30 +68,30 @@ class Migration(migrations.Migration): ], ), migrations.CreateModel( - name='MapSource', + name='Source', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.SlugField(unique=True, verbose_name='source name')), - ('image', models.FileField(max_length=70, storage=c3nav.mapdata.models.MapSourceImageStorage(), upload_to=c3nav.mapdata.models.map_source_filename, verbose_name='source image')), + ('image', models.FileField(max_length=70, storage=c3nav.mapdata.models.source.SourceImageStorage(), upload_to=c3nav.mapdata.models.source.map_source_filename, verbose_name='source image')), ('bottom', models.DecimalField(decimal_places=2, max_digits=6, verbose_name='bottom coordinate')), ('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')), + ('package', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sources', to='mapdata.Package', verbose_name='map package')), ], ), migrations.AddField( - model_name='maplevel', + model_name='level', name='package', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, 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.Package', verbose_name='map package'), ), migrations.AddField( - model_name='mapfeature', + model_name='feature', name='package', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='features', to='mapdata.MapPackage', verbose_name='map package'), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='features', to='mapdata.Package', verbose_name='map package'), ), migrations.AlterUniqueTogether( - name='mapfeaturetranslation', + name='featuretranslation', unique_together=set([('language_code', 'master')]), ), ] diff --git a/src/c3nav/mapdata/models/__init__.py b/src/c3nav/mapdata/models/__init__.py new file mode 100644 index 00000000..cefc7c5c --- /dev/null +++ b/src/c3nav/mapdata/models/__init__.py @@ -0,0 +1,4 @@ +from .feature import Feature # noqa +from .level import Level # noqa +from .package import Package # noqa +from .source import Source # noqa diff --git a/src/c3nav/mapdata/models/feature.py b/src/c3nav/mapdata/models/feature.py new file mode 100644 index 00000000..95dabd14 --- /dev/null +++ b/src/c3nav/mapdata/models/feature.py @@ -0,0 +1,25 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from parler.models import TranslatedFields, TranslatableModel + + +class Feature(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('Package', 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), + ) diff --git a/src/c3nav/mapdata/models/level.py b/src/c3nav/mapdata/models/level.py new file mode 100644 index 00000000..32d807fa --- /dev/null +++ b/src/c3nav/mapdata/models/level.py @@ -0,0 +1,16 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ + + +class Level(models.Model): + """ + A map level (-1, 0, 1, 2…) + """ + name = models.CharField(_('level name'), max_length=50, unique=True, + help_text=_('Usually just an integer (e.g. -1, 0, 1, 2)')) + altitude = models.DecimalField(_('level altitude'), null=True, max_digits=6, decimal_places=2) + package = models.ForeignKey('Package', on_delete=models.CASCADE, related_name='levels', + verbose_name=_('map package')) + + class Meta: + ordering = ['altitude'] diff --git a/src/c3nav/mapdata/models/package.py b/src/c3nav/mapdata/models/package.py new file mode 100644 index 00000000..8378153f --- /dev/null +++ b/src/c3nav/mapdata/models/package.py @@ -0,0 +1,15 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ + + +class Package(models.Model): + """ + A c3nav map package + """ + name = models.CharField(_('package identifier'), unique=True, max_length=50, + help_text=_('e.g. de.c3nav.33c3.base')) + + bottom = models.DecimalField(_('bottom coordinate'), null=True, max_digits=6, decimal_places=2) + 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) diff --git a/src/c3nav/mapdata/models.py b/src/c3nav/mapdata/models/source.py similarity index 51% rename from src/c3nav/mapdata/models.py rename to src/c3nav/mapdata/models/source.py index e4dd459d..944c72c1 100644 --- a/src/c3nav/mapdata/models.py +++ b/src/c3nav/mapdata/models/source.py @@ -7,37 +7,8 @@ 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 - -class MapPackage(models.Model): - """ - A c3nav map package - """ - name = models.CharField(_('package identifier'), unique=True, max_length=50, - help_text=_('e.g. de.c3nav.33c3.base')) - - bottom = models.DecimalField(_('bottom coordinate'), null=True, max_digits=6, decimal_places=2) - 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(models.Model): - """ - A map level (-1, 0, 1, 2…) - """ - name = models.CharField(_('level name'), max_length=50, unique=True, - help_text=_('Usually just an integer (e.g. -1, 0, 1, 2)')) - 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')) - - class Meta: - ordering = ['altitude'] - - -class MapSourceImageStorage(FileSystemStorage): +class SourceImageStorage(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)) @@ -48,16 +19,16 @@ def map_source_filename(instance, filename): return os.path.join('mapsources', '%s.%s' % (instance.name, filename.split('.')[-1])) -class MapSource(models.Model): +class Source(models.Model): """ 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) - package = models.ForeignKey('MapPackage', on_delete=models.CASCADE, related_name='sources', + package = models.ForeignKey('Package', on_delete=models.CASCADE, related_name='sources', verbose_name=_('map package')) image = models.FileField(_('source image'), max_length=70, - upload_to=map_source_filename, storage=MapSourceImageStorage()) + upload_to=map_source_filename, storage=SourceImageStorage()) bottom = models.DecimalField(_('bottom coordinate'), max_digits=6, decimal_places=2) left = models.DecimalField(_('left coordinate'), max_digits=6, decimal_places=2) @@ -80,43 +51,22 @@ class MapSource(models.Model): return json.dumps(((float(self.bottom), float(self.left)), (float(self.top), float(self.right)))) -@receiver(models.signals.post_delete, sender=MapSource) +@receiver(models.signals.post_delete, sender=Source) 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) +@receiver(models.signals.pre_save, sender=Source) 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: + old_file = Source.objects.get(pk=instance.pk).image + except Source.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), - ) diff --git a/src/c3nav/mapdata/packageio.py b/src/c3nav/mapdata/packageio.py index 33263357..7f0c242c 100644 --- a/src/c3nav/mapdata/packageio.py +++ b/src/c3nav/mapdata/packageio.py @@ -5,6 +5,7 @@ from collections import OrderedDict from django.core.files import File from django.core.management.base import CommandError +from .models import Package, Level, Source class PackageIOError(CommandError): diff --git a/src/c3nav/mapdata/views.py b/src/c3nav/mapdata/views.py index 8d4d4142..fea0e6f4 100644 --- a/src/c3nav/mapdata/views.py +++ b/src/c3nav/mapdata/views.py @@ -3,12 +3,12 @@ import mimetypes from django.contrib.admin.views.decorators import staff_member_required from django.http import HttpResponse from django.shortcuts import get_object_or_404, render -from .models import MapSource +from .models import Source @staff_member_required def source(request, source): - source = get_object_or_404(MapSource, name=source) + source = get_object_or_404(Source, name=source) response = HttpResponse(content_type=mimetypes.guess_type(source.image.name)[0]) for chunk in source.image.chunks(): response.write(chunk)