Features: store titles as a JSONField and add loadmap and dumpmap support
This commit is contained in:
parent
49c2270b84
commit
be62b14669
11 changed files with 101 additions and 27 deletions
|
@ -21,13 +21,11 @@ def add_feature(request, feature_type):
|
|||
with transaction.atomic():
|
||||
feature = form.instance
|
||||
feature.feature_type = feature_type.name
|
||||
feature.save()
|
||||
|
||||
feature.titles = {}
|
||||
for language, title in form.titles.items():
|
||||
if title:
|
||||
feature.featuretitles.update_or_create(language=language, defaults={'title': title})
|
||||
else:
|
||||
feature.featuretitles.filter(language=language).delete()
|
||||
feature.titles[language] = title
|
||||
feature.save()
|
||||
|
||||
return render(request, 'editor/feature_success.html', {})
|
||||
else:
|
||||
|
|
|
@ -87,6 +87,6 @@ class FeatureViewSet(ReadOnlyModelViewSet):
|
|||
"""
|
||||
Get all Map Features including ones that are only part of the current session
|
||||
"""
|
||||
queryset = Feature.objects.all().prefetch_related('featuretitles')
|
||||
queryset = Feature.objects.all()
|
||||
serializer_class = FeatureSerializer
|
||||
lookup_value_regex = '[^/]+'
|
||||
|
|
|
@ -17,3 +17,16 @@ class GeometryField(models.TextField):
|
|||
|
||||
def get_prep_value(self, value):
|
||||
return json.dumps(sort_geojson(mapping(value)))
|
||||
|
||||
|
||||
class JSONField(models.TextField):
|
||||
def from_db_value(self, value, expression, connection, context):
|
||||
if value is None:
|
||||
return value
|
||||
return json.loads(value)
|
||||
|
||||
def to_python(self, value):
|
||||
return json.loads(value)
|
||||
|
||||
def get_prep_value(self, value):
|
||||
return json.dumps(value)
|
||||
|
|
33
src/c3nav/mapdata/migrations/0002_auto_20160926_0858.py
Normal file
33
src/c3nav/mapdata/migrations/0002_auto_20160926_0858.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-26 08:58
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import c3nav.mapdata.fields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mapdata', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='featuretitle',
|
||||
unique_together=set([]),
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='featuretitle',
|
||||
name='feature',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='feature',
|
||||
name='titles',
|
||||
field=c3nav.mapdata.fields.JSONField(default={}),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='FeatureTitle',
|
||||
),
|
||||
]
|
|
@ -1,10 +1,13 @@
|
|||
import os
|
||||
from collections import OrderedDict, namedtuple
|
||||
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import get_language
|
||||
from shapely.geometry import mapping, shape
|
||||
|
||||
from ..fields import GeometryField
|
||||
from c3nav.mapdata.utils import sort_geojson
|
||||
from ..fields import GeometryField, JSONField
|
||||
|
||||
|
||||
class FeatureType(namedtuple('FeatureType', ('name', 'title', 'title_plural', 'geomtype', 'color'))):
|
||||
|
@ -38,28 +41,54 @@ class Feature(models.Model):
|
|||
feature_type = models.CharField(max_length=50, choices=TYPES)
|
||||
level = models.ForeignKey('mapdata.Level', on_delete=models.CASCADE, related_name='features',
|
||||
verbose_name=_('level'))
|
||||
titles = JSONField()
|
||||
geometry = GeometryField()
|
||||
|
||||
path_regex = r'^features/('+'|'.join(name for name, title in TYPES)+')/'
|
||||
|
||||
@property
|
||||
def titles(self):
|
||||
return {title.language: title.title for title in self.featuretitles.all()}
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
titles = self.titles
|
||||
lang = get_language()
|
||||
if lang in titles:
|
||||
return titles[lang]
|
||||
return next(iter(titles.values())) if titles else self.name
|
||||
if lang in self.titles:
|
||||
return self.titles[lang]
|
||||
return next(iter(self.titles.values())) if self.titles else self.name
|
||||
|
||||
def tofilename(self):
|
||||
return 'features/%s/%s.json' % (self.feature_type, self.name)
|
||||
|
||||
class FeatureTitle(models.Model):
|
||||
feature = models.ForeignKey('Feature', on_delete=models.CASCADE, related_name='featuretitles',
|
||||
verbose_name=_('map package'))
|
||||
language = models.CharField(max_length=50)
|
||||
title = models.CharField(max_length=50)
|
||||
@classmethod
|
||||
def fromfile(cls, data, file_path):
|
||||
kwargs = {}
|
||||
kwargs['feature_type'] = file_path.split(os.path.sep)[1]
|
||||
|
||||
class Meta:
|
||||
unique_together = ('feature', 'language')
|
||||
if 'geometry' not in data:
|
||||
raise ValueError('missing geometry.')
|
||||
try:
|
||||
kwargs['geometry'] = shape(data['geometry'])
|
||||
except:
|
||||
raise ValueError(_('Invalid GeoJSON.'))
|
||||
|
||||
if 'level' not in data:
|
||||
raise ValueError('missing level.')
|
||||
kwargs['level'] = data['level']
|
||||
|
||||
if 'titles' not in data:
|
||||
raise ValueError('missing titles.')
|
||||
titles = data['titles']
|
||||
if not isinstance(titles, dict):
|
||||
raise ValueError('Invalid titles format.')
|
||||
if any(not isinstance(lang, str) for lang in titles.keys()):
|
||||
raise ValueError('titles: All languages have to be strings.')
|
||||
if any(not isinstance(title, str) for title in titles.values()):
|
||||
raise ValueError('titles: All titles have to be strings.')
|
||||
if any(not title for title in titles.values()):
|
||||
raise ValueError('titles: Titles must not be empty strings.')
|
||||
kwargs['titles'] = titles
|
||||
return kwargs
|
||||
|
||||
def tofile(self):
|
||||
return OrderedDict((
|
||||
('titles', OrderedDict(sorted(self.titles.items()))),
|
||||
('level', self.level.name),
|
||||
('geometry', sort_geojson(mapping(self.geometry)))
|
||||
))
|
||||
|
|
|
@ -18,7 +18,7 @@ class Level(models.Model):
|
|||
return 'levels/%s.json' % self.name
|
||||
|
||||
@classmethod
|
||||
def fromfile(cls, data):
|
||||
def fromfile(cls, data, file_path):
|
||||
if 'altitude' not in data:
|
||||
raise ValueError('missing altitude.')
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ class Package(models.Model):
|
|||
return self.name in settings.PUBLIC_PACKAGES
|
||||
|
||||
@classmethod
|
||||
def fromfile(cls, data):
|
||||
def fromfile(cls, data, file_path):
|
||||
kwargs = {}
|
||||
|
||||
if 'name' not in data:
|
||||
|
|
|
@ -32,7 +32,7 @@ class Source(models.Model):
|
|||
return 'sources/%s.json' % self.name
|
||||
|
||||
@classmethod
|
||||
def fromfile(cls, data):
|
||||
def fromfile(cls, data, file_path):
|
||||
kwargs = {}
|
||||
|
||||
if 'bounds' not in data:
|
||||
|
|
|
@ -136,7 +136,7 @@ class ReaderItem:
|
|||
self.data['commit_id'] = result.stdout.read().strip()
|
||||
|
||||
try:
|
||||
add_data = self.model.fromfile(self.json_data)
|
||||
add_data = self.model.fromfile(self.json_data, self.path_in_package)
|
||||
except Exception as e:
|
||||
raise CommandError('Could not load data: %s' % e)
|
||||
self.data.update(add_data)
|
||||
|
|
|
@ -106,7 +106,7 @@ class MapdataWriter:
|
|||
for file_path, content in self.write:
|
||||
full_file_path = os.path.join(settings.MAP_ROOT, file_path)
|
||||
try:
|
||||
os.makedirs(os.path.join(os.path.split(full_file_path)[:-1]))
|
||||
os.makedirs(os.path.join(*os.path.split(full_file_path)[0]))
|
||||
except os.error:
|
||||
pass
|
||||
if content is not None:
|
||||
|
|
|
@ -55,6 +55,7 @@ class FeatureTypeSerializer(serializers.Serializer):
|
|||
|
||||
|
||||
class FeatureSerializer(serializers.ModelSerializer):
|
||||
titles = serializers.JSONField()
|
||||
geometry = GeometryField()
|
||||
|
||||
class Meta:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue