diff options
Diffstat (limited to 'chimere/models.py')
| -rw-r--r-- | chimere/models.py | 244 |
1 files changed, 205 insertions, 39 deletions
diff --git a/chimere/models.py b/chimere/models.py index 37355de..357a32a 100644 --- a/chimere/models.py +++ b/chimere/models.py @@ -27,22 +27,25 @@ from PIL import Image from subprocess import Popen, PIPE from BeautifulSoup import BeautifulSoup +from django import forms from django.conf import settings +from django.contrib import admin +from django.contrib.auth.models import User, Permission, ContentType, Group from django.contrib.gis.db import models from django.contrib.gis.gdal import SpatialReference -from django.contrib import admin from django.core.files import File -from django.core.exceptions import ValidationError +from django.core.exceptions import ValidationError, ObjectDoesNotExist from django.core.urlresolvers import reverse +from django.db.models import Q from django.db.models.signals import post_save, pre_save, m2m_changed -from django import forms from django.template import defaultfilters from django.utils.translation import ugettext_lazy as _ from chimere.widgets import PointField, RouteField, SelectMultipleField, \ TextareaWidget, DatePickerWidget from chimere.managers import BaseGeoManager -from chimere.utils import KMLManager, OSMManager, ShapefileManager +from chimere.utils import KMLManager, OSMManager, ShapefileManager, \ + GeoRSSManager, CSVManager class Page(models.Model): """Simple extra pages @@ -102,6 +105,8 @@ class News(models.Model): date = models.DateField(_(u"Date"), auto_now_add=True) content = models.TextField() url = models.URLField(_(u"Url"), max_length=200, blank=True, null=True) + areas = SelectMultipleField('Area', verbose_name=_(u"Associated areas"), + blank=True, null=True) def __unicode__(self): ordering = ["-date"] return self.title @@ -202,7 +207,8 @@ class Icon(models.Model): class SubCategory(models.Model): '''Sub-category ''' - category = models.ForeignKey(Category, verbose_name=_(u"Category")) + category = models.ForeignKey(Category, verbose_name=_(u"Category"), + related_name='subcategories') name = models.CharField(_(u"Name"), max_length=150) available = models.BooleanField(_(u"Available"), default=True) submission = models.BooleanField(_(u"Available for submission"), @@ -284,14 +290,20 @@ class SubCategory(models.Model): IMPORTERS = {'KML':KMLManager, 'OSM':OSMManager, - 'SHP':ShapefileManager + 'SHP':ShapefileManager, + 'RSS':GeoRSSManager, + 'CSV':CSVManager } IMPORTER_CHOICES = (('KML', 'KML'), ('OSM', 'OSM'), ('SHP', 'Shapefile'), + ('RSS', 'GeoRSS'), + ('CSV', 'CSV') ) +IMPORTER_CHOICES_DICT = dict(IMPORTER_CHOICES) + class Importer(models.Model): ''' Data importer for a specific subcategory @@ -301,20 +313,34 @@ class Importer(models.Model): # URL of a KML file or a XAPI service for OSM source = models.CharField(_(u"Source"), max_length=200, blank=True, null=True) + source_file = models.FileField(_(u"Source file"), + upload_to='import_files', blank=True, null=True) filtr = models.CharField(_(u"Filter"), max_length=200, blank=True, null=True) default_name = models.CharField(_(u"Name by default"), max_length=200, blank=True, null=True) + srid = models.IntegerField(_(u"SRID"), blank=True, null=True) + zipped = models.BooleanField(_(u"Zipped file"), default=False) + origin = models.CharField(_(u"Origin"), max_length=100, + blank=True, null=True) + license = models.CharField(_(u"License"), max_length=100, + blank=True, null=True) categories = SelectMultipleField(SubCategory, verbose_name=_(u"Associated subcategories")) state = models.CharField(_(u"State"), max_length=200, blank=True, null=True) - srid = models.IntegerField(_(u"SRID"), blank=True, null=True) - zipped = models.BooleanField(_(u"Zipped file"), default=False) class Meta: verbose_name = _(u"Importer") + def __unicode__(self): + vals = [IMPORTER_CHOICES_DICT[self.importer_type], + self.source, self.source_file.name, + u", ".join([unicode(cat) for cat in self.categories.all()]), + self.default_name] + return u' %d: %s' % (self.pk, u" - ".join([unicode(v) + for v in vals if v])) + @property def manager(self): return IMPORTERS[self.importer_type](self) @@ -351,6 +377,10 @@ class GeographicItem(models.Model): default=False) not_for_osm = models.BooleanField(_(u"Not to be imported inside OSM"), default=False) + origin = models.CharField(_(u"Origin"), max_length=100, + blank=True, null=True) + license = models.CharField(_(u"License"), max_length=100, + blank=True, null=True) if settings.CHIMERE_DAYS_BEFORE_EVENT: start_date = models.DateField(_(u"Start date"), blank=True, null=True, help_text=_(u"Not mandatory. Set it for dated item such as event. "\ @@ -388,6 +418,16 @@ class GeographicItem(models.Model): def properties(cls): return [pm for pm in PropertyModel.objects.filter(available=True)] +def property_setter(cls, propertymodel): + def setter(self, value): + marker = self + if cls == Route: + if not self.associated_marker.objects.count(): + return + marker = self.associated_marker.objects.all()[0] + marker.setProperty(propertymodel, value) + return setter + class Marker(GeographicItem): '''Marker for a POI ''' @@ -418,6 +458,9 @@ class Marker(GeographicItem): if property: val = property.python_value setattr(self, attr_name, val) + if not hasattr(self, attr_name + '_set'): + setattr(self, attr_name + '_set', + property_setter(self.__class__, pm)) def get_init_multi(self): multis = [forms.model_to_dict(multi) @@ -495,31 +538,37 @@ class Marker(GeographicItem): properties.append(property) return properties + def setProperty(self, pm, value): + u""" + Set a property + """ + properties = Property.objects.filter(marker=self, + propertymodel=pm).all() + # in case of multiple edition as the same time delete arbitrary + # the others + if len(properties) > 1: + for property in properties[1:]: + property.delete() + # new property + if not properties: + new_property = Property.objects.create(marker=self, + propertymodel=pm, + value=value) + new_property.save() + else: + property = properties[0] + property.value = value + property.save() + def saveProperties(self, values): """ Save properties """ for propertymodel in PropertyModel.objects.filter(available=True): - properties = Property.objects.filter(marker=self, - propertymodel=propertymodel).all() - # in case of multiple edition as the same time delete arbitrary - # the others - if len(properties) > 1: - for property in properties[1:]: - property.delete() val = u"" if unicode(propertymodel.id) in values: val = values[unicode(propertymodel.id)] - # new property - if not properties: - new_property = Property.objects.create(marker=self, - propertymodel=propertymodel, - value=val) - new_property.save() - else: - property = properties[0] - property.value = val - property.save() + self.setProperty(propertymodel, val) def getGeoJSON(self, categories_id=[]): '''Return a GeoJSON string @@ -559,6 +608,12 @@ class Marker(GeographicItem): url = reverse('chimere:tiny', args=[area_name, urn]) return url + +PRE_ATTRS = { + 'Marker':('name', 'geometry', 'import_version'), + 'Route':('name', 'geometry', 'import_version'), + 'Area':('urn', 'name'), + } def geometry_pre_save(cls, pre_save_geom_values): def geom_pre_save(sender, **kwargs): if not kwargs['instance'] or not kwargs['instance'].pk: @@ -566,8 +621,8 @@ def geometry_pre_save(cls, pre_save_geom_values): instance = kwargs['instance'] try: instance = cls.objects.get(pk=instance.pk) - pre_save_geom_values[instance.pk] = (instance.name, - instance.geometry, instance.import_version) + pre_save_geom_values[instance.pk] = [getattr(instance, attr) + for attr in PRE_ATTRS[cls.__name__]] except ObjectDoesNotExist: pass return geom_pre_save @@ -826,7 +881,8 @@ post_save.connect(picturefile_post_save, sender=PictureFile) class RouteFile(models.Model): name = models.CharField(_(u"Name"), max_length=150) - raw_file = models.FileField(_(u"Raw file (gpx or kml)"), upload_to='route_files') + raw_file = models.FileField(_(u"Raw file (gpx or kml)"), + upload_to='route_files') simplified_file = models.FileField(_(u"Simplified file"), upload_to='route_files', blank=True, null=True) TYPE = (('K', _(u'KML')), ('G', _(u'GPX'))) @@ -894,7 +950,7 @@ class Route(GeographicItem): null=True, height_field='height', width_field='width') height = models.IntegerField(_(u"Height"), blank=True, null=True) width = models.IntegerField(_(u"Width"), blank=True, null=True) - objects = BaseGeoManager() + objects = models.GeoManager() def __unicode__(self): return self.name @@ -903,9 +959,31 @@ class Route(GeographicItem): ordering = ('status', 'name') verbose_name = _(u"Route") + def __init__(self, *args, **kwargs): + super(Route, self).__init__(*args, **kwargs) + self.description = '' + try: + associated_marker = Marker.objects.get(route=self) + self.description = associated_marker.description + except: + associated_marker = None + # add read attributes for properties + for pm in self.properties(): + attr_name = pm.getAttrName() + if not hasattr(self, attr_name): + val = '' + if associated_marker: + property = associated_marker.getProperty(pm) + if property: + val = property.python_value + setattr(self, attr_name, val) + if not hasattr(self, attr_name + '_set'): + setattr(self, attr_name + '_set', + property_setter(self.__class__, pm)) + @property def geometry(self): - return self.point.wkt + return self.route.wkt def get_init_multi(self): if not self.associated_marker.count(): @@ -1175,20 +1253,34 @@ class Area(models.Model, SimpleArea): ''' return cls.objects.filter(available=True) - def getIncludeSql(self, geometry='"chimere_marker".point'): - """ - Get the sql statement for the test if the point is included in the area - """ - area = "ST_GeometryFromText('POLYGON((%f %f,%f %f,%f %f,%f %f, %f %f"\ - "))', %d)" % (self.upper_left_corner.x, self.upper_left_corner.y, + def getWkt(self): + return "SRID=%d;POLYGON((%f %f,%f %f,%f %f,%f %f, %f %f))" % ( + settings.CHIMERE_EPSG_DISPLAY_PROJECTION, + self.upper_left_corner.x, self.upper_left_corner.y, self.lower_right_corner.x, self.upper_left_corner.y, self.lower_right_corner.x, self.lower_right_corner.y, self.upper_left_corner.x, self.lower_right_corner.y, self.upper_left_corner.x, self.upper_left_corner.y, - settings.CHIMERE_EPSG_DISPLAY_PROJECTION ) - sql = "ST_Contains(" + area + ", " + geometry + ")" - return sql + + def getIncludeMarker(self): + """ + Get the sql statement for the test if the point is included in the area + """ + return Q(point__contained=self.getWkt()) + + def getIncludeRoute(self): + """ + Get the sql statement for the test if the route is included in the area + """ + return Q(route__contained=self.getWkt()) + +pre_save_area_values = {} +def area_pre_save(sender, **kwargs): + if not kwargs['instance']: + return + geometry_pre_save(Area, pre_save_area_values)(sender, **kwargs) +pre_save.connect(area_pre_save, sender=Area) def area_post_save(sender, **kwargs): if not kwargs['instance']: @@ -1199,8 +1291,82 @@ def area_post_save(sender, **kwargs): for default in defaults: default.default = False default.save() + # manage permissions + old_urn, old_name = area.urn, area.name + if area.pk in pre_save_area_values: + old_urn, old_name = pre_save_area_values[area.pk] + perm, old_groups, old_users = None, [], [] + if area.urn != old_urn: + oldmnemo = 'change_area_' + old_urn + old_perm = Permission.objects.filter(codename=oldmnemo) + if old_perm.count(): + perm = old_perm.all()[0] + perm.codename = 'change_area_' + area.urn + perm.save() + if not area.urn: + area.urn = defaultfilters.slugify(area.name) + area.save() + mnemo = 'change_area_' + area.urn + perm = Permission.objects.filter(codename=mnemo) + lbl = "Can change " + area.name + if not perm.count(): + content_type = ContentType.objects.get(app_label="chimere", + model="area") + perm = Permission(name=lbl, content_type_id=content_type.id, + codename=mnemo) + perm.save() + else: + perm = perm.all()[0] + if old_name != area.name: + perm.name = lbl + perm.save() + # manage moderation group + groupname = area.name + " moderation" + if old_name != area.name: + old_groupname = old_name + " moderation" + old_gp = Group.objects.filter(name=old_groupname) + if old_gp.count(): + old_gp = old_gp.all()[0] + old_gp.name = groupname + old_gp.save() + group = Group.objects.filter(name=groupname) + if not group.count(): + group = Group.objects.create(name=groupname) + group.permissions.add(perm) + for app_label, model in (('chimere', 'marker'), + ('chimere', 'route'), + ('chimere', 'multimediafile'), + ('chimere', 'picturefile'), + ('chimere', 'routefile')): + ct = ContentType.objects.get(app_label=app_label, model=model) + for p in Permission.objects.filter(content_type=ct).all(): + group.permissions.add(p) + post_save.connect(area_post_save, sender=Area) +def get_areas_for_user(user): + """ + Getting subcats for a specific user + """ + perms = user.get_all_permissions() + areas = set() + prefix = 'chimere.change_area_' + for perm in perms: + if perm.startswith(prefix): + try: + area = Area.objects.get(urn=perm[len(prefix):]) + areas.add(area) + except ObjectDoesNotExist: + pass + return areas + +def get_users_by_area(area): + if not area: + return [] + perm = 'change_area_'+area.urn + return User.objects.filter(Q(groups__permissions__codename=perm)| + Q(user_permissions__codename=perm)).all() + class AreaLayers(models.Model): area = models.ForeignKey(Area) layer = models.ForeignKey(Layer) |
