diff options
Diffstat (limited to 'chimere/models.py')
-rw-r--r-- | chimere/models.py | 791 |
1 files changed, 451 insertions, 340 deletions
diff --git a/chimere/models.py b/chimere/models.py index 122d61f..f605e08 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, PolygonField from chimere.utils import KMLManager, OSMManager, ShapefileManager, \ GeoRSSManager, CSVManager, HtmlXsltManager, XMLXsltManager, JsonManager, \ IcalManager @@ -61,13 +61,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 @@ -77,6 +80,7 @@ def page_post_save(sender, **kwargs): page.save() post_save.connect(page_post_save, sender=Page) + def shortify(text): if not text: return '' @@ -89,15 +93,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 """ @@ -110,33 +115,39 @@ 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, unique=True) - def __unicode__(self): - return self.parameters - class Meta: - verbose_name = _(u"TinyUrl") + parameters = models.CharField(_(u"Parameters"), max_length=500, + unique=True) 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: @@ -160,27 +171,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) """ @@ -188,12 +206,15 @@ 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 ''' @@ -202,11 +223,18 @@ class Icon(models.Model): height_field='height', width_field='width') height = models.IntegerField(_(u"Height")) width = models.IntegerField(_(u"Width")) + offset_x = models.IntegerField(_(u"Offset x"), default=10) + offset_y = models.IntegerField(_(u"Offset y"), default=20) + popup_offset_x = models.IntegerField(_(u"Popup offset x"), default=5) + popup_offset_y = models.IntegerField(_(u"Popup offset y"), default=20) + def __unicode__(self): return self.name + class Meta: verbose_name = _(u"Icon") + class SubCategory(models.Model): '''Sub-category ''' @@ -218,13 +246,15 @@ class SubCategory(models.Model): 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) 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"), @@ -232,14 +262,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 @@ -270,8 +302,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 @@ -286,15 +318,20 @@ class SubCategory(models.Model): return cats def getJSONDict(self): - items = {'id':self.pk, 'name':self.name, - 'description':self.description if self.description\ - else '', - 'icon':{'url':self.icon.image.url, - 'width':self.icon.image.width, - 'height':self.icon.image.height} - } + items = {'id': self.pk, 'name': self.name, + 'description': self.description if self.description + else '', + 'icon': {'url': self.icon.image.url, + 'width': self.icon.image.width, + 'height': self.icon.image.height, + 'offset_x': self.icon.offset_x, + '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} + items['icon_hover'] = {'url': self.hover_icon.image.url} return items def getJSON(self, categories_id=[]): @@ -370,10 +407,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) @@ -388,19 +425,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") @@ -446,47 +484,139 @@ class ImporterKeyCategories(models.Model): class Meta: verbose_name = _(u"Importer - Key categories") + class GeographicItem(models.Model): name = models.TextField(_(u"Name")) 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 __unicode__(self): + return self.name + + def __init__(self, *args, **kwargs): + super(GeographicItem, self).__init__(*args, **kwargs) + # add read attributes for properties + for pm in self.all_properties(): + attr_name = pm.getAttrName() + if not hasattr(self, attr_name): + val = '' + property = self.getProperty(pm) + if property: + val = property.python_value + setattr(self, attr_name, val) + if not hasattr(self, attr_name + '_set'): + setattr(self, attr_name + '_set', + property_setter(self.__class__, pm)) + + @property + def geometry(self): + return getattr(self, self.geom_attr).wkt + + def getProperty(self, propertymodel, safe=None): + """Get the property of an associated property model. + If safe set to True, verify if the property is available + """ + if safe and not propertymodel.available: + return + try: + property = Property.objects.get(propertymodel=propertymodel, + marker=self) + except Property.DoesNotExist: + return + return property + + def getProperties(self): + """Get all the property availables + """ + properties = [] + for pm in PropertyModel.objects.filter(available=True): + property = self.getProperty(pm) + if property: + properties.append(property) + return properties + + def setProperty(self, pm, value): + u""" + Set a property + """ + properties = Property.objects.filter(marker=self, + propertymodel=pm).all() + # in case of multiple edition as the same time delete arbitrary + # the others + if len(properties) > 1: + for property in properties[1:]: + property.delete() + if pm.type == 'C' and value: + try: + value = str(int(value)) + except ValueError: + choice = PropertyModelChoice.objects.filter(propertymodel=pm, + value=value) + if choice.count(): + value = choice.all()[0].pk + else: + 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.save() + else: + property = properties[0] + property.value = value + property.save() + + def saveProperties(self, values): + """ + Save properties + """ + for propertymodel in PropertyModel.objects.filter(available=True): + val = u"" + if unicode(propertymodel.id) in values: + val = values[unicode(propertymodel.id)] + self.setProperty(propertymodel, val) + 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): @@ -513,8 +643,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 @@ -526,6 +656,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 @@ -536,15 +667,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) @@ -552,23 +685,9 @@ class Marker(GeographicItem): null=True) objects = models.GeoManager() - def __unicode__(self): - return self.name - - def __init__(self, *args, **kwargs): - super(Marker, self).__init__(*args, **kwargs) - # add read attributes for properties - for pm in self.all_properties(): - attr_name = pm.getAttrName() - if not hasattr(self, attr_name): - val = '' - property = self.getProperty(pm) - if property: - val = property.python_value - setattr(self, attr_name, val) - if not hasattr(self, attr_name + '_set'): - setattr(self, attr_name + '_set', - property_setter(self.__class__, pm)) + class Meta: + ordering = ('status', 'name') + verbose_name = _(u"Point of interest") def get_init_multi(self): multis = [forms.model_to_dict(multi) @@ -609,17 +728,9 @@ class Marker(GeographicItem): return shortify(self.description) @property - def geometry(self): - return self.point.wkt - - @property def geom_attr(self): return 'point' - class Meta: - ordering = ('status', 'name') - verbose_name = _(u"Point of interest") - def getLatitude(self): '''Return the latitude ''' @@ -630,93 +741,32 @@ class Marker(GeographicItem): ''' return self.point.x - def getProperty(self, propertymodel, safe=None): - """Get the property of an associated property model. - If safe set to True, verify if the property is available - """ - if safe and not propertymodel.available: - return - try: - property = Property.objects.get(propertymodel=propertymodel, - marker=self) - except Property.DoesNotExist: - return - return property - - def getProperties(self): - """Get all the property availables - """ - properties = [] - for pm in PropertyModel.objects.filter(available=True): - property = self.getProperty(pm) - if property: - properties.append(property) - return properties - - def setProperty(self, pm, value): - u""" - Set a property - """ - properties = Property.objects.filter(marker=self, - propertymodel=pm).all() - # in case of multiple edition as the same time delete arbitrary - # the others - if len(properties) > 1: - for property in properties[1:]: - property.delete() - if pm.type == 'C' and value: - try: - value = str(int(value)) - except ValueError: - choice = PropertyModelChoice.objects.filter(propertymodel=pm, - value=value) - if choice.count(): - value = choice.all()[0].pk - else: - 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.save() - else: - property = properties[0] - property.value = value - property.save() - - def saveProperties(self, values): - """ - Save properties - """ - for propertymodel in PropertyModel.objects.filter(available=True): - val = u"" - if unicode(propertymodel.id) in values: - val = values[unicode(propertymodel.id)] - self.setProperty(propertymodel, val) - def getGeoJSON(self, categories_id=[]): '''Return a GeoJSON string ''' jsons = [] - json_tpl = {"type":"Feature", "properties":{}} + json_tpl = {"type": "Feature", "properties": {}} for cat in self.categories.all(): if categories_id and cat.id not in categories_id: continue items = copy.deepcopy(json_tpl) items['geometry'] = json.loads(self.point.geojson) items['properties'].update({ - 'pk':self.id, - 'name':self.name, - 'icon_path':unicode(cat.icon.image), - 'icon_hover_path':unicode(cat.hover_icon.image) \ - if cat.hover_icon else '', - 'category_name':cat.name}) + 'pk': self.id, + 'name': self.name, + 'icon_path': unicode(cat.icon.image), + 'icon_hover_path': unicode(cat.hover_icon.image) + if cat.hover_icon else '', + 'icon_offset_x': cat.icon.offset_x, + 'icon_offset_y': cat.icon.offset_y, + 'icon_popup_offset_x': cat.icon.popup_offset_x, + 'icon_popup_offset_y': cat.icon.popup_offset_y, + 'category_name': cat.name}) try: - items['properties'].update({'icon_width':cat.icon.image.width, - 'icon_height':cat.icon.image.height,}) + items['properties'].update( + {'icon_width': cat.icon.image.width, + 'icon_height': cat.icon.image.height, + }) except IOError: pass @@ -742,11 +792,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: @@ -762,12 +814,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'] \ @@ -785,18 +840,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")), @@ -832,25 +890,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") @@ -859,13 +919,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: @@ -875,6 +937,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 @@ -886,7 +949,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] @@ -896,10 +959,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) @@ -909,8 +974,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() @@ -918,18 +984,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"), @@ -944,6 +1011,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 @@ -958,6 +1026,7 @@ IMAGE_EXIF_ORIENTATION_MAP = { PYEXIV2_OLD_API = not hasattr(pyexiv2, 'ImageMetadata') + def picturefile_post_save(sender, **kwargs): if not kwargs['instance']: return @@ -970,12 +1039,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 @@ -993,7 +1062,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) @@ -1005,12 +1074,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') @@ -1027,8 +1094,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() @@ -1036,12 +1103,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) @@ -1058,7 +1126,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') @@ -1070,7 +1138,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() @@ -1088,32 +1156,30 @@ 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"), default=True) objects = models.GeoManager() - def __unicode__(self): - return self.name - class Meta: ordering = ('status', 'name') verbose_name = _(u"Route") @@ -1138,11 +1204,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 - def geometry(self): - return self.route.wkt + property_setter(self.__class__, pm)) @property def geom_attr(self): @@ -1151,8 +1213,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): @@ -1162,52 +1226,27 @@ class Route(GeographicItem): for pict in self.associated_marker.all()[0].pictures.all()] return picts - def getProperty(self, propertymodel, safe=None): - """Get the property of an associated property model. - If safe set to True, verify if the property is available - """ - if safe and not propertymodel.available: - return - try: - property = Property.objects.get(propertymodel=propertymodel, - marker=self) - except Property.DoesNotExist: - return - return property - - def getProperties(self): - """Get all the property availables - """ - properties = [] - for pm in PropertyModel.objects.filter(available=True): - property = self.getProperty(pm) - if property: - properties.append(property) - return properties - def getGeoJSON(self, color="#000"): '''Return a GeoJSON string ''' 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) - 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 @@ -1218,10 +1257,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: @@ -1241,6 +1281,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 @@ -1256,6 +1297,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 @@ -1263,9 +1305,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 + "')" @@ -1273,6 +1315,7 @@ def getDateCondition(): date_condition += "%(alias)s.end_date >='" + now + "')) " return date_condition + class AggregatedRoute(models.Model): ''' Database view for aggregated routes @@ -1280,6 +1323,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' @@ -1289,11 +1333,51 @@ 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 Polygon(GeographicItem): + '''Polygon on the map + ''' + ref_item = models.ForeignKey( + "Polygon", blank=True, null=True, verbose_name=_(u"Reference polygon"), + related_name='submited_polygon') + polygon = PolygonField( + _(u"Polygon"), srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION) + 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) + objects = models.GeoManager() + + class Meta: + ordering = ('status', 'name') + verbose_name = _(u"Polygon") + + @property + def geom_attr(self): + return 'polygon' + + def getGeoJSON(self, color="#000", inner_color='#0F0'): + '''Return a GeoJSON string + ''' + if '#' not in color: + color = '#' + color + if '#' not in inner_color: + color = '#' + inner_color + attributes = {"type": "Feature", + "geometry": json.loads(self.route.geojson), + "properties": {"pk": self.id, "name": self.name, + "color": color, + "inner_color": inner_color}} + return json.dumps(attributes) + + class SimplePoint: """ Point in the map (not in the database) @@ -1301,6 +1385,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) @@ -1336,7 +1421,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 "" @@ -1346,13 +1431,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, @@ -1364,10 +1449,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 + ''' @@ -1375,17 +1460,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) @@ -1396,6 +1482,7 @@ class Layer(models.Model): class Meta: verbose_name = _("Layer") + class Area(models.Model, SimpleArea): """Rectangular area of the map """ @@ -1406,25 +1493,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"), @@ -1446,13 +1537,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): """ @@ -1466,13 +1557,22 @@ class Area(models.Model, SimpleArea): """ return Q(route__contained=self.getWkt()) + def getIncludePolygon(self): + """ + Get the sql statement for the test if the route is included in the area + """ + return Q(polygon__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 @@ -1486,7 +1586,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) @@ -1502,7 +1602,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() @@ -1529,13 +1629,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 @@ -1552,12 +1653,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) @@ -1570,6 +1673,7 @@ class AreaLayers(models.Model): verbose_name = _("Layers") verbose_name_plural = _("Layers") + class PropertyModel(models.Model): '''Model for a property ''' @@ -1577,10 +1681,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')), @@ -1588,23 +1693,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): @@ -1612,6 +1719,7 @@ class PropertyModel(models.Model): ''' return 'property_%d_%d' % (self.order, self.id) + class PropertyModelChoice(models.Model): '''Choices for property model ''' @@ -1619,12 +1727,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 ''' @@ -1632,13 +1742,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) @@ -1650,7 +1761,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: @@ -1660,4 +1772,3 @@ class Property(models.Model): return None else: return self.value - |