refactor models
This commit is contained in:
parent
752b7d6d7d
commit
e8824a02d4
9 changed files with 98 additions and 84 deletions
|
@ -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,
|
||||||
})
|
})
|
||||||
|
|
|
@ -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')]),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
4
src/c3nav/mapdata/models/__init__.py
Normal file
4
src/c3nav/mapdata/models/__init__.py
Normal 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
|
25
src/c3nav/mapdata/models/feature.py
Normal file
25
src/c3nav/mapdata/models/feature.py
Normal 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),
|
||||||
|
)
|
16
src/c3nav/mapdata/models/level.py
Normal file
16
src/c3nav/mapdata/models/level.py
Normal 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']
|
15
src/c3nav/mapdata/models/package.py
Normal file
15
src/c3nav/mapdata/models/package.py
Normal 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)
|
|
@ -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),
|
|
||||||
)
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue