summaryrefslogtreecommitdiff
path: root/chimere/models.py
diff options
context:
space:
mode:
Diffstat (limited to 'chimere/models.py')
-rw-r--r--chimere/models.py319
1 files changed, 241 insertions, 78 deletions
diff --git a/chimere/models.py b/chimere/models.py
index 5727098..4c77211 100644
--- a/chimere/models.py
+++ b/chimere/models.py
@@ -106,7 +106,7 @@ class News(models.Model):
date = models.DateField(_(u"Date"))
content = models.TextField()
url = models.URLField(_(u"Url"), max_length=200, blank=True, null=True)
- areas = SelectMultipleField('Area', verbose_name=_(u"Associated areas"),
+ maps = SelectMultipleField('Map', verbose_name=_(u"Associated maps"),
blank=True, null=True)
def __unicode__(self):
ordering = ["-date"]
@@ -172,7 +172,8 @@ class Color(models.Model):
"""
code = models.CharField(_(u"Code"), max_length=6)
order = models.IntegerField(_(u"Order"))
- color_theme = models.ForeignKey(ColorTheme, verbose_name=_(u"Color theme"))
+ color_theme = models.ForeignKey(ColorTheme, verbose_name=_(u"Color theme"),
+ related_name='colors')
def __unicode__(self):
return self.code
class Meta:
@@ -183,6 +184,7 @@ class Category(models.Model):
"""Category of Point Of Interest (POI)
"""
name = models.CharField(_(u"Name"), max_length=150)
+ slug = models.SlugField()
available = models.BooleanField(_(u"Available"))
order = models.IntegerField(_(u"Order"))
description = models.TextField(blank=True, null=True)
@@ -211,9 +213,12 @@ class SubCategory(models.Model):
category = models.ForeignKey(Category, verbose_name=_(u"Category"),
related_name='subcategories')
name = models.CharField(_(u"Name"), max_length=150)
+ slug = models.SlugField()
available = models.BooleanField(_(u"Available"), default=True)
submission = models.BooleanField(_(u"Available for submission"),
default=True)
+ weighted = models.BooleanField(_(u"Has an associated quantity"),
+ default=False)
TYPE = (('M', _(u'Marker')),
('R', _(u'Route')),
('B', _(u'Both')),)
@@ -237,7 +242,7 @@ class SubCategory(models.Model):
verbose_name_plural = _(u"Sub-categories")
@classmethod
- def getAvailable(cls, item_types=None, area_name=None, public=False):
+ def getAvailable(cls, item_types=None, map_name=None, public=False):
'''Get list of tuples with first the category and second the associated
subcategories
'''
@@ -250,14 +255,14 @@ class SubCategory(models.Model):
if public:
subcategories = subcategories.filter(submission=True)
selected_cats = []
- if area_name:
- area = Area.objects.get(urn=area_name)
+ if map_name:
+ map = Map.objects.get(urn=map_name)
# if there some restrictions with categories limit them
- if area.subcategories.count():
- sub_ids = [sub.id for sub in area.subcategories.all()]
+ if map.subcategories.count():
+ sub_ids = [sub.id for sub in map.subcategories.all()]
subcategories = subcategories.filter(id__in=sub_ids)
selected_cats = [subcat.pk
- for subcat in area.default_subcategories.all()]
+ for subcat in map.default_subcategories.all()]
for sub_category in subcategories.order_by('order'):
if sub_category.category not in sub_categories:
sub_categories[sub_category.category] = []
@@ -273,10 +278,10 @@ class SubCategory(models.Model):
return subcategories
@classmethod
- def getAvailableTuples(cls, item_types=None, area_name=None):
+ def getAvailableTuples(cls, item_types=None, map_name=None):
cats = []
for cat, subcats in cls.getAvailable(item_types=item_types,
- area_name=area_name):
+ map_name=map_name):
cats.append((unicode(cat),
[(subcat.pk, subcat.name) for subcat in subcats]))
return cats
@@ -504,6 +509,8 @@ class Marker(GeographicItem):
null=True) # used by feeds
route = models.ForeignKey(u"Route", blank=True, null=True,
related_name='associated_marker')
+ weight = models.IntegerField(_(u"Quantity"), blank=True, null=True,
+ default=0)
description = models.TextField(_(u"Description"), blank=True, null=True)
is_front_page = models.NullBooleanField(_(u"Is front page"), blank=True,
null=True)
@@ -573,6 +580,10 @@ class Marker(GeographicItem):
def geom_attr(self):
return 'point'
+ @property
+ def has_weight(self):
+ return bool(self.categories.filter(weighted=True).count())
+
class Meta:
ordering = ('status', 'name')
verbose_name = _(u"Point of interest")
@@ -654,6 +665,32 @@ class Marker(GeographicItem):
val = values[unicode(propertymodel.id)]
self.setProperty(propertymodel, val)
+ PROPERTIES_KEYS = ['point', 'pk', 'name', 'weight']
+ @classmethod
+ def _getJson(cls, values, base_dct={"properties":{}}):
+ item = base_dct.copy()
+ item["geometry"] = {"type": "Point",
+ "coordinates": [values['point'].x,
+ values['point'].y]}
+ item["properties"]['pk'] = values['pk']
+ item["properties"]['name'] = values['name']
+ if values['weight']:
+ item["properties"]['weight'] = values['weight']
+ return item
+
+ def _getItems(self, base_dct={"properties":{}}):
+ '''Return a dict representation for json
+ '''
+ item = base_dct.copy()
+ item["geometry"] = {"type": "Point",
+ "coordinates": [ self.point.x, self.point.y ]
+ }
+ item["properties"]['pk'] = self.pk
+ item["properties"]['name'] = self.name
+ if self.weight:
+ item["properties"]['weight'] = self.weight
+ return item
+
def getGeoJSON(self, categories_id=[]):
'''Return a GeoJSON string
'''
@@ -668,38 +705,48 @@ class Marker(GeographicItem):
if cat.hover_icon else '',
'icon_width':cat.icon.image.width,
'icon_height':cat.icon.image.height,
- 'category_name':json.dumps(cat.name)}
+ 'category_name':json.dumps(cat.name),}
+ items['weight'] = ''
+ if cat.weighted:
+ if not self.weight:
+ continue
+ items['weight'] = u', "weight":%d' % self.weight
+ if cat.color_theme and cat.color_theme.colors.count():
+ items['weight'] += u', "colors":["#%s"]' % '", "#'.join(
+ [color.code for color in cat.color_theme.colors.\
+ order_by('order').all()])
jsons.append(u'{"type":"Feature", "geometry":%(geometry)s, '\
u'"properties":{"pk": %(id)d, "name": %(name)s, '\
u'"icon_path":"%(icon_path)s", '\
u'"icon_hover_path":"%(icon_hover_path)s", '\
u'"icon_width":%(icon_width)d, '\
u'"icon_height":%(icon_height)d, '\
- u'"category_name":%(category_name)s}}' % items)
+ u'"category_name":%(category_name)s'\
+ u'%(weight)s}}' % items)
return ",".join(jsons)
@property
def default_category(self):
# Should we select only available ones ?
# Should we catch if not exists ?
- cats = self.categories
+ cats = self.categories.filter(available=True, category__available=True)
if cats.count():
return cats.all()[0]
- def get_absolute_url(self, area_name=''):
+ def get_absolute_url(self, map_name=''):
parameters = 'current_feature=%d' % self.id
if self.default_category:
parameters += '&checked_categories=%s' % self.default_category.pk
urn = TinyUrl.getUrnByParameters(parameters)
- area_name = area_name + '/' if area_name else ''
- url = reverse('chimere:tiny', args=[area_name, urn])
+ map_name = map_name + '/' if map_name else ''
+ url = reverse('chimere:tiny', args=[map_name, urn])
return url
PRE_ATTRS = {
'Marker':('name', 'geometry', 'import_version', 'modified_since_import'),
'Route':('name', 'geometry', 'import_version', 'modified_since_import'),
- 'Area':('urn', 'name'),
+ 'Map':('urn', 'name'),
}
def geometry_pre_save(cls, pre_save_geom_values):
def geom_pre_save(sender, **kwargs):
@@ -1137,6 +1184,13 @@ class Route(GeographicItem):
properties.append(property)
return properties
+ def _getItems(self, dct={'properties':{}}):
+ dct['geometry'] = { "type": "LineString",
+ "coordinates": [[point.x, point.y]
+ for point in self.route]}
+ dct['properties'].update({'id':self.id, 'name':self.name})
+ return dct
+
def getGeoJSON(self, color="#000"):
'''Return a GeoJSON string
'''
@@ -1278,14 +1332,14 @@ class SimpleArea:
return True
return False
- def getCategories(self, status='A', filter_available=True, area_name=None):
+ def getCategories(self, status='A', filter_available=True, map_name=None):
"""
- Get categories for this area
+ Get categories for this map
"""
wheres = []
- if area_name:
+ if map_name:
subcategory_pks = []
- for cat, subcats in SubCategory.getAvailable(area_name=area_name):
+ for cat, subcats in SubCategory.getAvailable(map_name=map_name):
for subcat in subcats:
subcategory_pks.append(unicode(subcat.pk))
if filter_available:
@@ -1349,37 +1403,43 @@ class Layer(models.Model):
class Meta:
verbose_name = _("Layer")
-class Area(models.Model, SimpleArea):
- """Rectangular area of the map
+class Map(models.Model, SimpleArea):
+ """A map
"""
name = models.CharField(_(u"Name"), max_length=150)
- urn = models.SlugField(_(u"Area urn"), max_length=50, blank=True,
+ available = models.BooleanField(_(u"Available"))
+ users = models.ManyToManyField(User, through='MapUsers')
+ urn = models.SlugField(_(u"Map urn"), max_length=50, blank=True,
unique=True)
welcome_message = models.TextField(_(u"Welcome message"), blank=True,
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"))
- layers = SelectMultipleField(Layer, related_name='areas',
- through='AreaLayers', blank=True)
+ default = models.BooleanField(_(u"Default map"), default=False,
+ help_text=_(u"Only one map is set by default"))
+ layers = SelectMultipleField(Layer, related_name='maps',
+ through='MapLayers', blank=True)
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',
+ subcategories = SelectMultipleField(SubCategory, related_name='maps',
+ blank=True, db_table='chimere_subcategory_maps',
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)
+ cluster = models.BooleanField(u"Clustering map (weight of items are added)",
+ default=False)
+ public_read = models.BooleanField(_(u"Public can read the map"))
+ public_propose = models.BooleanField(_(u"Public can propose item to the map"))
+ public_write = models.BooleanField(_(u"Public can write without moderation to the map"))
restrict_to_extent = models.BooleanField(_(u"Restrict to the area extent"),
default=False)
objects = models.GeoManager()
@@ -1389,13 +1449,72 @@ class Area(models.Model, SimpleArea):
class Meta:
ordering = ('order', 'name')
- verbose_name = _("Area")
+ verbose_name = _("Map")
+
+ def can_write(self, user=None):
+ return bool(self.getAvailable(user=user, urn=self.urn, single=True,
+ edit=True))
+
+ def can_propose(self, user=None):
+ return bool(self.getAvailable(user=user, urn=self.urn, single=True,
+ propose=True))
@classmethod
- def getAvailable(cls):
- '''Get available areas
+ def getAvailable(cls, user=None, urn=None, single=False, edit=False,
+ propose=False):
+ '''Get available maps
'''
- return cls.objects.filter(available=True)
+ map_filter = {'available':True}
+ if urn:
+ map_filter['urn'] = urn
+ elif single:
+ map_filter['default'] = True
+ filters = []
+ if not propose and not edit:
+ filters = [{'public_write':True},
+ {'public_propose':True},
+ {'public_read':True}]
+ elif propose:
+ filters = [{'public_write':True},
+ {'public_propose':True}]
+ elif edit:
+ filters = [{'public_write':True}]
+ if user and user.is_authenticated():
+ if not propose and not edit:
+ filters += [
+ {'mapusers__user':user, 'mapusers__read':True},
+ {'mapusers__user':user, 'mapusers__write':True},
+ {'mapusers__user':user, 'mapusers__propose':True},
+ {'mapgroups__group__user':user, 'mapgroups__read':True},
+ {'mapgroups__group__user':user, 'mapgroups__write':True},
+ {'mapgroups__group__user':user, 'mapgroups__propose':True}
+ ]
+ elif propose:
+ filters += [
+ {'mapusers__user':user, 'mapusers__write':True},
+ {'mapusers__user':user, 'mapusers__propose':True},
+ {'mapgroups__group__user':user, 'mapgroups__write':True},
+ {'mapgroups__group__user':user, 'mapgroups__propose':True}
+ ]
+ elif edit:
+ filters += [
+ {'mapusers__user':user, 'mapusers__write':True},
+ {'mapgroups__group__user':user, 'mapgroups__write':True},
+ ]
+ query = None
+ for fltr in filters:
+ fltr.update(map_filter)
+ if not query:
+ query = Q(**fltr)
+ else:
+ query = query | Q(**fltr)
+ maps = cls.objects.filter(query).distinct()
+ if single:
+ if not maps.count():
+ return
+ return maps.all()[0]
+ else:
+ return maps.all()
def getWkt(self):
return "SRID=%d;POLYGON((%f %f,%f %f,%f %f,%f %f, %f %f))" % (
@@ -1419,54 +1538,66 @@ class Area(models.Model, SimpleArea):
"""
return Q(route__contained=self.getWkt())
-pre_save_area_values = {}
-def area_pre_save(sender, **kwargs):
+pre_save_map_values = {}
+def map_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)
+ geometry_pre_save(Map, pre_save_map_values)(sender, **kwargs)
+pre_save.connect(map_pre_save, sender=Map)
-def area_post_save(sender, **kwargs):
+def map_post_save(sender, **kwargs):
if not kwargs['instance']:
return
- area = kwargs['instance']
- if area.default:
- defaults = Area.objects.filter(default=True).exclude(pk=area.pk)
+ map = kwargs['instance']
+ if map.default:
+ defaults = Map.objects.filter(default=True).exclude(pk=map.pk)
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]
+ old_urn, old_name = map.urn, map.name
+ if map.pk in pre_save_map_values:
+ old_urn, old_name = pre_save_map_values[map.pk]
perm, old_groups, old_users = None, [], []
- if area.urn != old_urn:
- oldmnemo = 'change_area_' + old_urn
+
+ if map.urn != old_urn:
+ oldmnemo = 'change_map_' + 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
+ codename = 'change_map_' + map.urn
+ if not Permission.objects.filter(codename=codename).count():
+ perm.codename = codename
+ perm.save()
+ if not map.urn:
+ map.urn = defaultfilters.slugify(map.name)
+ map.save()
+
+ # fix old mnemo
+ oldmnemo = 'change_area_' + old_urn
+ old_perm = Permission.objects.filter(codename=oldmnemo)
+ if old_perm.count():
+ perm = old_perm.all()[0]
+ perm.codename = 'change_map_' + map.urn
+ perm.save()
+
+ mnemo = 'change_map_' + map.urn
perm = Permission.objects.filter(codename=mnemo)
- lbl = "Can change " + area.name
+ lbl = "Can change " + map.name
if not perm.count():
content_type, created = ContentType.objects.get_or_create(
- app_label="chimere", model="area")
+ app_label="chimere", model="map")
perm = Permission(name=lbl, content_type_id=content_type.id,
codename=mnemo)
perm.save()
else:
perm = perm.all()[0]
- if old_name != area.name:
+ if old_name != map.name:
perm.name = lbl
perm.save()
# manage moderation group
- groupname = area.name + " moderation"
- if old_name != area.name:
+ groupname = map.name + " moderation"
+ if old_name != map.name:
old_groupname = old_name + " moderation"
old_gp = Group.objects.filter(name=old_groupname)
if old_gp.count():
@@ -1487,36 +1618,56 @@ def area_post_save(sender, **kwargs):
for p in Permission.objects.filter(content_type=ct).all():
group.permissions.add(p)
-post_save.connect(area_post_save, sender=Area)
+post_save.connect(map_post_save, sender=Map)
-def get_areas_for_user(user):
+def get_maps_for_user(user):
"""
- Getting subcats for a specific user
+ Getting maps for a specific user
"""
perms = user.get_all_permissions()
- areas = set()
- prefix = 'chimere.change_area_'
+ maps = set()
+ prefix = 'chimere.change_map_'
for perm in perms:
if perm.startswith(prefix):
try:
- area = Area.objects.get(urn=perm[len(prefix):])
- areas.add(area)
+ map = Map.objects.get(urn=perm[len(prefix):])
+ maps.add(map)
except ObjectDoesNotExist:
pass
- return areas
+ return maps
-def get_users_by_area(area):
- if not area:
+def get_users_by_map(map):
+ if not map:
return []
- perm = 'change_area_'+area.urn
+ perm = 'change_map_'+map.urn
return User.objects.filter(Q(groups__permissions__codename=perm)|
Q(user_permissions__codename=perm)).all()
-class AreaLayers(models.Model):
- area = models.ForeignKey(Area)
+class MapUsers(models.Model):
+ map = models.ForeignKey(Map, related_name='mapusers')
+ user = models.ForeignKey(User, related_name='mapusers')
+ read = models.BooleanField(_(u"Can read the map"))
+ propose = models.BooleanField(_(u"Can propose item to the map"))
+ write = models.BooleanField(_(u"Can write without moderation to the map"))
+ class Meta:
+ verbose_name = _("Map - user")
+ verbose_name_plural = _("Map - users")
+
+class MapGroups(models.Model):
+ map = models.ForeignKey(Map, related_name='mapgroups')
+ group = models.ForeignKey(Group, related_name='mapgroups')
+ read = models.BooleanField(_(u"Can read the map"))
+ propose = models.BooleanField(_(u"Can propose item to the map"))
+ write = models.BooleanField(_(u"Can write without moderation to the map"))
+ class Meta:
+ verbose_name = _("Map - group")
+ verbose_name_plural = _("Map - groups")
+
+class MapLayers(models.Model):
+ map = models.ForeignKey(Map)
layer = models.ForeignKey(Layer)
order = models.IntegerField(_(u"Order"))
- default = models.NullBooleanField(_(u"Default layer"))
+ default = models.BooleanField(_(u"Default layer"), default=False)
class Meta:
ordering = ('order',)
@@ -1530,6 +1681,7 @@ class PropertyModel(models.Model):
order = models.IntegerField(_(u"Order"))
available = models.BooleanField(_(u"Available"))
mandatory = models.BooleanField(_(u"Mandatory"))
+ slug = models.SlugField()
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 "
@@ -1538,13 +1690,16 @@ class PropertyModel(models.Model):
('L', _('Long text')),
('P', _('Password')),
('D', _("Date")),
+ ('B', _("Boolean")),
('C', _("Choices")),
+ ('B', _("Boolean")),
)
TYPE_WIDGET = {'T':forms.TextInput,
'L':TextareaWidget,
'P':forms.PasswordInput,
'D':DatePickerWidget,
- 'C':forms.Select
+ 'C':forms.Select,
+ 'B':forms.CheckboxInput,
}
type = models.CharField(_(u"Type"), max_length=1, choices=TYPE)
def __unicode__(self):
@@ -1554,8 +1709,7 @@ class PropertyModel(models.Model):
verbose_name = _("Property model")
def getAttrName(self):
- attr_name = defaultfilters.slugify(self.name)
- attr_name = re.sub(r'-','_', attr_name)
+ attr_name = self.slug.replace('-', '_')
return attr_name
def getNamedId(self):
@@ -1563,6 +1717,11 @@ class PropertyModel(models.Model):
'''
return 'property_%d_%d' % (self.order, self.id)
+ def save(self, *args, **kwargs):
+ if not self.slug:
+ self.slug = defaultfilters.slugify(self.name)
+ super(PropertyModel, self).save(*args, **kwargs)
+
class PropertyModelChoice(models.Model):
'''Choices for property model
'''
@@ -1583,15 +1742,19 @@ 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 self.value and self.propertymodel.type == 'C':
try:
return unicode(PropertyModelChoice.objects.get(
pk=self.value).value)
- except self.DoesNotExist:
+ except (self.DoesNotExist, ValueError):
return ""
return unicode(self.value)
+ def label(self):
+ return unicode(self)
+
class Meta:
verbose_name = _(u"Property")