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