diff options
Diffstat (limited to 'chimere/models.py')
| -rw-r--r-- | chimere/models.py | 644 | 
1 files changed, 318 insertions, 326 deletions
| diff --git a/chimere/models.py b/chimere/models.py index 825b4c0..cb69684 100644 --- a/chimere/models.py +++ b/chimere/models.py @@ -20,16 +20,17 @@  """  Models description  """ -import os +import copy  import datetime +import json +import os  import pyexiv2  import re -import copy -import simplejson as json +  from lxml import etree  from PIL import Image  from subprocess import Popen, PIPE -from BeautifulSoup import BeautifulSoup +from bs4 import BeautifulSoup  from django import forms  from django.conf import settings @@ -54,21 +55,21 @@ from chimere.utils import KMLManager, OSMManager, ShapefileManager, \  class Page(models.Model):      """Simple extra pages      """ -    title = models.CharField(_(u"Name"), max_length=150) -    mnemonic = models.CharField(_(u"Mnemonic"), max_length=10, blank=True, +    title = models.CharField(_("Name"), max_length=150) +    mnemonic = models.CharField(_("Mnemonic"), max_length=10, blank=True,                                  null=True) -    available = models.BooleanField(_(u"Available"), default=True) -    order = models.IntegerField(_(u"Order"), default=10, blank=True, null=True) -    template_path = models.CharField(_(u"Template path"), max_length=150, +    available = models.BooleanField(_("Available"), default=True) +    order = models.IntegerField(_("Order"), default=10, blank=True, null=True) +    template_path = models.CharField(_("Template path"), max_length=150,                                       blank=True, null=True)      content = models.TextField(blank=True, null=True)      class Meta:          ordering = ["order"] -        verbose_name = _(u"Page") -        verbose_name_plural = _(u"Page") +        verbose_name = _("Page") +        verbose_name_plural = _("Page") -    def __unicode__(self): +    def __str__(self):          return self.title @@ -107,22 +108,22 @@ def shortify(text):  class News(models.Model):      """News of the site      """ -    title = models.CharField(_(u"Name"), max_length=150) -    available = models.BooleanField(_(u"Available")) -    is_front_page = models.NullBooleanField(_(u"Is front page"), blank=True, +    title = models.CharField(_("Name"), max_length=150) +    available = models.BooleanField(_("Available")) +    is_front_page = models.NullBooleanField(_("Is front page"), blank=True,                                              null=True) -    date = models.DateField(_(u"Date")) +    date = models.DateField(_("Date"))      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) +    url = models.URLField(_("Url"), max_length=200, blank=True, null=True) +    areas = SelectMultipleField('Area', verbose_name=_("Associated areas"), +                                blank=True)      class Meta:          ordering = ["-date"] -        verbose_name = _(u"News") -        verbose_name_plural = _(u"News") +        verbose_name = _("News") +        verbose_name_plural = _("News") -    def __unicode__(self): +    def __str__(self):          return self.title      @property @@ -133,15 +134,15 @@ class News(models.Model):  class TinyUrl(models.Model):      """Tinyfied version of permalink parameters      """ -    parameters = models.CharField(_(u"Parameters"), max_length=500, +    parameters = models.CharField(_("Parameters"), max_length=500,                                    unique=True)      digits = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"      base = len(digits)      class Meta: -        verbose_name = _(u"TinyUrl") +        verbose_name = _("TinyUrl") -    def __unicode__(self): +    def __str__(self):          return self.parameters      @classmethod @@ -167,7 +168,7 @@ class TinyUrl(models.Model):          while 1:              idx = n % cls.base              urn = cls.digits[idx] + urn -            n = n / cls.base +            n = int(n / cls.base)              if n == 0:                  break          return urn @@ -176,31 +177,31 @@ class TinyUrl(models.Model):  class ColorTheme(models.Model):      """Color theme      """ -    name = models.CharField(_(u"Name"), max_length=150) +    name = models.CharField(_("Name"), max_length=150)      class Meta: -        verbose_name = _(u"Color theme") +        verbose_name = _("Color theme") -    def __unicode__(self): +    def __str__(self):          return self.name  class Color(models.Model):      """Color      """ -    code = models.CharField(_(u"Code/name"), max_length=200, -                            help_text=_(u"HTML code/name")) -    inner_code = models.CharField(_(u"Code/name (inner)"), max_length=200, -                                  help_text=_(u"HTML code/name"), +    code = models.CharField(_("Code/name"), max_length=200, +                            help_text=_("HTML code/name")) +    inner_code = models.CharField(_("Code/name (inner)"), max_length=200, +                                  help_text=_("HTML code/name"),                                    blank=True, null=True) -    order = models.IntegerField(_(u"Order")) -    color_theme = models.ForeignKey(ColorTheme, verbose_name=_(u"Color theme")) +    order = models.IntegerField(_("Order")) +    color_theme = models.ForeignKey(ColorTheme, verbose_name=_("Color theme"))      class Meta:          ordering = ["order"] -        verbose_name = _(u"Color") +        verbose_name = _("Color") -    def __unicode__(self): +    def __str__(self):          return self.code      @property @@ -215,96 +216,96 @@ class Color(models.Model):  class Category(models.Model):      """Category of Point Of Interest (POI)      """ -    name = models.CharField(_(u"Name"), max_length=150) -    available = models.BooleanField(_(u"Available")) -    order = models.IntegerField(_(u"Order")) +    name = models.CharField(_("Name"), max_length=150) +    available = models.BooleanField(_("Available")) +    order = models.IntegerField(_("Order"))      description = models.TextField(blank=True, null=True)      class Meta:          ordering = ["order"] -        verbose_name = _(u"Category") +        verbose_name = _("Category") -    def __unicode__(self): +    def __str__(self):          return self.name  class Icon(models.Model):      '''Icon      ''' -    name = models.CharField(_(u"Name"), max_length=150) -    image = models.ImageField(_(u"Image"), upload_to='icons', +    name = models.CharField(_("Name"), max_length=150) +    image = models.ImageField(_("Image"), upload_to='icons',                                height_field='height', width_field='width') -    height = models.IntegerField(_(u"Height")) -    width = models.IntegerField(_(u"Width")) +    height = models.IntegerField(_("Height")) +    width = models.IntegerField(_("Width"))      offset_x = models.IntegerField( -        _(u"Offset x"), default=10, -        help_text=_(u"Common value is half the icon width")) +        _("Offset x"), default=10, +        help_text=_("Common value is half the icon width"))      offset_y = models.IntegerField( -        _(u"Offset y"), default=20, -        help_text=_(u"Common value is icon height")) -    popup_offset_x = models.IntegerField(_(u"Popup offset x"), default=0, -                                         help_text=_(u"Common value is 0")) +        _("Offset y"), default=20, +        help_text=_("Common value is icon height")) +    popup_offset_x = models.IntegerField(_("Popup offset x"), default=0, +                                         help_text=_("Common value is 0"))      popup_offset_y = models.IntegerField( -        _(u"Popup offset y"), default=20, -        help_text=_(u"Common value is icon height")) +        _("Popup offset y"), default=20, +        help_text=_("Common value is icon height")) -    def __unicode__(self): +    def __str__(self):          return self.name      class Meta: -        verbose_name = _(u"Icon") +        verbose_name = _("Icon")  class SubCategory(models.Model):      '''Sub-category      ''' -    category = models.ForeignKey(Category, verbose_name=_(u"Category"), +    category = models.ForeignKey(Category, verbose_name=_("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"), +    name = models.CharField(_("Name"), max_length=150) +    available = models.BooleanField(_("Available"), default=True) +    submission = models.BooleanField(_("Available for submission"),                                       default=True) -    TYPE = (('M', _(u'Marker')), -            ('R', _(u'Route')), -            ('P', _(u'Polygon')), -            ('B', _(u'Both')),) -    item_type = models.CharField(_(u"Item type"), max_length=1, choices=TYPE) -    dated = models.BooleanField(_(u"Is dated"), default=False) +    TYPE = (('M', _('Marker')), +            ('R', _('Route')), +            ('P', _('Polygon')), +            ('B', _('Both')),) +    item_type = models.CharField(_("Item type"), max_length=1, choices=TYPE) +    dated = models.BooleanField(_("Is dated"), default=False)      description = models.TextField(blank=True, null=True) -    icon = models.ForeignKey(Icon, verbose_name=_(u"Icon")) +    icon = models.ForeignKey(Icon, verbose_name=_("Icon"))      hover_icon = models.ForeignKey( -        Icon, verbose_name=_(u"Hover icon"), blank=True, null=True, +        Icon, verbose_name=_("Hover icon"), blank=True, null=True,          related_name='subcat_hovered') -    color_theme = models.ForeignKey(ColorTheme, verbose_name=_(u"Color theme"), +    color_theme = models.ForeignKey(ColorTheme, verbose_name=_("Color theme"),                                      blank=True, null=True,                                      related_name='subcategories') -    as_layer = models.BooleanField(_(u"Displayed in the layer menu"), +    as_layer = models.BooleanField(_("Displayed in the layer menu"),                                     default=False) -    weight_formula = models.TextField(_(u"Weight formula"), default="", +    weight_formula = models.TextField(_("Weight formula"), default="",                                        blank=True, null=True) -    routing_warn = models.BooleanField(_(u"Routing warn"), default=False) -    order = models.IntegerField(_(u"Order"), default=1000) -    keywords = models.TextField(_(u"Keywords"), max_length=200, +    routing_warn = models.BooleanField(_("Routing warn"), default=False) +    order = models.IntegerField(_("Order"), default=1000) +    keywords = models.TextField(_("Keywords"), max_length=200,                                  blank=True, null=True)      min_zoom = models.IntegerField( -        _(u"Minimum zoom for loading details"), blank=True, null=True, -        help_text=_(u"Optimization when too many data have to be displayed. " -                    u"Currently available only for route and polygon.")) +        _("Minimum zoom for loading details"), blank=True, null=True, +        help_text=_("Optimization when too many data have to be displayed. " +                    "Currently available only for route and polygon."))      simplify_tolerance = models.FloatField( -        _(u"Simplify tolerance for lower zoom"), blank=True, null=True, -        help_text=_(u"Only relevant when Minimum zoom is set. Use the " -                    u"Douglas-Peucker algorythm to simplify the geometry when " -                    u"details is not alvailable. Adjust to your data volume " -                    u"and your performance need. 0.0003 is a good starting " -                    u"point. Note: typology is not preserved.")) +        _("Simplify tolerance for lower zoom"), blank=True, null=True, +        help_text=_("Only relevant when Minimum zoom is set. Use the " +                    "Douglas-Peucker algorythm to simplify the geometry when " +                    "details is not alvailable. Adjust to your data volume " +                    "and your performance need. 0.0003 is a good starting " +                    "point. Note: typology is not preserved."))      class Meta:          ordering = ["category", "order"] -        verbose_name = _(u"Sub-category") -        verbose_name_plural = _(u"Sub-categories") +        verbose_name = _("Sub-category") +        verbose_name_plural = _("Sub-categories") -    def __unicode__(self): -        return u"%s / %s" % (self.category.name, self.name) +    def __str__(self): +        return "%s / %s" % (self.category.name, self.name)      @classmethod      def getAvailable(cls, item_types=None, area_name=None, public=False, @@ -351,7 +352,7 @@ class SubCategory(models.Model):          cats = []          for cat, subcats in cls.getAvailable(item_types=item_types,                                               area_name=area_name): -            cats.append((unicode(cat), +            cats.append((str(cat),                          [(subcat.pk, subcat.name) for subcat in subcats]))          return cats @@ -402,19 +403,19 @@ class SubCategoryUserLimit(models.Model):          SubCategory, related_name='limited_for_user')      user = models.ForeignKey(User, related_name='subcategory_limit_to') -    def __unicode__(self): -        return u"{} / {}".format(self.user, self.subcategory) +    def __str__(self): +        return "{} / {}".format(self.user, self.subcategory)      class Meta: -        verbose_name = _(u"Sub-category limit for user") -        verbose_name_plural = _(u"Sub-category limits for users") +        verbose_name = _("Sub-category limit for user") +        verbose_name_plural = _("Sub-category limits for users") -STATUS = (('S', _(u'Submited')), -          ('A', _(u'Available')), -          ('M', _(u'Modified')), -          ('D', _(u'Disabled')), -          ('I', _(u'Imported'))) +STATUS = (('S', _('Submited')), +          ('A', _('Available')), +          ('M', _('Modified')), +          ('D', _('Disabled')), +          ('I', _('Imported')))  STATUS_DCT = dict(STATUS)  IMPORTERS = {'KML': KMLManager, @@ -446,60 +447,60 @@ class Importer(models.Model):      '''      Data importer for a specific subcategory      ''' -    importer_type = models.CharField(_(u"Importer type"), max_length=4, +    importer_type = models.CharField(_("Importer type"), max_length=4,                                       choices=IMPORTER_CHOICES) -    filtr = models.TextField(_(u"Filter"), blank=True, null=True) -    source = models.CharField(_(u"Web address"), max_length=200, +    filtr = models.TextField(_("Filter"), blank=True, null=True) +    source = models.CharField(_("Web address"), max_length=200,                                blank=True, null=True, -                              help_text=_(u"Don't forget the trailing slash")) +                              help_text=_("Don't forget the trailing slash"))      source_file = models.FileField( -        _(u"Source file"), upload_to='import_files', blank=True, null=True) +        _("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, +        _("Alt source file"), upload_to='import_files', blank=True, null=True) +    default_name = models.CharField(_("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) -    overwrite = models.BooleanField(_(u"Overwrite existing data"), +    srid = models.IntegerField(_("SRID"), blank=True, null=True) +    zipped = models.BooleanField(_("Zipped file"), default=False) +    overwrite = models.BooleanField(_("Overwrite existing data"),                                      default=False) -    get_description = models.BooleanField(_(u"Get description from source"), +    get_description = models.BooleanField(_("Get description from source"),                                            default=False) -    default_description = models.TextField(_(u"Default description"), +    default_description = models.TextField(_("Default description"),                                             blank=True, null=True) -    origin = models.CharField(_(u"Origin"), max_length=1000, +    origin = models.CharField(_("Origin"), max_length=1000,                                blank=True, null=True) -    license = models.CharField(_(u"License"), max_length=1000, +    license = models.CharField(_("License"), max_length=1000,                                 blank=True, null=True)      categories = SelectMultipleField( -        SubCategory, blank=True, null=True, -        verbose_name=_(u"Associated subcategories")) -    state = models.TextField(_(u"State"), blank=True, null=True) -    automatic_update = models.BooleanField(_(u"Automatically updated"), +        SubCategory, blank=True, +        verbose_name=_("Associated subcategories")) +    state = models.TextField(_("State"), blank=True, null=True) +    automatic_update = models.BooleanField(_("Automatically updated"),                                             default=False) -    default_status = models.CharField(_(u"Default status"), max_length=1, +    default_status = models.CharField(_("Default status"), max_length=1,                                        choices=STATUS, default='I')      default_localisation = PointField( -        _(u"Default localisation"), +        _("Default localisation"),          srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION, blank=True, null=True,          widget=HiddenPointChooserWidget)      class Meta: -        verbose_name = _(u"Importer") +        verbose_name = _("Importer") -    def __unicode__(self): +    def __str__(self):          vals = [IMPORTER_CHOICES_DICT[self.importer_type],                  self.source, self.source_file.name, -                u", ".join([unicode(cat) for cat in self.categories.all()]), +                ", ".join([str(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])) +        return ' %d: %s' % (self.pk, " - ".join([str(v) +                                                 for v in vals if v]))      @property      def manager(self):          return IMPORTERS[self.importer_type](self)      def display_categories(self): -        return u"\n".join([cat.name for cat in self.categories.all()]) +        return "\n".join([cat.name for cat in self.categories.all()])      def get_key_category_dict(self):          dct = {} @@ -519,65 +520,65 @@ class ImporterKeyCategories(models.Model):      """      Association between key and categories      """ -    importer = models.ForeignKey(Importer, verbose_name=_(u"Importer"), +    importer = models.ForeignKey(Importer, verbose_name=_("Importer"),                                   related_name='key_categories') -    category = models.ForeignKey(SubCategory, verbose_name=_(u"Category")) -    key = models.CharField(_(u"Import key"), max_length=200) +    category = models.ForeignKey(SubCategory, verbose_name=_("Category")) +    key = models.CharField(_("Import key"), max_length=200)      class Meta: -        verbose_name = _(u"Importer - Key categories") +        verbose_name = _("Importer - Key categories")  class GeographicItem(models.Model):      categories = SelectMultipleField(SubCategory) -    name = models.TextField(_(u"Name")) +    name = models.TextField(_("Name"))      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"), +        _("Submitter session key"), blank=True, null=True, max_length=40) +    submiter_name = models.CharField(_("Submitter name or nickname"),                                       blank=True, null=True, max_length=40) -    submiter_email = models.EmailField(_(u"Submitter email"), blank=True, +    submiter_email = models.EmailField(_("Submitter email"), blank=True,                                         null=True) -    submiter_comment = models.TextField(_(u"Submitter comment"), +    submiter_comment = models.TextField(_("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, +    status = models.CharField(_("Status"), max_length=1, choices=STATUS) +    keywords = models.TextField(_("Keywords"), max_length=200, blank=True,                                  null=True) -    import_key = models.CharField(_(u"Import key"), max_length=200, +    import_key = models.CharField(_("Import key"), max_length=200,                                    blank=True, null=True) -    import_version = models.IntegerField(_(u"Import version"), +    import_version = models.IntegerField(_("Import version"),                                           blank=True, null=True) -    import_source = models.CharField(_(u"Source"), max_length=200, +    import_source = models.CharField(_("Source"), max_length=200,                                       blank=True, null=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"), +        _("Modified since last import"), default=True) +    not_for_osm = models.BooleanField(_("Not to be exported to OSM"),                                        default=False) -    origin = models.CharField(_(u"Origin"), max_length=1000, +    origin = models.CharField(_("Origin"), max_length=1000,                                blank=True, null=True) -    license = models.CharField(_(u"License"), max_length=1000, +    license = models.CharField(_("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. " -                    u"Format YYYY-MM-DD")) +        _("Start date"), blank=True, null=True, +        help_text=_("Not mandatory. Set it for dated item such as event. " +                    "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 " -                    u"event. Format YYYY-MM-DD")) +        _("End date"), blank=True, null=True, +        help_text=_("Not mandatory. Set it only if you have a multi-day " +                    "event. Format YYYY-MM-DD"))      weight = models.FloatField( -        _(u"Weight"), blank=True, null=True, +        _("Weight"), blank=True, null=True,          help_text=_( -            u"Weight are used for heatmap and clustering. A formula must " -            u"defined in the associated category.")) +            "Weight are used for heatmap and clustering. A formula must " +            "defined in the associated category."))      normalised_weight = models.FloatField( -        _(u"Normalised weight"), blank=True, null=True, -        help_text=_(u"The weight normalised to be between 0 and 1. " -                    u"Automatically recalculated.")) +        _("Normalised weight"), blank=True, null=True, +        help_text=_("The weight normalised to be between 0 and 1. " +                    "Automatically recalculated."))      class Meta:          abstract = True -    def __unicode__(self): +    def __str__(self):          return self.name      def __init__(self, *args, **kwargs): @@ -637,7 +638,7 @@ class GeographicItem(models.Model):          return properties      def setProperty(self, pm, value): -        u""" +        """          Set a property          """          if not hasattr(pm, 'pk'): @@ -678,9 +679,9 @@ class GeographicItem(models.Model):          Save properties          """          for propertymodel in PropertyModel.objects.filter(available=True): -            val = u"" -            if unicode(propertymodel.id) in values: -                val = values[unicode(propertymodel.id)] +            val = "" +            if str(propertymodel.id) in values: +                val = values[str(propertymodel.id)]              self.setProperty(propertymodel, val)      def get_key(self, key): @@ -740,7 +741,7 @@ class GeographicItem(models.Model):          dct = {}          # get all property even the one not displayed          for pm in PropertyModel.objects.all(): -            dct[pm.slug] = unicode(self.getProperty(pm)) +            dct[pm.slug] = str(self.getProperty(pm))          return dct      def calculate_weight(self, formula): @@ -811,20 +812,20 @@ class Marker(GeographicItem):      '''Marker for a POI      '''      ref_item = models.ForeignKey( -        "Marker", blank=True, null=True, verbose_name=_(u"Reference marker"), +        "Marker", blank=True, null=True, verbose_name=_("Reference marker"),          related_name='submited_marker') -    point = PointField(_(u"Localisation"), +    point = PointField(_("Localisation"),                         srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION) -    available_date = models.DateTimeField(_(u"Available Date"), blank=True, +    available_date = models.DateTimeField(_("Available Date"), blank=True,                                            null=True)  # used by feeds -    description = models.TextField(_(u"Description"), blank=True, null=True) -    is_front_page = models.NullBooleanField(_(u"Is front page"), blank=True, +    description = models.TextField(_("Description"), blank=True, null=True) +    is_front_page = models.NullBooleanField(_("Is front page"), blank=True,                                              null=True)      objects = models.GeoManager()      class Meta:          ordering = ('status', 'name') -        verbose_name = _(u"Point of interest") +        verbose_name = _("Point of interest")      @property      def multimedia_items(self): @@ -885,8 +886,8 @@ class Marker(GeographicItem):                  'pk': self.id,                  'key': "marker-{}".format(self.id),                  'name': self.name, -                'icon_path': unicode(cat.icon.image), -                'icon_hover_path': unicode(cat.hover_icon.image) +                'icon_path': str(cat.icon.image), +                'icon_hover_path': str(cat.hover_icon.image)                  if cat.hover_icon else '',                  'icon_offset_x': cat.icon.offset_x,                  'icon_offset_y': cat.icon.offset_y, @@ -925,8 +926,8 @@ class Marker(GeographicItem):                  except SubCategory.DoesNotExist:                      continue                  cats[item['categories__pk']] = { -                    'icon_path': unicode(cat.icon.image), -                    'icon_hover_path': unicode(cat.hover_icon.image) +                    'icon_path': str(cat.icon.image), +                    'icon_hover_path': str(cat.hover_icon.image)                      if cat.hover_icon else '',                      'icon_offset_x': cat.icon.offset_x,                      'icon_offset_y': cat.icon.offset_y, @@ -1036,26 +1037,26 @@ class Polygon(GeographicItem):      '''Polygon on the map      '''      ref_item = models.ForeignKey( -        "Polygon", blank=True, null=True, verbose_name=_(u"Reference polygon"), +        "Polygon", blank=True, null=True, verbose_name=_("Reference polygon"),          related_name='submited_polygon')      polygon = PolygonField( -        _(u"Polygon"), srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION) +        _("Polygon"), srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION)      picture = models.ImageField( -        _(u"Image"), upload_to='upload', blank=True, null=True, +        _("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) +    height = models.IntegerField(_("Height"), blank=True, null=True) +    width = models.IntegerField(_("Width"), blank=True, null=True)      color = models.CharField( -        _(u"Color"), max_length=200, help_text=_(u"HTML code/name"), +        _("Color"), max_length=200, help_text=_("HTML code/name"),          blank=True, null=True)      inner_color = models.CharField( -        _(u"Inner color"), max_length=200, -        help_text=_(u"HTML code/name"), blank=True, null=True) +        _("Inner color"), max_length=200, +        help_text=_("HTML code/name"), blank=True, null=True)      objects = models.GeoManager()      class Meta:          ordering = ('status', 'name') -        verbose_name = _(u"Polygon") +        verbose_name = _("Polygon")      @property      def geom_attr(self): @@ -1142,7 +1143,7 @@ class AggregatedPolygon(models.Model):      '''      polygon = models.MultiPolygonField()      subcategory = models.ForeignKey(SubCategory) -    status = models.CharField(_(u"Status"), max_length=1, choices=STATUS) +    status = models.CharField(_("Status"), max_length=1, choices=STATUS)      class Meta:          managed = False @@ -1180,28 +1181,28 @@ class AggregatedPolygon(models.Model):                  'color': color,                  'inner_color': inner_color,                  'key': "aggpoly-{}".format(self.pk), -                "pk": self.id, "name": u'Aggregated polygon'}} +                "pk": self.id, "name": 'Aggregated polygon'}}          return json.dumps(attributes)  class MultimediaType(models.Model): -    MEDIA_TYPES = (('A', _(u"Audio")), -                   ('V', _(u"Video")), -                   ('I', _(u"Image")), -                   ('O', _(u"Other")),) -    media_type = models.CharField(_(u"Media type"), max_length=1, +    MEDIA_TYPES = (('A', _("Audio")), +                   ('V', _("Video")), +                   ('I', _("Image")), +                   ('O', _("Other")),) +    media_type = models.CharField(_("Media type"), max_length=1,                                    choices=MEDIA_TYPES) -    name = models.CharField(_(u"Name"), max_length=150) -    mime_type = models.CharField(_(u"Mime type"), max_length=50, blank=True, +    name = models.CharField(_("Name"), max_length=150) +    mime_type = models.CharField(_("Mime type"), max_length=50, blank=True,                                   null=True) -    iframe = models.BooleanField(_(u"Inside an iframe"), default=False) -    available = models.BooleanField(_(u"Available"), default=True) +    iframe = models.BooleanField(_("Inside an iframe"), default=False) +    available = models.BooleanField(_("Available"), default=True)      class Meta: -        verbose_name = _(u"Multimedia type") -        verbose_name_plural = _(u"Multimedia types") +        verbose_name = _("Multimedia type") +        verbose_name_plural = _("Multimedia types") -    def __unicode__(self): +    def __str__(self):          return self.name      @classmethod @@ -1236,26 +1237,26 @@ IFRAME_LINKS = {  class MultimediaExtension(models.Model): -    name = models.CharField(_(u"Extension name"), max_length=6) +    name = models.CharField(_("Extension name"), max_length=6)      multimedia_type = models.ForeignKey( -        MultimediaType, verbose_name=_(u"Associated multimedia type"), +        MultimediaType, verbose_name=_("Associated multimedia type"),          related_name='extensions')      class Meta: -        verbose_name = _(u"Multimedia extension") -        verbose_name_plural = _(u"Multimedia extensions") +        verbose_name = _("Multimedia extension") +        verbose_name_plural = _("Multimedia extensions") -    def __unicode__(self): +    def __str__(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) +    name = models.CharField(_("Name"), max_length=150) +    url = models.URLField(_("Url"), max_length=200) +    order = models.IntegerField(_("Order"), default=1)      multimedia_type = models.ForeignKey(MultimediaType, blank=True, null=True)      miniature = models.BooleanField( -        _(u"Display inside the description?"), +        _("Display inside the description?"),          default=settings.CHIMERE_MINIATURE_BY_DEFAULT)      marker = models.ForeignKey(Marker, related_name='multimedia_files',                                 blank=True, null=True) @@ -1265,11 +1266,11 @@ class MultimediaFile(models.Model):                                  blank=True, null=True)      class Meta: -        verbose_name = _(u"Multimedia file") -        verbose_name_plural = _(u"Multimedia files") +        verbose_name = _("Multimedia file") +        verbose_name_plural = _("Multimedia files") -    def __unicode__(self): -        return self.name or u"" +    def __str__(self): +        return self.name or ""  def multimediafile_post_save(sender, **kwargs): @@ -1320,22 +1321,22 @@ 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', +    name = models.CharField(_("Name"), max_length=150) +    picture = models.ImageField(_("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) +    height = models.IntegerField(_("Height"), blank=True, null=True) +    width = models.IntegerField(_("Width"), blank=True, null=True)      miniature = models.BooleanField( -        _(u"Display inside the description?"), +        _("Display inside the description?"),          default=settings.CHIMERE_MINIATURE_BY_DEFAULT)      thumbnailfile = models.ImageField( -        _(u"Thumbnail"), upload_to='pictures', blank=True, null=True, +        _("Thumbnail"), upload_to='pictures', blank=True, null=True,          height_field='thumbnailfile_height', width_field='thumbnailfile_width') -    thumbnailfile_height = models.IntegerField(_(u"Thumbnail height"), +    thumbnailfile_height = models.IntegerField(_("Thumbnail height"),                                                 blank=True, null=True) -    thumbnailfile_width = models.IntegerField(_(u"Thumbnail width"), +    thumbnailfile_width = models.IntegerField(_("Thumbnail width"),                                                blank=True, null=True) -    order = models.IntegerField(_(u"Order"), default=1) +    order = models.IntegerField(_("Order"), default=1)      marker = models.ForeignKey(Marker, related_name='pictures', blank=True,                                 null=True)      route = models.ForeignKey('Route', related_name='pictures', @@ -1343,12 +1344,12 @@ class PictureFile(models.Model):      polygon = models.ForeignKey(Polygon, related_name='pictures', blank=True,                                  null=True) -    def __unicode__(self): -        return self.name or u"" +    def __str__(self): +        return self.name or ""      class Meta: -        verbose_name = _(u"Picture file") -        verbose_name_plural = _(u"Picture files") +        verbose_name = _("Picture file") +        verbose_name_plural = _("Picture files")  def scale_image(max_x, pair): @@ -1363,8 +1364,6 @@ IMAGE_EXIF_ORIENTATION_MAP = {      6: 4,  } -PYEXIV2_OLD_API = not hasattr(pyexiv2, 'ImageMetadata') -  def picturefile_post_save(sender, **kwargs):      if not kwargs['instance']: @@ -1374,23 +1373,14 @@ def picturefile_post_save(sender, **kwargs):      if kwargs['created']:          filename = picturefile.picture.path          metadata, orientation = None, None -        if PYEXIV2_OLD_API: -            metadata = pyexiv2.Image(filename) -            metadata.readMetadata() -            orientation = metadata['Exif.Image.Orientation'] \ -                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 +        metadata = pyexiv2.ImageMetadata(filename) +        metadata.read() +        orientation = metadata['Exif.Image.Orientation'].value \ +            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 -            if PYEXIV2_OLD_API: -                metadata.writeMetadata() -            else: -                metadata.write() +            metadata.write()              im = Image.open(filename)              im = im.transpose(IMAGE_EXIF_ORIENTATION_MAP[orientation])              im.save(filename) @@ -1444,20 +1434,20 @@ 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)"), +    name = models.CharField(_("Name"), max_length=150) +    raw_file = models.FileField(_("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'))) +        _("Simplified file"), upload_to='route_files', blank=True, null=True) +    TYPE = (('K', _('KML')), ('G', _('GPX')))      file_type = models.CharField(max_length=1, choices=TYPE)      class Meta:          ordering = ('name',) -        verbose_name = _(u"Route file") -        verbose_name_plural = _(u"Route files") +        verbose_name = _("Route file") +        verbose_name_plural = _("Route files") -    def __unicode__(self): +    def __str__(self):          return self.name      def process(self): @@ -1476,7 +1466,7 @@ class RouteFile(models.Model):          p = Popen(cli_args, stderr=PIPE)          p.wait()          if p.returncode: -            print p.stderr.read() +            print(p.stderr.read())              # logger.error(p.stderr.read())          else:              self.simplified_file = File(open(output_name)) @@ -1495,34 +1485,34 @@ class RouteFile(models.Model):              if not pt.tag.endswith('trkpt'):                  continue              pts.append((pt.get("lon"), pt.get("lat"))) -        wkt_tpl = u'LINESTRING(%s)' -        return wkt_tpl % u','.join([u'%s %s' % (pt[0], pt[1]) -                                    for pt in pts]) +        wkt_tpl = 'LINESTRING(%s)' +        return wkt_tpl % ','.join(['%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"), +        "Route", blank=True, null=True, verbose_name=_("Reference route"),          related_name='submited_route') -    route = RouteField(_(u"Route"), +    route = RouteField(_("Route"),                         srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION)      associated_file = models.ForeignKey(RouteFile, blank=True, null=True, -                                        verbose_name=_(u"Associated file")) +                                        verbose_name=_("Associated file"))      picture = models.ImageField( -        _(u"Image"), upload_to='upload', blank=True, null=True, +        _("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) +    height = models.IntegerField(_("Height"), blank=True, null=True) +    width = models.IntegerField(_("Width"), blank=True, null=True)      color = models.CharField( -        _(u"Color"), max_length=200, help_text=_(u"HTML code/name"), +        _("Color"), max_length=200, help_text=_("HTML code/name"),          blank=True, null=True)      objects = models.GeoManager()      class Meta:          ordering = ('status', 'name') -        verbose_name = _(u"Route") +        verbose_name = _("Route")      @property      def geom_attr(self): @@ -1632,7 +1622,7 @@ class AggregatedRoute(models.Model):      '''      route = models.MultiLineStringField()      subcategory = models.ForeignKey(SubCategory) -    status = models.CharField(_(u"Status"), max_length=1, choices=STATUS) +    status = models.CharField(_("Status"), max_length=1, choices=STATUS)      class Meta:          managed = False @@ -1651,7 +1641,7 @@ class AggregatedRoute(models.Model):              'color': color, 'geometry': geom,              'type': "Feature", "properties": {                  'key': "aggroute-{}".format(self.pk), -                "pk": self.id, "name": u'Aggregated route'}} +                "pk": self.id, "name": 'Aggregated route'}}          return json.dumps(attributes) @@ -1667,14 +1657,17 @@ class SimpleArea:      """      Rectangular area of a map (not in the database)      """ -    def __init__(self, area): +    def __init__(self, area=None):          """ -        Defining upper left corner ans lower right corner from a tuple +        Defining upper left corner and lower right corner from a tuple          """ +        if not area: +            return super(SimpleArea, self).__init__()          assert len(area) == 4          x1, y1, x2, y2 = area          self.upper_left_corner = SimplePoint(x1, y1)          self.lower_right_corner = SimplePoint(x2, y2) +        return super(SimpleArea, self).__init__()      def isIn(self, area):          """ @@ -1696,7 +1689,7 @@ class SimpleArea:              subcategory_pks = []              for cat, subcats in SubCategory.getAvailable(area_name=area_name):                  for subcat in subcats: -                    subcategory_pks.append(unicode(subcat.pk)) +                    subcategory_pks.append(str(subcat.pk))              if filter_available:                  wheres += ['subcat.available = TRUE', 'cat.available = TRUE']              wheres += ['subcat.id in (%s)' % ",".join(subcategory_pks)] @@ -1707,8 +1700,8 @@ class SimpleArea:              equal_status = "='%s'" % status[0]          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)" % ( +        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,                  self.lower_right_corner.x, self.upper_left_corner.y,                  self.lower_right_corner.x, self.lower_right_corner.y, @@ -1767,20 +1760,20 @@ class SimpleArea:          return subcats      def getExtent(self): -        return (unicode(self.upper_left_corner.x), -                unicode(self.upper_left_corner.y), -                unicode(self.lower_right_corner.x), -                unicode(self.lower_right_corner.y)) +        return (str(self.upper_left_corner.x), +                str(self.upper_left_corner.y), +                str(self.lower_right_corner.x), +                str(self.lower_right_corner.y))  class Layer(models.Model): -    name = models.CharField(_(u"Name"), max_length=150) -    layer_code = models.TextField(_(u"Layer code")) +    name = models.CharField(_("Name"), max_length=150) +    layer_code = models.TextField(_("Layer code"))      extra_js_code = models.TextField( -        _(u"Extra JS code"), blank=True, null=True, default='', -        help_text=_(u"This code is loaded before the layer code.")) +        _("Extra JS code"), blank=True, null=True, default='', +        help_text=_("This code is loaded before the layer code.")) -    def __unicode__(self): +    def __str__(self):          return self.name      class Meta: @@ -1790,57 +1783,57 @@ class Layer(models.Model):  class Area(models.Model, SimpleArea):      """Rectangular area of the map      """ -    name = models.CharField(_(u"Name"), max_length=150) -    urn = models.SlugField(_(u"Area urn"), max_length=50, blank=True, +    name = models.CharField(_("Name"), max_length=150) +    urn = models.SlugField(_("Area urn"), max_length=50, blank=True,                             unique=True) -    welcome_message = models.TextField(_(u"Welcome message"), blank=True, +    welcome_message = models.TextField(_("Welcome message"), blank=True,                                         null=True) -    order = models.IntegerField(_(u"Order"), unique=True) -    available = models.BooleanField(_(u"Available")) +    order = models.IntegerField(_("Order"), unique=True) +    available = models.BooleanField(_("Available"))      upper_left_corner = models.PointField( -        _(u"Upper left corner"), default='POINT(0 0)', +        _("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)', +        _("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")) +        _("Default area"), help_text=_("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")) +        verbose_name=_("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 " -                    u"if they are available on the current extent.")) +        _("Sub-categories dynamicaly displayed"), +        help_text=_("If checked, categories are only displayed in the menu " +                    "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")) +        verbose_name=_("Restricted to theses sub-categories"), +        help_text=_("If no sub-category is set all sub-categories are " +                    "available"))      display_category_menu = models.BooleanField( -        _(u"Display category menu"), default=True, -        help_text=_(u"If set to False, category menu will be hide and all " -                    u"categories will be always displayed.")) -    external_css = models.URLField(_(u"Link to an external CSS"), blank=True, +        _("Display category menu"), default=True, +        help_text=_("If set to False, category menu will be hide and all " +                    "categories will be always displayed.")) +    external_css = models.URLField(_("Link to an external CSS"), blank=True,                                     null=True) -    restrict_to_extent = models.BooleanField(_(u"Restrict to the area extent"), +    restrict_to_extent = models.BooleanField(_("Restrict to the area extent"),                                               default=False) -    allow_point_edition = models.BooleanField(_(u"Allow point edition"), +    allow_point_edition = models.BooleanField(_("Allow point edition"),                                                default=True) -    allow_route_edition = models.BooleanField(_(u"Allow route edition"), +    allow_route_edition = models.BooleanField(_("Allow route edition"),                                                default=True) -    allow_polygon_edition = models.BooleanField(_(u"Allow polygon edition"), +    allow_polygon_edition = models.BooleanField(_("Allow polygon edition"),                                                  default=True)      extra_map_def = models.TextField( -        _(u"Extra map definition"), blank=True, null=True, -        help_text=_(u"Extra javascript script loaded for this area. " -                    u"Carreful! To prevent breaking the map must be valid.")) +        _("Extra map definition"), blank=True, null=True, +        help_text=_("Extra javascript script loaded for this area. " +                    "Carreful! To prevent breaking the map must be valid."))      objects = models.GeoManager() -    def __unicode__(self): +    def __str__(self):          return self.name      class Meta: @@ -1984,8 +1977,8 @@ def get_users_by_area(area):  class AreaLayers(models.Model):      area = models.ForeignKey(Area)      layer = models.ForeignKey(Layer) -    order = models.IntegerField(_(u"Order")) -    default = models.NullBooleanField(_(u"Default layer")) +    order = models.IntegerField(_("Order")) +    default = models.NullBooleanField(_("Default layer"))      class Meta:          ordering = ('order',) @@ -1996,21 +1989,20 @@ class AreaLayers(models.Model):  class PropertyModel(models.Model):      '''Model for a property      ''' -    name = models.CharField(_(u"Name"), max_length=150) -    slug = models.SlugField(_(u"Slug"), blank=True, null=True) -    order = models.IntegerField(_(u"Order")) -    available = models.BooleanField(_(u"Available")) -    mandatory = models.BooleanField(_(u"Mandatory")) +    name = models.CharField(_("Name"), max_length=150) +    slug = models.SlugField(_("Slug"), blank=True, null=True) +    order = models.IntegerField(_("Order")) +    available = models.BooleanField(_("Available")) +    mandatory = models.BooleanField(_("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 " -                    u"all sub-categories")) +        blank=True, verbose_name=_("Restricted to theses sub-categories"), +        help_text=_("If no sub-category is set all the property applies to " +                    "all sub-categories"))      areas = SelectMultipleField( -        'Area', verbose_name=_(u"Restrict to theses areas"), blank=True, -        null=True, -        help_text=_(u"If no area is set the property apply to " -                    u"all areas")) +        'Area', verbose_name=_("Restrict to theses areas"), blank=True, +        help_text=_("If no area is set the property apply to " +                    "all areas"))      TYPE = (('T', _('Text')),              ('L', _('Long text')),              ('P', _('Password')), @@ -2027,13 +2019,13 @@ class PropertyModel(models.Model):                     'A': JQueryAutoComplete,                     'B': forms.CheckboxInput,                     } -    type = models.CharField(_(u"Type"), max_length=1, choices=TYPE) +    type = models.CharField(_("Type"), max_length=1, choices=TYPE)      class Meta:          ordering = ('order',)          verbose_name = _("Property model") -    def __unicode__(self): +    def __str__(self):          return self.name      def getAttrName(self): @@ -2064,41 +2056,41 @@ class PropertyModelChoice(models.Model):      '''Choices for property model      '''      propertymodel = models.ForeignKey(PropertyModel, related_name='choices', -                                      verbose_name=_(u"Property model")) -    value = models.CharField(_(u"Value"), max_length=150) -    available = models.BooleanField(_(u"Available"), default=True) +                                      verbose_name=_("Property model")) +    value = models.CharField(_("Value"), max_length=150) +    available = models.BooleanField(_("Available"), default=True)      class Meta: -        verbose_name = _(u"Model property choice") +        verbose_name = _("Model property choice") -    def __unicode__(self): -        return unicode(self.value) +    def __str__(self): +        return str(self.value)  class Property(models.Model):      '''Property for a POI      '''      marker = models.ForeignKey( -        Marker, verbose_name=_(u"Point of interest"), blank=True, null=True) +        Marker, verbose_name=_("Point of interest"), blank=True, null=True)      polygon = models.ForeignKey( -        Polygon, verbose_name=_(u"Polygon"), blank=True, null=True) +        Polygon, verbose_name=_("Polygon"), blank=True, null=True)      propertymodel = models.ForeignKey(PropertyModel, -                                      verbose_name=_(u"Property model")) -    value = models.TextField(_(u"Value")) +                                      verbose_name=_("Property model")) +    value = models.TextField(_("Value")) -    def __unicode__(self): +    def __str__(self):          if self.propertymodel.type == 'C':              if not self.value:                  return ''              try: -                return unicode(PropertyModelChoice.objects.get( +                return str(PropertyModelChoice.objects.get(                      pk=self.value).value)              except (self.DoesNotExist, ValueError):                  return "" -        return unicode(self.value) +        return str(self.value)      class Meta: -        verbose_name = _(u"Property") +        verbose_name = _("Property")      @property      def python_value(self): | 
