diff options
Diffstat (limited to 'chimere/models.py')
| -rw-r--r-- | chimere/models.py | 489 | 
1 files changed, 285 insertions, 204 deletions
| diff --git a/chimere/models.py b/chimere/models.py index a37a548..94da386 100644 --- a/chimere/models.py +++ b/chimere/models.py @@ -20,7 +20,11 @@  """  Models description  """ -import os, datetime, pyexiv2, re, string, copy +import os +import datetime +import pyexiv2 +import re +import copy  import simplejson as json  from lxml import etree  from PIL import Image @@ -29,12 +33,10 @@ 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.core.files import File -from django.core.exceptions import ValidationError, ObjectDoesNotExist +from django.core.exceptions import 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 @@ -42,9 +44,7 @@ from django.template import defaultfilters  from django.utils.translation import ugettext_lazy as _  from chimere.widgets import HiddenPointChooserWidget, PointField, RouteField, \ -                            SelectMultipleField, TextareaWidget, \ -                            DatePickerWidget -from chimere.managers import BaseGeoManager +    SelectMultipleField, TextareaWidget, DatePickerWidget  from chimere.utils import KMLManager, OSMManager, ShapefileManager, \      GeoRSSManager, CSVManager, HtmlXsltManager, XMLXsltManager, JsonManager @@ -60,13 +60,16 @@ class Page(models.Model):      template_path = models.CharField(_(u"Template path"), max_length=150,                                       blank=True, null=True)      content = models.TextField(blank=True, null=True) -    def __unicode__(self): -        ordering = ["order"] -        return self.title +      class Meta: +        ordering = ["order"]          verbose_name = _(u"Page")          verbose_name_plural = _(u"Page") +    def __unicode__(self): +        return self.title + +  def page_post_save(sender, **kwargs):      if not kwargs['instance']:          return @@ -76,6 +79,7 @@ def page_post_save(sender, **kwargs):          page.save()  post_save.connect(page_post_save, sender=Page) +  def shortify(text):      if not text:          return '' @@ -88,15 +92,16 @@ def shortify(text):          if c == '>':              break          if c == '<': -            short_desc = desc[:-(idx+1)] +            short_desc = desc[:-(idx + 1)]              break      if not short_desc:          for idx, c in enumerate(reversed(desc)):              if c == ' ' or c == '\n': -                short_desc = desc[:-(idx+1)] +                short_desc = desc[:-(idx + 1)]                  break      return BeautifulSoup(short_desc).prettify() +  class News(models.Model):      """News of the site      """ @@ -109,33 +114,38 @@ class News(models.Model):      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 +      class Meta: +        ordering = ["-date"]          verbose_name = _(u"News")          verbose_name_plural = _(u"News") +    def __unicode__(self): +        return self.title +      @property      def short_desc(self):          return shortify(self.content) +  class TinyUrl(models.Model):      """Tinyfied version of permalink parameters      """      parameters = models.CharField(_(u"Parameters"), max_length=500) -    def __unicode__(self): -        return self.parameters -    class Meta: -        verbose_name = _(u"TinyUrl")      digits = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"      base = len(digits) +    class Meta: +        verbose_name = _(u"TinyUrl") + +    def __unicode__(self): +        return self.parameters +      @classmethod      def getParametersByUrn(cls, urn):          c_id = 0          for idx, char in enumerate(reversed(urn)): -            c_id += cls.digits.index(char)*pow(cls.base, idx) +            c_id += cls.digits.index(char) * pow(cls.base, idx)          try:              params = cls.objects.get(id=c_id).parameters          except cls.DoesNotExist: @@ -159,27 +169,34 @@ class TinyUrl(models.Model):                  break          return urn +  class ColorTheme(models.Model):      """Color theme      """      name = models.CharField(_(u"Name"), max_length=150) -    def __unicode__(self): -        return self.name +      class Meta:          verbose_name = _(u"Color theme") +    def __unicode__(self): +        return self.name + +  class Color(models.Model):      """Color      """      code = models.CharField(_(u"Code"), max_length=6)      order = models.IntegerField(_(u"Order"))      color_theme = models.ForeignKey(ColorTheme, verbose_name=_(u"Color theme")) -    def __unicode__(self): -        return self.code +      class Meta:          ordering = ["order"]          verbose_name = _(u"Color") +    def __unicode__(self): +        return self.code + +  class Category(models.Model):      """Category of Point Of Interest (POI)      """ @@ -187,12 +204,14 @@ class Category(models.Model):      available = models.BooleanField(_(u"Available"))      order = models.IntegerField(_(u"Order"))      description = models.TextField(blank=True, null=True) -    def __unicode__(self): -        return self.name +      class Meta:          ordering = ["order"]          verbose_name = _(u"Category") +    def __unicode__(self): +        return self.name +  class Icon(models.Model):      '''Icon @@ -230,8 +249,9 @@ class SubCategory(models.Model):      dated = models.BooleanField(_(u"Is dated"), default=False)      description = models.TextField(blank=True, null=True)      icon = models.ForeignKey(Icon, verbose_name=_(u"Icon")) -    hover_icon = models.ForeignKey(Icon, verbose_name=_(u"Hover icon"), -                          blank=True, null=True, related_name='subcat_hovered') +    hover_icon = models.ForeignKey( +        Icon, verbose_name=_(u"Hover icon"), blank=True, null=True, +        related_name='subcat_hovered')      color_theme = models.ForeignKey(ColorTheme, verbose_name=_(u"Color theme"),                                      blank=True, null=True)      as_layer = models.BooleanField(_(u"Displayed in the layer menu"), @@ -239,14 +259,16 @@ class SubCategory(models.Model):      routing_warn = models.BooleanField(_(u"Routing warn"), default=False)      order = models.IntegerField(_(u"Order"), default=1000)      keywords = models.TextField(_(u"Keywords"), max_length=200, -                                        blank=True, null=True) -    def __unicode__(self): -        return u"%s / %s" % (self.category.name, self.name) +                                blank=True, null=True) +      class Meta:          ordering = ["category", "order"]          verbose_name = _(u"Sub-category")          verbose_name_plural = _(u"Sub-categories") +    def __unicode__(self): +        return u"%s / %s" % (self.category.name, self.name) +      @classmethod      def getAvailable(cls, item_types=None, area_name=None, public=False):          '''Get list of tuples with first the category and second the associated @@ -277,8 +299,8 @@ class SubCategory(models.Model):                  sub_category.category.selected = True              sub_categories[sub_category.category].append(sub_category) -        subcategories = [(cat, subcats) \ -                           for cat, subcats in sub_categories.items()] +        subcategories = [(cat, subcats) +                         for cat, subcats in sub_categories.items()]          get_cat_order = lambda cat_tuple: cat_tuple[0].order          subcategories = sorted(subcategories, key=get_cat_order)          return subcategories @@ -303,7 +325,7 @@ class SubCategory(models.Model):                            'offset_y': self.icon.offset_y,                            'popup_offset_x': self.icon.popup_offset_x,                            'popup_offset_y': self.icon.popup_offset_y} -                          } +                 }          if self.hover_icon:              items['icon_hover'] = {'url': self.hover_icon.image.url} @@ -380,10 +402,10 @@ class Importer(models.Model):      source = models.CharField(_(u"Web address"), max_length=200,                                blank=True, null=True,                                help_text=_(u"Don't forget the trailing slash")) -    source_file = models.FileField(_(u"Source file"), -                        upload_to='import_files', blank=True, null=True) -    source_file_alt = models.FileField(_(u"Alt source file"), -                        upload_to='import_files', blank=True, null=True) +    source_file = models.FileField( +        _(u"Source file"), upload_to='import_files', blank=True, null=True) +    source_file_alt = models.FileField( +        _(u"Alt source file"), upload_to='import_files', 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) @@ -398,19 +420,20 @@ class Importer(models.Model):                                blank=True, null=True)      license = models.CharField(_(u"License"), max_length=1000,                                 blank=True, null=True) -    categories = SelectMultipleField(SubCategory, blank=True, null=True, -                      verbose_name=_(u"Associated subcategories")) +    categories = SelectMultipleField( +        SubCategory, blank=True, null=True, +        verbose_name=_(u"Associated subcategories"))      state = models.TextField(_(u"State"), blank=True, null=True) -    associate_marker_to_way = models.BooleanField(_(u"Automatically associate "\ -                                           u"a marker to a way"), default=False) +    associate_marker_to_way = models.BooleanField( +        _(u"Automatically associate a marker to a way"), default=False)      automatic_update = models.BooleanField(_(u"Automatically updated"),                                             default=False)      default_status = models.CharField(_(u"Default status"), max_length=1,                                        choices=STATUS, default='I') -    default_localisation = PointField(_(u"Default localisation"), -                               srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION, -                               blank=True, null=True, -                               widget=HiddenPointChooserWidget) +    default_localisation = PointField( +        _(u"Default localisation"), +        srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION, blank=True, null=True, +        widget=HiddenPointChooserWidget)      class Meta:          verbose_name = _(u"Importer") @@ -456,47 +479,51 @@ class ImporterKeyCategories(models.Model):      class Meta:          verbose_name = _(u"Importer - Key categories") +  class GeographicItem(models.Model):      name = models.CharField(_(u"Name"), max_length=150)      categories = SelectMultipleField(SubCategory) -    submiter_session_key = models.CharField(_(u"Submitter session key"), -                                        blank=True, null=True, max_length=40) +    submiter_session_key = models.CharField( +        _(u"Submitter session key"), blank=True, null=True, max_length=40)      submiter_name = models.CharField(_(u"Submitter name or nickname"),                                       blank=True, null=True, max_length=40)      submiter_email = models.EmailField(_(u"Submitter email"), blank=True,                                         null=True) -    submiter_comment = models.TextField(_(u"Submitter comment"), max_length=200, -                                        blank=True, null=True) +    submiter_comment = models.TextField(_(u"Submitter comment"), +                                        max_length=200, blank=True, null=True)      status = models.CharField(_(u"Status"), max_length=1, choices=STATUS) -    keywords = models.TextField(_(u"Keywords"), max_length=200, -                                        blank=True, null=True) +    keywords = models.TextField(_(u"Keywords"), max_length=200, blank=True, +                                null=True)      import_key = models.CharField(_(u"Import key"), max_length=200,                                    blank=True, null=True)      import_version = models.IntegerField(_(u"Import version"),                                           blank=True, null=True)      import_source = models.CharField(_(u"Source"), max_length=200,                                       blank=True, null=True) -    modified_since_import = models.BooleanField(_(u"Modified since last import"), -                                                default=True) +    modified_since_import = models.BooleanField( +        _(u"Modified since last import"), default=True)      not_for_osm = models.BooleanField(_(u"Not to be exported to OSM"),                                        default=False)      origin = models.CharField(_(u"Origin"), max_length=1000,                                blank=True, null=True)      license = models.CharField(_(u"License"), max_length=1000,                                 blank=True, null=True) -    start_date = models.DateField(_(u"Start date"), blank=True, null=True, -        help_text=_(u"Not mandatory. Set it for dated item such as 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. "                      u"Format YYYY-MM-DD")) -    end_date = models.DateField(_(u"End date"), blank=True, null=True, -        help_text=_(u"Not mandatory. Set it only if you have a multi-day "\ +    end_date = models.DateField( +        _(u"End date"), blank=True, null=True, +        help_text=_(u"Not mandatory. Set it only if you have a multi-day "                      u"event. Format YYYY-MM-DD")) +      class Meta:          abstract = True      def get_key(self, key):          key_vals = self.import_key.split(';')          for k_v in key_vals: -            if k_v.startswith(key+':'): +            if k_v.startswith(key + ':'):                  return k_v.split(':')[1]      def set_key(self, key, value): @@ -523,8 +550,8 @@ class GeographicItem(models.Model):      def has_modified(self):          if (self.ref_item and self.ref_item != self) \ -           or self.__class__.objects.filter(ref_item=self -                                ).exclude(pk=self.pk).count(): +           or self.__class__.objects.filter( +                ref_item=self).exclude(pk=self.pk).count():              return True          return False @@ -536,6 +563,7 @@ class GeographicItem(models.Model):      def all_properties(cls):          return [pm for pm in PropertyModel.objects.all()] +  def property_setter(cls, propertymodel):      def setter(self, value):          marker = self @@ -546,15 +574,17 @@ def property_setter(cls, propertymodel):          marker.setProperty(propertymodel, value)      return setter +  class Marker(GeographicItem):      '''Marker for a POI      ''' -    ref_item = models.ForeignKey("Marker", blank=True, null=True, -            verbose_name=_(u"Reference marker"), related_name='submited_marker') +    ref_item = models.ForeignKey( +        "Marker", blank=True, null=True, verbose_name=_(u"Reference marker"), +        related_name='submited_marker')      point = PointField(_(u"Localisation"),                         srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION)      available_date = models.DateTimeField(_(u"Available Date"), blank=True, -                                          null=True) # used by feeds +                                          null=True)  # used by feeds      route = models.ForeignKey(u"Route", blank=True, null=True,                                related_name='associated_marker')      description = models.TextField(_(u"Description"), blank=True, null=True) @@ -578,7 +608,7 @@ class Marker(GeographicItem):                  setattr(self, attr_name, val)              if not hasattr(self, attr_name + '_set'):                  setattr(self, attr_name + '_set', -                              property_setter(self.__class__, pm)) +                        property_setter(self.__class__, pm))      def get_init_multi(self):          multis = [forms.model_to_dict(multi) @@ -683,14 +713,13 @@ class Marker(GeographicItem):                  if choice.count():                      value = choice.all()[0].pk                  else: -                    choice = PropertyModelChoice.objects.create(value=value, -                                                            propertymodel=pm) +                    choice = PropertyModelChoice.objects.create( +                        value=value, propertymodel=pm)                      value = choice.pk          # new property          if not properties: -            new_property = Property.objects.create(marker=self, -                                propertymodel=pm, -                                value=value) +            new_property = Property.objects.create( +                marker=self, propertymodel=pm, value=value)              new_property.save()          else:              property = properties[0] @@ -758,11 +787,13 @@ class Marker(GeographicItem):          return url  PRE_ATTRS = { -    'Marker':('name', 'description', 'start_date', 'geometry', 'import_version', -              'modified_since_import'), -    'Route':('name', 'geometry', 'import_version', 'modified_since_import'), -    'Area':('urn', 'name'), -    } +    'Marker': ('name', 'description', 'start_date', 'geometry', +               'import_version', 'modified_since_import'), +    'Route': ('name', 'geometry', 'import_version', 'modified_since_import'), +    '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: @@ -778,12 +809,15 @@ def geometry_pre_save(cls, pre_save_geom_values):      return geom_pre_save  pre_save_marker_values = {} + +  def marker_pre_save(sender, **kwargs):      if not kwargs['instance']:          return      geometry_pre_save(Marker, pre_save_marker_values)(sender, **kwargs)  pre_save.connect(marker_pre_save, sender=Marker) +  def geometry_post_save(pre_save_geom_values):      def geom_post_save(sender, **kwargs):          if not kwargs['instance'] \ @@ -801,18 +835,21 @@ def geometry_post_save(pre_save_geom_values):              return          if instance.modified_since_import:              return -        if [key for key in pre if pre not in ('import_version', -                                        'modified_since_import') and -                           getattr(instance, key) != pre[key]]: +        if [key for key in pre if pre not in ( +            'import_version', 'modified_since_import') and +                getattr(instance, key) != pre[key]]:              instance.modified_since_import = True              instance.save()      return geom_post_save + +  def marker_post_save(sender, **kwargs):      if not kwargs['instance'] or kwargs['created']:          return      geometry_post_save(pre_save_marker_values)(sender, **kwargs)  post_save.connect(marker_post_save, sender=Marker) +  class MultimediaType(models.Model):      MEDIA_TYPES = (('A', _(u"Audio")),                     ('V', _(u"Video")), @@ -848,25 +885,27 @@ class MultimediaType(models.Model):          return tuples  IFRAME_LINKS = { -    'youtube':((re.compile(r'youtube.com\/watch\?[A-Za-z0-9_\-\=\&]*v=([A-Za-z0-9_-]*)[A-Za-z0-9_\-\=\&]*'), -                re.compile(r'youtu.be\/([A-Za-z0-9_-]*)'), -                re.compile(r'youtube.com\/embed\/([A-Za-z0-9_-]*)')), -                 "http://www.youtube.com/embed/%s"), -    'dailymotion':( +    'youtube': ((re.compile(r'youtube.com\/watch\?[A-Za-z0-9_\-\=\&]*v=' +                            r'([A-Za-z0-9_-]*)[A-Za-z0-9_\-\=\&]*'), +                 re.compile(r'youtu.be\/([A-Za-z0-9_-]*)'), +                 re.compile(r'youtube.com\/embed\/([A-Za-z0-9_-]*)')), +                "http://www.youtube.com/embed/%s"), +    'dailymotion': (          (re.compile(r'dailymotion.com/video/([A-Za-z0-9]*)_[A-Za-z0-9_-]*'),           re.compile(r'dailymotion.com/embed/video/([A-Za-z0-9]*)'),           re.compile("http://www.dailymotion.com/embed/video/%s")), -         'http://www.dailymotion.com/embed/video/%s'), -    'vimeo':((re.compile(r'vimeo.com/video/([A-Za-z0-9]*)'), -              re.compile(r'vimeo.com/([A-Za-z0-9]*)'),), +        'http://www.dailymotion.com/embed/video/%s'), +    'vimeo': ((re.compile(r'vimeo.com/video/([A-Za-z0-9]*)'), +               re.compile(r'vimeo.com/([A-Za-z0-9]*)'),),                "http://player.vimeo.com/video/%s")  } +  class MultimediaExtension(models.Model):      name = models.CharField(_(u"Extension name"), max_length=6) -    multimedia_type = models.ForeignKey(MultimediaType, -                        verbose_name=_(u"Associated multimedia type"), -                        related_name='extensions') +    multimedia_type = models.ForeignKey( +        MultimediaType, verbose_name=_(u"Associated multimedia type"), +        related_name='extensions')      class Meta:          verbose_name = _(u"Multimedia extension") @@ -875,13 +914,15 @@ class MultimediaExtension(models.Model):      def __unicode__(self):          return self.name +  class MultimediaFile(models.Model):      name = models.CharField(_(u"Name"), max_length=150)      url = models.URLField(_(u"Url"), max_length=200)      order = models.IntegerField(_(u"Order"), default=1)      multimedia_type = models.ForeignKey(MultimediaType, blank=True, null=True) -    miniature = models.BooleanField(_(u"Display inside the description?"), -                                  default=settings.CHIMERE_MINIATURE_BY_DEFAULT) +    miniature = models.BooleanField( +        _(u"Display inside the description?"), +        default=settings.CHIMERE_MINIATURE_BY_DEFAULT)      marker = models.ForeignKey(Marker, related_name='multimedia_files')      class Meta: @@ -891,6 +932,7 @@ class MultimediaFile(models.Model):      def __unicode__(self):          return self.name or u"" +  def multimediafile_post_save(sender, **kwargs):      if not kwargs['instance'] or not kwargs['created']:          return @@ -902,7 +944,7 @@ def multimediafile_post_save(sender, **kwargs):              res, embeded_url = IFRAME_LINKS[mm_type]              if [r for r in res if r.search(url)]:                  multimedia_type = MultimediaType.objects.get( -                                    name__iexact=mm_type) +                    name__iexact=mm_type)                  multimediafile.multimedia_type = multimedia_type          if not multimediafile.multimedia_type:              ext = url.split(".")[-1] @@ -912,10 +954,12 @@ def multimediafile_post_save(sender, **kwargs):              else:                  # default to an iframe                  multimediafile.multimedia_type = \ -                   MultimediaType.objects.filter(name__iexact='iframe').all()[0] +                    MultimediaType.objects.filter(name__iexact='iframe')\ +                                          .all()[0]      # manage iframe of video providers      if multimediafile.multimedia_type.name.lower() in IFRAME_LINKS: -        regexps, lnk = IFRAME_LINKS[multimediafile.multimedia_type.name.lower()] +        regexps, lnk = IFRAME_LINKS[ +            multimediafile.multimedia_type.name.lower()]          key = None          for regexp in regexps:              key = regexp.findall(multimediafile.url) @@ -925,8 +969,9 @@ def multimediafile_post_save(sender, **kwargs):          if key:              multimediafile.url = lnk % key -    mfs = MultimediaFile.objects.filter(marker=multimediafile.marker).exclude( -                                        pk=multimediafile.pk).order_by('order') +    mfs = MultimediaFile.objects.filter(marker=multimediafile.marker)\ +                                .exclude(pk=multimediafile.pk)\ +                                .order_by('order')      for idx, mf in enumerate(mfs.all()):          mf.order = idx + 1          mf.save() @@ -934,18 +979,19 @@ def multimediafile_post_save(sender, **kwargs):      multimediafile.save()  post_save.connect(multimediafile_post_save, sender=MultimediaFile) +  class PictureFile(models.Model):      name = models.CharField(_(u"Name"), max_length=150)      picture = models.ImageField(_(u"Image"), upload_to='pictures',                                  height_field='height', width_field='width')      height = models.IntegerField(_(u"Height"), blank=True, null=True)      width = models.IntegerField(_(u"Width"), blank=True, null=True) -    miniature = models.BooleanField(_(u"Display inside the description?"), -                                  default=settings.CHIMERE_MINIATURE_BY_DEFAULT) -    thumbnailfile = models.ImageField(_(u"Thumbnail"), upload_to='pictures', -                        blank=True, null=True, -                        height_field='thumbnailfile_height', -                        width_field='thumbnailfile_width') +    miniature = models.BooleanField( +        _(u"Display inside the description?"), +        default=settings.CHIMERE_MINIATURE_BY_DEFAULT) +    thumbnailfile = models.ImageField( +        _(u"Thumbnail"), upload_to='pictures', blank=True, null=True, +        height_field='thumbnailfile_height', width_field='thumbnailfile_width')      thumbnailfile_height = models.IntegerField(_(u"Thumbnail height"),                                                 blank=True, null=True)      thumbnailfile_width = models.IntegerField(_(u"Thumbnail width"), @@ -960,6 +1006,7 @@ class PictureFile(models.Model):          verbose_name = _(u"Picture file")          verbose_name_plural = _(u"Picture files") +  def scale_image(max_x, pair):      x, y = pair      new_y = (float(max_x) / x) * y @@ -974,6 +1021,7 @@ IMAGE_EXIF_ORIENTATION_MAP = {  PYEXIV2_OLD_API = not hasattr(pyexiv2, 'ImageMetadata') +  def picturefile_post_save(sender, **kwargs):      if not kwargs['instance']:          return @@ -986,12 +1034,12 @@ def picturefile_post_save(sender, **kwargs):              metadata = pyexiv2.Image(filename)              metadata.readMetadata()              orientation = metadata['Exif.Image.Orientation'] \ -                   if 'Exif.Image.Orientation' in metadata.exifKeys() else None +                if 'Exif.Image.Orientation' in metadata.exifKeys() else None          else:              metadata = pyexiv2.ImageMetadata(filename)              metadata.read()              orientation = metadata['Exif.Image.Orientation'].value \ -                      if 'Exif.Image.Orientation' in metadata else None +                if 'Exif.Image.Orientation' in metadata else None          if orientation and orientation in IMAGE_EXIF_ORIENTATION_MAP \             and orientation > 1:              metadata['Exif.Image.Orientation'] = 1 @@ -1009,7 +1057,7 @@ def picturefile_post_save(sender, **kwargs):          filehead, filetail = os.path.split(os.path.abspath(file.path))          basename, format = os.path.splitext(filetail)          basename = defaultfilters.slugify(basename) -        basename = re.sub(r'-','_', basename) +        basename = re.sub(r'-', '_', basename)          miniature = basename + '_thumb.jpg'          filename = file.path          miniature_filename = os.path.join(filehead, miniature) @@ -1021,12 +1069,10 @@ def picturefile_post_save(sender, **kwargs):              image_x, image_y = image.size              if settings.CHIMERE_THUMBS_SCALE_HEIGHT:                  image_y, image_x = scale_image( -                                          settings.CHIMERE_THUMBS_SCALE_HEIGHT, -                                          (image_y, image_x)) +                    settings.CHIMERE_THUMBS_SCALE_HEIGHT, (image_y, image_x))              elif settings.CHIMERE_THUMBS_SCALE_WIDTH:                  image_x, image_y = scale_image( -                                          settings.CHIMERE_THUMBS_SCALE_WIDTH, -                                          (image_x, image_y)) +                    settings.CHIMERE_THUMBS_SCALE_WIDTH, (image_x, image_y))              image.thumbnail([image_x, image_y], Image.ANTIALIAS)              temp_image = open(miniature_filename, 'w') @@ -1043,8 +1089,8 @@ def picturefile_post_save(sender, **kwargs):      if not kwargs['created']:          return -    pfs = PictureFile.objects.filter(marker=picturefile.marker).exclude( -                                     pk=picturefile.pk).order_by('order') +    pfs = PictureFile.objects.filter(marker=picturefile.marker)\ +                             .exclude(pk=picturefile.pk).order_by('order')      for idx, pf in enumerate(pfs.all()):          pf.order = idx + 1          pf.save() @@ -1052,12 +1098,13 @@ def picturefile_post_save(sender, **kwargs):      picturefile.save()  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') -    simplified_file = models.FileField(_(u"Simplified file"), -                        upload_to='route_files', blank=True, null=True) +    simplified_file = models.FileField( +        _(u"Simplified file"), upload_to='route_files', blank=True, null=True)      TYPE = (('K', _(u'KML')), ('G', _(u'GPX')))      file_type = models.CharField(max_length=1, choices=TYPE) @@ -1074,7 +1121,7 @@ class RouteFile(models.Model):              return          input_name = settings.MEDIA_ROOT + self.raw_file.name          output_name = settings.MEDIA_ROOT + self.raw_file.name[:-4] + \ -                      "_simplified" + ".gpx" +            "_simplified" + ".gpx"          cli_args = [settings.GPSBABEL, '-i']          if self.file_type == 'K':              cli_args.append('kml') @@ -1086,7 +1133,7 @@ class RouteFile(models.Model):          p.wait()          if p.returncode:              print p.stderr.read() -            #logger.error(p.stderr.read()) +            # logger.error(p.stderr.read())          else:              self.simplified_file = File(open(output_name))              self.save() @@ -1104,23 +1151,24 @@ class RouteFile(models.Model):              if not pt.tag.endswith('trkpt'):                  continue              pts.append((pt.get("lon"), pt.get("lat"))) -        geojson_tpl = u'{"type":"Feature", "geometry":{ "type": "LineString", '\ -                       '"coordinates":[%s]}}'          wkt_tpl = u'LINESTRING(%s)' -        return wkt_tpl % u','.join([u'%s %s' % (pt[0], pt[1]) \ -                                           for pt in pts]) +        return wkt_tpl % u','.join([u'%s %s' % (pt[0], pt[1]) +                                    for pt in pts]) +  class Route(GeographicItem):      '''Route on the map      ''' -    ref_item = models.ForeignKey("Route", blank=True, null=True, -              verbose_name=_(u"Reference route"), related_name='submited_route') +    ref_item = models.ForeignKey( +        "Route", blank=True, null=True, verbose_name=_(u"Reference route"), +        related_name='submited_route')      route = RouteField(_(u"Route"),                         srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION)      associated_file = models.ForeignKey(RouteFile, blank=True, null=True,                                          verbose_name=_(u"Associated file")) -    picture = models.ImageField(_(u"Image"), upload_to='upload', blank=True, -                          null=True, height_field='height', width_field='width') +    picture = models.ImageField( +        _(u"Image"), upload_to='upload', blank=True, 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)      has_associated_marker = models.BooleanField(_(u"Has an associated marker"), @@ -1154,7 +1202,7 @@ class Route(GeographicItem):                  setattr(self, attr_name, val)              if not hasattr(self, attr_name + '_set'):                  setattr(self, attr_name + '_set', -                              property_setter(self.__class__, pm)) +                        property_setter(self.__class__, pm))      @property      def geometry(self): @@ -1167,8 +1215,10 @@ class Route(GeographicItem):      def get_init_multi(self):          if not self.associated_marker.count():              return [] -        multis = [forms.model_to_dict(multi) -            for multi in self.associated_marker.all()[0].multimedia_files.all()] +        multis = [ +            forms.model_to_dict(multi) +            for multi in self.associated_marker.all()[0].multimedia_files.all() +        ]          return multis      def get_init_picture(self): @@ -1206,24 +1256,27 @@ class Route(GeographicItem):          '''          if '#' not in color:              color = '#' + color -        attributes = {"type":"Feature", -                      "geometry":json.loads(self.route.geojson), -                      "properties":{"pk":self.id, "name":self.name, -                      "color":color}} +        attributes = {"type": "Feature", +                      "geometry": json.loads(self.route.geojson), +                      "properties": {"pk": self.id, "name": self.name, +                                     "color": color}}          return json.dumps(attributes)      def getTinyUrl(self): -        parameters = 'current_feature=%d&checked_categories=%s' % (self.id, -                                                          self.categories[0].id) +        parameters = 'current_feature=%d&checked_categories=%s' % ( +            self.id, self.categories[0].id)          return TinyUrl.getUrnByParameters(parameters)  pre_save_route_values = {} + +  def route_pre_save(sender, **kwargs):      if not kwargs['instance']:          return      geometry_pre_save(Route, pre_save_route_values)(sender, **kwargs)  pre_save.connect(route_pre_save, sender=Route) +  def route_post_save(sender, **kwargs):      if not kwargs['instance']:          return @@ -1234,10 +1287,11 @@ def route_post_save(sender, **kwargs):      if instance.has_associated_marker:          marker_fields = [f.attname for f in Marker._meta.fields]          route_fields = [f.attname for f in Route._meta.fields] -        marker_dct = dict([(k, getattr(instance, k)) for k in marker_fields -                       if k in route_fields and k not in ('id', 'ref_item_id')]) -        marker_dct['point'] = "SRID=%d;POINT(%f %f)" % (instance.route.srid, -                                  instance.route[0][0], instance.route[0][1]) +        marker_dct = dict( +            [(k, getattr(instance, k)) for k in marker_fields +             if k in route_fields and k not in ('id', 'ref_item_id')]) +        marker_dct['point'] = "SRID=%d;POINT(%f %f)" % ( +            instance.route.srid, instance.route[0][0], instance.route[0][1])          marker, created = Marker.objects.get_or_create(route=instance,                                                         defaults=marker_dct)          if not created: @@ -1257,6 +1311,7 @@ def route_post_save(sender, **kwargs):  post_save.connect(route_post_save, sender=Route) +  def sync_m2m_route(sender, **kwargs):      if kwargs['action'] not in ('post_add', 'post_clear', 'post_remove'):          return @@ -1272,6 +1327,7 @@ def sync_m2m_route(sender, **kwargs):          marker.categories.add(cat)  m2m_changed.connect(sync_m2m_route, sender=Route.categories.through) +  def getDateCondition():      '''      Return an SQL condition for apparition of dates @@ -1279,9 +1335,9 @@ def getDateCondition():      if not settings.CHIMERE_DAYS_BEFORE_EVENT:          return ""      now = datetime.datetime.now().strftime('%Y-%m-%d') -    after = (datetime.datetime.now() + \ -             datetime.timedelta(settings.CHIMERE_DAYS_BEFORE_EVENT) -                                                  ).strftime('%Y-%m-%d') +    after = (datetime.datetime.now() + +             datetime.timedelta(settings.CHIMERE_DAYS_BEFORE_EVENT)).strftime( +        '%Y-%m-%d')      date_condition = " and (%(alias)s.start_date is null or "      date_condition += "(%(alias)s.start_date >= '" + now + "' and "      date_condition += "%(alias)s.start_date <='" + after + "')" @@ -1289,6 +1345,7 @@ def getDateCondition():      date_condition += "%(alias)s.end_date >='" + now + "')) "      return date_condition +  class AggregatedRoute(models.Model):      '''      Database view for aggregated routes @@ -1296,6 +1353,7 @@ class AggregatedRoute(models.Model):      route = models.MultiLineStringField()      subcategory = models.ForeignKey(SubCategory)      status = models.CharField(_(u"Status"), max_length=1, choices=STATUS) +      class Meta:          managed = False          db_table = 'chimere_aggregated_routes' @@ -1305,11 +1363,13 @@ class AggregatedRoute(models.Model):          '''          if '#' not in color:              color = '#' + color -        attributes = {'color':color, 'geometry':json.loads(self.route.geojson), -                      'type':"Feature", "properties":{"pk": self.id, -                                "name": u'Aggregated route',}} +        attributes = { +            'color': color, 'geometry': json.loads(self.route.geojson), +            'type': "Feature", "properties": { +                "pk": self.id, "name": u'Aggregated route'}}          return json.dumps(attributes) +  class SimplePoint:      """      Point in the map (not in the database) @@ -1317,6 +1377,7 @@ class SimplePoint:      def __init__(self, x, y):          self.x, self.y = x, y +  class SimpleArea:      """      Rectangular area of a map (not in the database) @@ -1352,7 +1413,7 @@ class SimpleArea:                  for subcat in subcats:                      subcategory_pks.append(unicode(subcat.pk))              if filter_available: -                wheres += ['subcat.available = TRUE',  'cat.available = TRUE'] +                wheres += ['subcat.available = TRUE', 'cat.available = TRUE']              wheres += ['subcat.id in (%s)' % ",".join(subcategory_pks)]          where = " where " + " and ".join(wheres) if wheres else "" @@ -1362,13 +1423,13 @@ class SimpleArea:          elif status:              equal_status = " in ('%s')" % "','".join(status)          area = u"ST_GeometryFromText('POLYGON((%f %f,%f %f,%f %f,%f %f, %f %f"\ -              u"))', %d)" % (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 -                          ) +            u"))', %d)" % ( +                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)          date_condition = getDateCondition()          sql_main = '''select subcat.id as id, subcat.category_id as category_id,          subcat.name as name, subcat.available as available, @@ -1380,10 +1441,10 @@ class SimpleArea:          inner join chimere_marker mark on ST_Contains(%s, mark.point)''' % area          if equal_status:              sql += ' and mark.status' + equal_status -        sql += date_condition % {'alias':'mark'} +        sql += date_condition % {'alias': 'mark'}          sql += ''' -        inner join chimere_marker_categories mc on mc.subcategory_id=subcat.id and -        mc.marker_id=mark.id''' +        inner join chimere_marker_categories mc on mc.subcategory_id=subcat.id +        and mc.marker_id=mark.id'''          sql += where          subcats = set(SubCategory.objects.raw(sql))          sql = sql_main + ''' @@ -1391,17 +1452,18 @@ class SimpleArea:          ST_Contains(%s, rt.route))''' % (area, area)          if equal_status:              sql += ' and rt.status' + equal_status -        sql += date_condition % {'alias':'rt'} +        sql += date_condition % {'alias': 'rt'}          sql += ''' -        inner join chimere_route_categories rc on rc.subcategory_id=subcat.id and -        rc.route_id=rt.id''' +        inner join chimere_route_categories rc on rc.subcategory_id=subcat.id +        and rc.route_id=rt.id'''          sql += where -        # subcats.union(set(SubCategory.objects.raw(sql))) +        # subcats.union(set(SubCategory.objects.raw(sql)))          # set union behave strangely. Doing it manualy...          for c in set(SubCategory.objects.raw(sql)):              subcats.add(c)          return subcats +  class Layer(models.Model):      name = models.CharField(_(u"Name"), max_length=150)      layer_code = models.TextField(_(u"Layer code"), max_length=300) @@ -1412,6 +1474,7 @@ class Layer(models.Model):      class Meta:          verbose_name = _("Layer") +  class Area(models.Model, SimpleArea):      """Rectangular area of the map      """ @@ -1422,25 +1485,29 @@ class Area(models.Model, SimpleArea):                                         null=True)      order = models.IntegerField(_(u"Order"), unique=True)      available = models.BooleanField(_(u"Available")) -    upper_left_corner = models.PointField(_(u"Upper left corner"), -            default='POINT(0 0)', srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION) -    lower_right_corner = models.PointField(_(u"Lower right corner"), -            default='POINT(0 0)', srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION) -    default = models.NullBooleanField(_(u"Default area"), -                    help_text=_(u"Only one area is set by default")) +    upper_left_corner = models.PointField( +        _(u"Upper left corner"), default='POINT(0 0)', +        srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION) +    lower_right_corner = models.PointField( +        _(u"Lower right corner"), default='POINT(0 0)', +        srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION) +    default = models.NullBooleanField( +        _(u"Default area"), help_text=_(u"Only one area is set by default"))      layers = SelectMultipleField(Layer, related_name='areas',                                   through='AreaLayers', blank=True) -    default_subcategories = SelectMultipleField(SubCategory, blank=True, -                           verbose_name=_(u"Sub-categories checked by default")) +    default_subcategories = SelectMultipleField( +        SubCategory, blank=True, +        verbose_name=_(u"Sub-categories checked by default"))      dynamic_categories = models.NullBooleanField(          _(u"Sub-categories dynamicaly displayed"), -        help_text=_(u"If checked, categories are only displayed in the menu if " -                    u"they are available on the current extent.")) -    subcategories = SelectMultipleField(SubCategory, related_name='areas', -       blank=True, db_table='chimere_subcategory_areas', -       verbose_name=_(u"Restricted to theses sub-categories"), -       help_text=_(u"If no sub-category is set all sub-categories are " -                   u"available")) +        help_text=_(u"If checked, categories are only displayed in the menu " +                    u"if they are available on the current extent.")) +    subcategories = SelectMultipleField( +        SubCategory, related_name='areas', +        blank=True, db_table='chimere_subcategory_areas', +        verbose_name=_(u"Restricted to theses sub-categories"), +        help_text=_(u"If no sub-category is set all sub-categories are " +                    u"available"))      external_css = models.URLField(_(u"Link to an external CSS"), blank=True,                                     null=True)      restrict_to_extent = models.BooleanField(_(u"Restrict to the area extent"), @@ -1462,13 +1529,13 @@ class Area(models.Model, SimpleArea):      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, +            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, +        )      def getIncludeMarker(self):          """ @@ -1483,12 +1550,15 @@ class Area(models.Model, SimpleArea):          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']:          return @@ -1502,7 +1572,7 @@ def area_post_save(sender, **kwargs):      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, [], [] +    perm = None      if area.urn != old_urn:          oldmnemo = 'change_area_' + old_urn          old_perm = Permission.objects.filter(codename=oldmnemo) @@ -1518,7 +1588,7 @@ def area_post_save(sender, **kwargs):      lbl = "Can change " + area.name      if not perm.count():          content_type, created = ContentType.objects.get_or_create( -                                        app_label="chimere", model="area") +            app_label="chimere", model="area")          perm = Permission(name=lbl, content_type_id=content_type.id,                            codename=mnemo)          perm.save() @@ -1545,13 +1615,14 @@ def area_post_save(sender, **kwargs):                                   ('chimere', 'multimediafile'),                                   ('chimere', 'picturefile'),                                   ('chimere', 'routefile')): -            ct, created = ContentType.objects.get_or_create(app_label=app_label, -                                                            model=model) +            ct, created = ContentType.objects.get_or_create( +                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 @@ -1568,12 +1639,14 @@ def get_areas_for_user(user):                  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() +    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) @@ -1586,6 +1659,7 @@ class AreaLayers(models.Model):          verbose_name = _("Layers")          verbose_name_plural = _("Layers") +  class PropertyModel(models.Model):      '''Model for a property      ''' @@ -1593,10 +1667,11 @@ class PropertyModel(models.Model):      order = models.IntegerField(_(u"Order"))      available = models.BooleanField(_(u"Available"))      mandatory = models.BooleanField(_(u"Mandatory")) -    subcategories = SelectMultipleField(SubCategory, related_name='properties', -       blank=True, verbose_name=_(u"Restricted to theses sub-categories"), -       help_text=_(u"If no sub-category is set all the property applies to all " -                   u"sub-categories")) +    subcategories = SelectMultipleField( +        SubCategory, related_name='properties', +        blank=True, verbose_name=_(u"Restricted to theses sub-categories"), +        help_text=_(u"If no sub-category is set all the property applies to " +                    u"all sub-categories"))      TYPE = (('T', _('Text')),              ('L', _('Long text')),              ('P', _('Password')), @@ -1604,23 +1679,25 @@ class PropertyModel(models.Model):              ('C', _("Choices")),              ('B', _("Boolean")),              ) -    TYPE_WIDGET = {'T':forms.TextInput, -                   'L':TextareaWidget, -                   'P':forms.PasswordInput, -                   'D':DatePickerWidget, -                   'C':forms.Select, -                   'B':forms.CheckboxInput, +    TYPE_WIDGET = {'T': forms.TextInput, +                   'L': TextareaWidget, +                   'P': forms.PasswordInput, +                   'D': DatePickerWidget, +                   'C': forms.Select, +                   'B': forms.CheckboxInput,                     }      type = models.CharField(_(u"Type"), max_length=1, choices=TYPE) -    def __unicode__(self): -        return self.name +      class Meta:          ordering = ('order',)          verbose_name = _("Property model") +    def __unicode__(self): +        return self.name +      def getAttrName(self):          attr_name = defaultfilters.slugify(self.name) -        attr_name = re.sub(r'-','_', attr_name) +        attr_name = re.sub(r'-', '_', attr_name)          return attr_name      def getNamedId(self): @@ -1628,6 +1705,7 @@ class PropertyModel(models.Model):          '''          return 'property_%d_%d' % (self.order, self.id) +  class PropertyModelChoice(models.Model):      '''Choices for property model      ''' @@ -1635,12 +1713,14 @@ class PropertyModelChoice(models.Model):                                        verbose_name=_(u"Property model"))      value = models.CharField(_(u"Value"), max_length=150)      available = models.BooleanField(_(u"Available"), default=True) -    def __unicode__(self): -        return unicode(self.value)      class Meta:          verbose_name = _(u"Model property choice") +    def __unicode__(self): +        return unicode(self.value) + +  class Property(models.Model):      '''Property for a POI      ''' @@ -1648,13 +1728,14 @@ class Property(models.Model):      propertymodel = models.ForeignKey(PropertyModel,                                        verbose_name=_(u"Property model"))      value = models.TextField(_(u"Value")) +      def __unicode__(self):          if self.propertymodel.type == 'C':              if not self.value:                  return ''              try:                  return unicode(PropertyModelChoice.objects.get( -                                                    pk=self.value).value) +                    pk=self.value).value)              except (self.DoesNotExist, ValueError):                  return ""          return unicode(self.value) @@ -1666,7 +1747,8 @@ class Property(models.Model):      def python_value(self):          if self.propertymodel.type == 'D':              try: -                return datetime.date(*[int(val) for val in self.value.split('-')]) +                return datetime.date(*[int(val) +                                       for val in self.value.split('-')])              except:                  return ""          if self.propertymodel.type == 'C' and self.value: @@ -1676,4 +1758,3 @@ class Property(models.Model):                  return None          else:              return self.value - | 
