refactor models

This commit is contained in:
Laura Klünder 2016-08-29 18:49:24 +02:00
parent 752b7d6d7d
commit e8824a02d4
9 changed files with 98 additions and 84 deletions

View file

@ -3,7 +3,7 @@ import json
from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admin.views.decorators import staff_member_required
from django.shortcuts import get_object_or_404, redirect, render 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 @staff_member_required
@ -14,12 +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=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', { return render(request, 'control/editor.html', {
'bounds': json.dumps(MapSource.max_bounds()), 'bounds': json.dumps(Source.max_bounds()),
'packages': MapPackage.objects.all(), 'packages': Package.objects.all(),
'levels': MapLevel.objects.all(), 'levels': Level.objects.all(),
'current_level': level, 'current_level': level,
}) })

View file

@ -1,8 +1,8 @@
# -*- coding: utf-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 from __future__ import unicode_literals
import c3nav.mapdata.models import c3nav.mapdata.models.source
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
@ -17,7 +17,7 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='MapFeature', name='Feature',
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. noc', max_length=50, unique=True, verbose_name='feature identifier')), ('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), bases=(parler.models.TranslatableModelMixin, models.Model),
), ),
migrations.CreateModel( migrations.CreateModel(
name='MapFeatureTranslation', name='FeatureTranslation',
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')),
('language_code', models.CharField(db_index=True, max_length=15, verbose_name='Language')), ('language_code', models.CharField(db_index=True, max_length=15, verbose_name='Language')),
('title', models.CharField(max_length=50, verbose_name='package title')), ('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={ options={
'managed': True,
'db_tablespace': '', 'db_tablespace': '',
'verbose_name': 'map feature Translation',
'db_table': 'mapdata_mapfeature_translation',
'default_permissions': (), 'default_permissions': (),
'db_table': 'mapdata_feature_translation',
'managed': True,
'verbose_name': 'feature Translation',
}, },
), ),
migrations.CreateModel( migrations.CreateModel(
name='MapLevel', name='Level',
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')), ('altitude', models.DecimalField(decimal_places=2, max_digits=6, null=True, verbose_name='level altitude')),
], ],
options={
'ordering': ['altitude'],
},
), ),
migrations.CreateModel( migrations.CreateModel(
name='MapPackage', name='Package',
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')),
@ -65,30 +68,30 @@ class Migration(migrations.Migration):
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
name='MapSource', name='Source',
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(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')), ('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')), ('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')), ('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')), ('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( migrations.AddField(
model_name='maplevel', model_name='level',
name='package', 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( migrations.AddField(
model_name='mapfeature', model_name='feature',
name='package', 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( migrations.AlterUniqueTogether(
name='mapfeaturetranslation', name='featuretranslation',
unique_together=set([('language_code', 'master')]), unique_together=set([('language_code', 'master')]),
), ),
] ]

View file

@ -0,0 +1,4 @@
from .feature import Feature # noqa
from .level import Level # noqa
from .package import Package # noqa
from .source import Source # noqa

View file

@ -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),
)

View file

@ -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']

View file

@ -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)

View file

@ -7,37 +7,8 @@ from django.utils.translation import ugettext_lazy as _
from django.dispatch import receiver from django.dispatch import receiver
from django.core.files.storage import FileSystemStorage from django.core.files.storage import FileSystemStorage
from parler.models import TranslatedFields, TranslatableModel
class SourceImageStorage(FileSystemStorage):
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):
def get_available_name(self, name, *args, max_length=None, **kwargs): def get_available_name(self, name, *args, max_length=None, **kwargs):
if self.exists(name): if self.exists(name):
os.remove(os.path.join(settings.MEDIA_ROOT, 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])) 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 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.CASCADE, related_name='sources', package = models.ForeignKey('Package', on_delete=models.CASCADE, related_name='sources',
verbose_name=_('map package')) verbose_name=_('map package'))
image = models.FileField(_('source image'), max_length=70, 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) bottom = models.DecimalField(_('bottom coordinate'), max_digits=6, decimal_places=2)
left = models.DecimalField(_('left 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)))) 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): def delete_image_on_mapsource_delete(sender, instance, **kwargs):
transaction.on_commit(lambda: instance.image.delete(save=False)) 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): def delete_image_on_mapsource_change(sender, instance, **kwargs):
if not instance.pk: if not instance.pk:
return False return False
try: try:
old_file = MapSource.objects.get(pk=instance.pk).image old_file = Source.objects.get(pk=instance.pk).image
except MapSource.DoesNotExist: except Source.DoesNotExist:
return False return False
new_file = instance.image new_file = instance.image
if map_source_filename(instance, new_file.name) != old_file.name: if map_source_filename(instance, new_file.name) != old_file.name:
transaction.on_commit(lambda: old_file.delete(save=False)) 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

@ -5,6 +5,7 @@ from collections import OrderedDict
from django.core.files import File from django.core.files import File
from django.core.management.base import CommandError from django.core.management.base import CommandError
from .models import Package, Level, Source
class PackageIOError(CommandError): class PackageIOError(CommandError):

View file

@ -3,12 +3,12 @@ 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 HttpResponse from django.http import HttpResponse
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from .models import MapSource from .models import Source
@staff_member_required @staff_member_required
def source(request, source): 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]) response = HttpResponse(content_type=mimetypes.guess_type(source.image.name)[0])
for chunk in source.image.chunks(): for chunk in source.image.chunks():
response.write(chunk) response.write(chunk)