diff options
| author | Étienne Loks <etienne.loks@peacefrogs.net> | 2012-10-14 16:55:22 +0200 | 
|---|---|---|
| committer | Étienne Loks <etienne.loks@peacefrogs.net> | 2012-10-14 16:55:22 +0200 | 
| commit | ea65e5512c236b81e7f4b8757521facadae4b3b8 (patch) | |
| tree | 739cdf40fb6a89de90c4189936d695288a82849f /chimere/models.py | |
| parent | 4b0f0777c434f5fa1366ca408c34d257d4833fad (diff) | |
| parent | a55f77a246f99764ff6289686f80825526654e2b (diff) | |
| download | Chimère-ea65e5512c236b81e7f4b8757521facadae4b3b8.tar.bz2 Chimère-ea65e5512c236b81e7f4b8757521facadae4b3b8.zip | |
Merge branch 'master' into saclay
Conflicts:
	chimere/admin.py
	chimere/fixtures/initial_data.json
	chimere/forms.py
	chimere/locale/fr/LC_MESSAGES/django.po
	chimere/models.py
	chimere/static/chimere/css/styles.css
	chimere/templates/chimere/detail.html
	chimere/templatetags/chimere_tags.py
	chimere/views.py
	chimere/widgets.py
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) | 
