summaryrefslogtreecommitdiff
path: root/chimere
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@peacefrogs.net>2012-05-16 12:56:44 +0200
committerÉtienne Loks <etienne.loks@peacefrogs.net>2012-05-16 12:56:44 +0200
commit9c9796a5bd5ced3ebbe4e2b143906d0bce9f0024 (patch)
tree079bd5fa43869e5794e66f4893c600a9f8e71c37 /chimere
parent8bfbb35bb37eb822f4967fc818d5592f40499fe6 (diff)
downloadChimère-9c9796a5bd5ced3ebbe4e2b143906d0bce9f0024.tar.bz2
Chimère-9c9796a5bd5ced3ebbe4e2b143906d0bce9f0024.zip
Fix RSS feeds for django 1.4 - reintegrate them in the main application
Diffstat (limited to 'chimere')
-rw-r--r--chimere/actions.py4
-rw-r--r--chimere/admin.py5
-rw-r--r--chimere/feeds.py222
-rw-r--r--chimere/migrations/0024_auto__add_field_marker_available_date.py211
-rw-r--r--chimere/models.py7
-rw-r--r--chimere/static/chimere/js/edit_area.js2
-rw-r--r--chimere/templates/chimere/contactus.html1
-rw-r--r--chimere/templates/chimere/main_map.html2
-rw-r--r--chimere/tests.py105
-rw-r--r--chimere/urls.py16
-rw-r--r--chimere/views.py126
-rw-r--r--chimere/widgets.py4
12 files changed, 659 insertions, 46 deletions
diff --git a/chimere/actions.py b/chimere/actions.py
index 4fe9bff..0363d54 100644
--- a/chimere/actions.py
+++ b/chimere/actions.py
@@ -34,8 +34,8 @@ actions = [(Action('view', '', _('View')), []),
Action('edit-route', 'edit-route/', _('Add a new route'))),
),]
-if 'chimere_rss' in settings.INSTALLED_APPS:
- actions.append((Action('rss', 'rss', _('RSS feeds')), []))
+if settings.CHIMERE_FEEDS:
+ actions.append((Action('rss', 'feeds', _('RSS feeds')), []))
if settings.EMAIL_HOST:
actions.append((Action('contact', 'contact', _('Contact us')), []),)
diff --git a/chimere/admin.py b/chimere/admin.py
index bfa90c5..060306f 100644
--- a/chimere/admin.py
+++ b/chimere/admin.py
@@ -98,10 +98,9 @@ class MarkerAdmin(admin.ModelAdmin):
list_display = ('name', 'status')
list_filter = ('status', 'categories')
actions = [validate, export_to_kml, export_to_shapefile]
- exclude = ['submiter_session_key', 'import_key', 'import_version']
+ exclude = ['submiter_session_key', 'import_key', 'import_version',
+ 'available_date']
readonly_fields = ['submiter_email', 'submiter_comment', 'import_source']
- if 'chimere_rss' in settings.INSTALLED_APPS:
- exclude.append('available_date')
form = MarkerAdminForm
inlines = [MultimediaInline, PictureInline]
diff --git a/chimere/feeds.py b/chimere/feeds.py
new file mode 100644
index 0000000..4fcef1e
--- /dev/null
+++ b/chimere/feeds.py
@@ -0,0 +1,222 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2010 Pierre Clarenc <pierre.crc_AT_gmailDOTcom>,
+# Samuel Renard <renard.samuel_AT_gmailDOTcom>,
+# Étienne Loks <etienne.loks_AT_peacefrogsDOTnet>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# See the file COPYING for details.
+
+from django.conf import settings
+from django.contrib.gis.geos import *
+from django.contrib.syndication.views import Feed, FeedDoesNotExist
+from django.core.exceptions import ObjectDoesNotExist
+from django.utils.translation import ugettext as _
+
+from django.shortcuts import get_object_or_404
+
+from chimere.models import Category, SubCategory, Marker, Area
+
+class BaseFeed(Feed):
+ """
+ Base feed for Chimere objects
+ """
+ def item_link(self, item):
+ ''' Return POI permalink '''
+ coord = item.point
+ cat = 0
+ if item.categories.all() and item.categories.all()[0]:
+ cat = item.categories.all()[0].pk
+ return settings.BASE_URL + '?zoom=16&lat=%d&lon=%d&current_feature=%d'\
+ '&checked_categories=%d' % (coord.y, coord.x, item.id, cat)
+
+ def item_pubdate(self, item):
+ """
+ Date of the Marker when it has been available
+ """
+ return item.available_date
+
+ def description(self, obj):
+ return ""
+
+class LatestPOIsByCategory(BaseFeed):
+ '''
+ Last Points of interests by category in Feeds
+ '''
+ title_template = "rss_title.html"
+ description_template = "rss_descr.html"
+
+ def get_object(self, request, category_id):
+ return get_object_or_404(Category, id=category_id)
+
+ def title(self, obj):
+ """
+ Define the title of the feed
+ """
+ return u"%s - %s" % (settings.PROJECT_NAME, obj.name)
+
+ def link(self, obj):
+ """
+ Define the link of the feed.
+ """
+ if not obj:
+ raise FeedDoesNotExist
+ return settings.BASE_URL + 'rss/category/' + str(obj.id)
+
+ def items(self, obj):
+ """
+ Requests to marker where its category match the category is requested
+ and its status is available
+ This returns a list of the 15 last markers/POIs ordering by date
+ """
+ q = Marker.objects.filter(status__exact='A',
+ categories__category__id__exact=obj.id,
+ available_date__isnull=False).order_by('-available_date')[:15]
+ return q
+
+class LatestPOIsBySubCategory(BaseFeed):
+ '''
+ Last Points of interests by SubCategory in Feeds
+ '''
+ title_template = "rss_title.html"
+ description_template = "rss_descr.html"
+
+ def get_object(self, request, category_id):
+ return get_object_or_404(SubCategory, id=category_id)
+
+ def title(self, obj):
+ return u"%s - %s - %s" % (settings.PROJECT_NAME, obj.category.name,
+ obj.name)
+
+ def link(self, obj):
+ if not obj:
+ raise FeedDoesNotExist
+ return settings.BASE_URL + 'rss/subcategory/' + str(obj.id)
+
+ def items(self, obj):
+ q = Marker.objects.filter(categories__id__exact=obj.id,
+ available_date__isnull=False, status__exact='A').order_by(
+ '-available_date')[:15]
+ return q
+
+class LatestPOIs(BaseFeed):
+ '''
+ Last Points of interests
+ '''
+ title_template = "rss_title.html"
+ description_template = "rss_descr.html"
+
+ def title(self):
+ return settings.PROJECT_NAME + u" - " + _(u"Last points of interest")
+
+ def link(self):
+ return settings.BASE_URL + 'rss/categories/'
+
+ def description(self):
+ return _("Latest points of interest from ") + settings.PROJECT_NAME
+
+ def items(self):
+ q = Marker.objects.filter(status__exact='A',
+ available_date__isnull=False).order_by('-available_date')[:15]
+ return q
+
+class LatestPOIsByZone(BaseFeed):
+ '''
+ Last Points of interests by zone by coordinates
+ '''
+ title_template = "rss_title.html"
+ description_template = "rss_descr.html"
+ upper_left_lat = 0
+ upper_left_lon = 0
+ lower_right_lat = 0
+ lower_right_lon = 0
+
+ def get_object(self, request, area):
+ """
+ Get the extra url. Parameters are the coordinates of the zone (the
+ upper left and lower right points)
+ """
+ if not area:
+ raise ObjectDoesNotExist
+ # Then define the upper right and lower left points
+ coordinates = str(area).split('_')
+ upper_left_lat = float(coordinates[0])
+ upper_left_lon = float(coordinates[1])
+ lower_right_lat = float(coordinates[2])
+ lower_right_lon = float(coordinates[3])
+ upper_right_lat = upper_left_lat
+ upper_right_lon = lower_right_lon
+ lower_left_lat = lower_right_lat
+ lower_left_lon = upper_left_lon
+ # Define a Polygon with the 4 points of the zone.
+ areaBox = Polygon(((upper_left_lon, upper_left_lat),
+ (upper_right_lon, upper_right_lat),
+ (lower_right_lon, lower_right_lat),
+ (lower_left_lon, lower_left_lat),
+ (upper_left_lon, upper_left_lat)),
+ srid=settings.EPSG_DISPLAY_PROJECTION)
+ return areaBox
+
+ def title(self, obj):
+ return settings.PROJECT_NAME + u" - " +\
+ _(u"Last points of interest by area")
+
+ def link(self, obj):
+ """
+ Define the link of the feed. It's the same url as we get in the method
+ get_object
+ """
+ if not obj:
+ raise FeedDoesNotExist
+ return settings.BASE_URL + 'rss/area/' \
+ + str(self.upper_left_lat) + '_' + str(self.upper_left_lon) + \
+ '_' + str(self.lower_right_lat) + '_' + str(self.lower_right_lon)
+
+ def items(self, obj):
+ """
+ Request to return Markers WHERE there points are containes in the zone
+ which is requested.
+ This returns a list of the 15 last markers/POIs ordering by date
+ """
+ q = Marker.objects.filter(point__contained=obj, status__exact='A',
+ available_date__isnull=False).order_by('-available_date')[:15]
+ return q
+
+class LatestPOIsByZoneID(BaseFeed):
+ '''
+ Last Points of interests by zone by id
+ '''
+ title_template = "rss_title.html"
+ description_template = "rss_descr.html"
+
+ def get_object(self, request, area_id):
+ return get_object_or_404(Area, id=area_id)
+
+ def title(self, obj):
+ return settings.PROJECT_NAME + u" - " + \
+ _(u"Last points of interest") + u" - " + obj.name
+
+ def link(self, obj):
+ if not obj:
+ raise FeedDoesNotExist
+ return settings.BASE_URL + 'rss/areaid/' + str(obj.id)
+
+ def items(self, obj):
+ sql = 'select * from "chimere_marker" where ' + obj.getIncludeSql()
+ sql += ' and "chimere_marker".available_date is not null'
+ sql += ' and "chimere_marker".status=\'A\''
+ sql += ' order by "chimere_marker".available_date desc limit 15'
+ q = Marker.objects.raw(sql)
+ return q
diff --git a/chimere/migrations/0024_auto__add_field_marker_available_date.py b/chimere/migrations/0024_auto__add_field_marker_available_date.py
new file mode 100644
index 0000000..78b5a68
--- /dev/null
+++ b/chimere/migrations/0024_auto__add_field_marker_available_date.py
@@ -0,0 +1,211 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Marker.available_date'
+ db.add_column('chimere_marker', 'available_date',
+ self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True),
+ keep_default=False)
+
+ def backwards(self, orm):
+ # Deleting field 'Marker.available_date'
+ db.delete_column('chimere_marker', 'available_date')
+
+ models = {
+ 'chimere.area': {
+ 'Meta': {'ordering': "('order', 'name')", 'object_name': 'Area'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'default': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'default_subcategories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'external_css': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layers': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'areas'", 'blank': 'True', 'through': "orm['chimere.AreaLayers']", 'to': "orm['chimere.Layer']"}),
+ 'lower_right_corner': ('django.contrib.gis.db.models.fields.PointField', [], {'default': "'POINT(0 0)'"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {}),
+ 'restrict_to_extent': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'subcategories': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'areas'", 'blank': 'True', 'db_table': "'chimere_subcategory_areas'", 'to': "orm['chimere.SubCategory']"}),
+ 'upper_left_corner': ('django.contrib.gis.db.models.fields.PointField', [], {'default': "'POINT(0 0)'"}),
+ 'urn': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'blank': 'True'}),
+ 'welcome_message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.arealayers': {
+ 'Meta': {'ordering': "('order',)", 'object_name': 'AreaLayers'},
+ 'area': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Area']"}),
+ 'default': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Layer']"}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.category': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'Category'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.color': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'Color'},
+ 'code': ('django.db.models.fields.CharField', [], {'max_length': '6'}),
+ 'color_theme': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.ColorTheme']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.colortheme': {
+ 'Meta': {'object_name': 'ColorTheme'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.icon': {
+ 'Meta': {'object_name': 'Icon'},
+ 'height': ('django.db.models.fields.IntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.importer': {
+ 'Meta': {'object_name': 'Importer'},
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'filtr': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'importer_type': ('django.db.models.fields.CharField', [], {'max_length': '4'}),
+ 'source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'srid': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'state': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'zipped': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.layer': {
+ 'Meta': {'object_name': 'Layer'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_code': ('django.db.models.fields.TextField', [], {'max_length': '300'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.marker': {
+ 'Meta': {'ordering': "('status', 'name')", 'object_name': 'Marker'},
+ 'available_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'end_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'import_key': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'point': ('chimere.widgets.PointField', [], {}),
+ 'ref_item': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'submited_marker'", 'null': 'True', 'to': "orm['chimere.Marker']"}),
+ 'route': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'associated_marker'", 'null': 'True', 'to': "orm['chimere.Route']"}),
+ 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'submiter_comment': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'submiter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'submiter_session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.multimediafile': {
+ 'Meta': {'object_name': 'MultimediaFile'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'multimedia_files'", 'to': "orm['chimere.Marker']"}),
+ 'miniature': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'multimedia_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.MultimediaType']"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
+ },
+ 'chimere.multimediatype': {
+ 'Meta': {'object_name': 'MultimediaType'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'iframe': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'media_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.news': {
+ 'Meta': {'object_name': 'News'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'content': ('django.db.models.fields.TextField', [], {}),
+ 'date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.picturefile': {
+ 'Meta': {'object_name': 'PictureFile'},
+ 'height': ('django.db.models.fields.IntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pictures'", 'to': "orm['chimere.Marker']"}),
+ 'miniature': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.property': {
+ 'Meta': {'object_name': 'Property'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Marker']"}),
+ 'propertymodel': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.PropertyModel']"}),
+ 'value': ('django.db.models.fields.TextField', [], {})
+ },
+ 'chimere.propertymodel': {
+ 'Meta': {'ordering': "('order',)", 'object_name': 'PropertyModel'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {}),
+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '1'})
+ },
+ 'chimere.route': {
+ 'Meta': {'ordering': "('status', 'name')", 'object_name': 'Route'},
+ 'associated_file': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.RouteFile']", 'null': 'True', 'blank': 'True'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'end_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'import_key': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'ref_item': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'submited_route'", 'null': 'True', 'to': "orm['chimere.Route']"}),
+ 'route': ('chimere.widgets.RouteField', [], {}),
+ 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'submiter_comment': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'submiter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'submiter_session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.routefile': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'RouteFile'},
+ 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'raw_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'simplified_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.subcategory': {
+ 'Meta': {'ordering': "['category', 'order']", 'object_name': 'SubCategory'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Category']"}),
+ 'color_theme': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.ColorTheme']", 'null': 'True', 'blank': 'True'}),
+ 'icon': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Icon']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'item_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.tinyurl': {
+ 'Meta': {'object_name': 'TinyUrl'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parameters': ('django.db.models.fields.CharField', [], {'max_length': '500'})
+ }
+ }
+
+ complete_apps = ['chimere'] \ No newline at end of file
diff --git a/chimere/models.py b/chimere/models.py
index c286422..8b15f29 100644
--- a/chimere/models.py
+++ b/chimere/models.py
@@ -289,9 +289,8 @@ class Marker(GeographicItem):
verbose_name=_(u"Reference marker"), related_name='submited_marker')
point = PointField(_(u"Localisation"),
srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION)
- if 'chimere_rss' in settings.INSTALLED_APPS:
- available_date = models.DateTimeField(_(u"Available Date"), blank=True,
- null=True)
+ available_date = models.DateTimeField(_(u"Available Date"), blank=True,
+ 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)
@@ -833,7 +832,7 @@ class Area(models.Model, SimpleArea):
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.EPSG_DISPLAY_PROJECTION
+ settings.CHIMERE_EPSG_DISPLAY_PROJECTION
)
sql = "ST_Contains(" + area + ", " + geometry + ")"
return sql
diff --git a/chimere/static/chimere/js/edit_area.js b/chimere/static/chimere/js/edit_area.js
index 7695e6b..f22d6e3 100644
--- a/chimere/static/chimere/js/edit_area.js
+++ b/chimere/static/chimere/js/edit_area.js
@@ -1,4 +1,4 @@
-/* Copyright (C) 2008 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet>
+/* Copyright (C) 2008-2012 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as
diff --git a/chimere/templates/chimere/contactus.html b/chimere/templates/chimere/contactus.html
index 4a44d77..cfb0d38 100644
--- a/chimere/templates/chimere/contactus.html
+++ b/chimere/templates/chimere/contactus.html
@@ -1,6 +1,5 @@
{% extends "chimere/base.html" %}
{% load i18n chimere_tags %}
-{% load i18n %}
{% block extra_head %}
{{ block.super }}
{{ form.media }}
diff --git a/chimere/templates/chimere/main_map.html b/chimere/templates/chimere/main_map.html
index 3d1e016..02ffc5c 100644
--- a/chimere/templates/chimere/main_map.html
+++ b/chimere/templates/chimere/main_map.html
@@ -2,8 +2,8 @@
{% load i18n unlocalize_point chimere_tags %}
{% block extra_head %}
{{ block.super }}
-{% head_chimere %}
{% head_jquery %}
+{% head_chimere %}
{% head_jme %}
<script src="{{ STATIC_URL }}chimere/js/jquery.chimere.js"></script>
{% endblock %}
diff --git a/chimere/tests.py b/chimere/tests.py
index 005cfcb..8ff3d25 100644
--- a/chimere/tests.py
+++ b/chimere/tests.py
@@ -1,42 +1,73 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
+import datetime
+import lxml.etree
import os
test_path = os.path.abspath(__file__)
test_dir_path = os.path.dirname(test_path) + os.sep
from django.conf import settings
+from django.core.urlresolvers import reverse
from django.test import TestCase
-from chimere.models import Icon, Importer, Category, SubCategory, Marker
+from chimere.models import Area, Icon, Importer, Category, SubCategory, Marker
+
+def area_setup():
+ area_1 = Area.objects.create(name='area 1', order=1, available=True,
+ upper_left_corner='SRID=4326;POINT(-4.907753 48.507656)',
+ lower_right_corner='SRID=4326;POINT(-4.049447 48.279688)')
+ return area_1
+
+def subcategory_setup():
+ category = Category.objects.create(name='Main category',
+ available=True,
+ order=1,
+ description='')
+
+ icon = Icon.objects.create(name='Default icon',
+ image='icons/marker.png',
+ height=25,
+ width=21)
+
+ subcategory_1 = SubCategory.objects.create(category=category,
+ name='Subcategory 1',
+ available=True,
+ icon=icon,
+ order=1,
+ item_type='M',)
+
+ subcategory_2 = SubCategory.objects.create(category=category,
+ name='Subcategory 2',
+ available=True,
+ icon=icon,
+ order=1,
+ item_type='M',)
+ return [subcategory_1, subcategory_2]
+
+def marker_setup(sub_categories=[]):
+ if not sub_categories:
+ sub_categories = subcategory_setup()
+ current_date = datetime.datetime.now()
+ markers = []
+ marker_1 = Marker.objects.create(name="Marker 1", status='A',
+ point='SRID=4326;POINT(-4.5 48.4)',
+ available_date=current_date)
+ marker_1.categories.add(sub_categories[0])
+ markers.append(marker_1)
+ marker_2 = Marker.objects.create(name="Marker 2", status='A',
+ point='SRID=4326;POINT(-3.5 48.4)',
+ available_date=current_date)
+ marker_2.categories.add(sub_categories[1])
+ markers.append(marker_2)
+ marker_3 = Marker.objects.create(name="Marker 3", status='A',
+ point='SRID=4326;POINT(-4.5 48.45)',
+ available_date=current_date - datetime.timedelta(days=60))
+ marker_3.categories.add(sub_categories[1])
+ markers.append(marker_3)
+ return markers
class ImporterTest:
- def _baseSetUp(self):
- category = Category.objects.create(name='Main category',
- available=True,
- order=1,
- description='')
-
- icon = Icon.objects.create(name='Default icon',
- image='icons/marker.png',
- height=25,
- width=21)
-
- subcategory_1 = SubCategory.objects.create(category=category,
- name='Subcategory 1',
- available=True,
- icon=icon,
- order=1,
- item_type='M',)
-
- subcategory_2 = SubCategory.objects.create(category=category,
- name='Subcategory 2',
- available=True,
- icon=icon,
- order=1,
- item_type='M',)
- return [subcategory_1, subcategory_2]
-
def test_get(self):
nb_by_cat = {}
for importer, awaited_nb in self.marker_importers:
@@ -63,7 +94,7 @@ class ImporterTest:
class KMLImporterTest(TestCase, ImporterTest):
def setUp(self):
- subcategory_1, subcategory_2 = self._baseSetUp()
+ subcategory_1, subcategory_2 = subcategory_setup()
importer1 = Importer.objects.create(importer_type='KML',
source=test_dir_path+'tests/sample.kml',
filtr="Category 1")
@@ -89,7 +120,7 @@ class KMLImporterTest(TestCase, ImporterTest):
class ShapefileImporterTest(TestCase, ImporterTest):
def setUp(self):
- subcategory_1, subcategory_2 = self._baseSetUp()
+ subcategory_1, subcategory_2 = subcategory_setup()
importer = Importer.objects.create(importer_type='SHP',
source=test_dir_path+'tests/sample.shp.zip', zipped=True)
importer.categories.add(subcategory_1)
@@ -98,7 +129,8 @@ class ShapefileImporterTest(TestCase, ImporterTest):
class OSMImporterTest(TestCase, ImporterTest):
def setUp(self):
- subcategory_1, subcategory_2 = self._baseSetUp()
+ subcategory_1, subcategory_2 = subcategory_setup()
+ markers = marker_setup()
importer1 = Importer.objects.create(importer_type='OSM',
source='OSM',
filtr="node[amenity=pub]"\
@@ -107,3 +139,16 @@ class OSMImporterTest(TestCase, ImporterTest):
self.marker_importers = [(importer1, None)]
+class FeedsTest(TestCase):
+ def setUp(self):
+ self.area = area_setup()
+ self.markers = marker_setup()
+
+ def test_rss(self):
+ # global
+ url = reverse('chimere:feeds-global')
+ response = self.client.get(url)
+ self.assertEqual(200, response.status_code)
+ doc = lxml.etree.fromstring(response.content)
+ self.assertEqual(int(doc.xpath('count(//item)')), len(self.markers))
+
diff --git a/chimere/urls.py b/chimere/urls.py
index d7c4463..197eb09 100644
--- a/chimere/urls.py
+++ b/chimere/urls.py
@@ -23,6 +23,8 @@ from django.contrib import admin
admin.autodiscover()
from chimere.models import Area
+from chimere.feeds import LatestPOIsByCategory, LatestPOIsBySubCategory, \
+ LatestPOIs, LatestPOIsByZone, LatestPOIsByZoneID
def i18n_javascript(request):
return admin.site.i18n_javascript(request)
@@ -33,6 +35,20 @@ urlpatterns = patterns('chimere.views',
name="simple_index")
)
+if settings.CHIMERE_FEEDS:
+ urlpatterns = patterns('',
+ url(r'^feeds$', 'chimere.views.rss', name='feeds-form'),
+ url(r'^feeds/category/(?P<category_id>\d+)$', LatestPOIsByCategory(),
+ name='feeds-cat'),
+ url(r'^feeds/subcategory/(?P<category_id>\d+)$',
+ LatestPOIsBySubCategory(), name='feeds-subcat'),
+ url(r'^feeds/global/$', LatestPOIs(), name='feeds-global'),
+ url(r'^feeds/area/(?P<area>[0-9-_.]+)$', LatestPOIs(),
+ name='feeds-area'),
+ url(r'^feeds/areaid/(?P<area_id>\d+)$', LatestPOIsByZoneID(),
+ name='feeds-areaid'),
+ )
+
urlpatterns += patterns('chimere.views',
url(r'^charte/?$', 'charte', name="charte"),
url(r'^(?P<area_name>\w+)?/?contact/?$', 'contactus', name="contact"),
diff --git a/chimere/views.py b/chimere/views.py
index 371731d..891078e 100644
--- a/chimere/views.py
+++ b/chimere/views.py
@@ -1,6 +1,9 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2008-2012 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet>
+#
+# RSS : Copyright (C) 2010 Pierre Clarenc <pierre.crc_AT_gmailDOTcom>,
+# Samuel Renard <renard.samuel_AT_gmailDOTcom>,
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
@@ -41,10 +44,10 @@ from chimere.models import Category, SubCategory, PropertyModel, \
Marker, Route, News, SimpleArea, Area, Color, TinyUrl, RouteFile
from chimere.widgets import getMapJS, PointChooserWidget, \
- RouteChooserWidget
+ RouteChooserWidget, AreaWidget
from chimere.forms import MarkerForm, RouteForm, ContactForm, FileForm, \
FullFileForm, MultimediaFileFormSet, PictureFileFormSet, notifySubmission,\
- notifyStaff
+ notifyStaff, AreaForm
#TODO: convert to requestcontext
def get_base_response(area_name=""):
@@ -592,3 +595,122 @@ def redirectFromTinyURN(request, area_name='', tiny_urn=''):
if redir:
return redir
return HttpResponseRedirect(response_dct['extra_url'] + parameters)
+
+def rss(request, area_name=''):
+ '''
+ Redirect to RSS subscription page
+ '''
+ response_dct, redir = get_base_response(area_name)
+ if redir:
+ return redir
+ response_dct.update({'actions':actions, 'action_selected':('rss',),
+ 'category_rss_feed':'',})
+ # If the form has been submited
+ if request.method == "POST":
+ # User has defined the kind of POI he is interested in : POI in a area
+ # (GET method is used for the link with RSS icon in the browser)
+ if 'rss_category' in request.POST:
+ #User wants to follow all the new POI
+ if request.POST['rss_category'] == 'global':
+ feeds_link = reverse('chimere:feeds-global')
+ return redirect(feeds_link)
+ # User wants to follow all the new POI by category or subcategory
+ elif request.POST['rss_category'] == 'poi':
+ response_dct['category_rss_feed'] = 'category'
+ response_dct['sub_categories'] = SubCategory.getAvailable()
+ return render_to_response('chimere/feeds/rss.html',
+ response_dct,
+ context_instance=RequestContext(request))
+ # User wants to follow all the new POI situated in a defined area
+ elif request.POST['rss_category'] == 'area':
+ # An unbound form
+ form = AreaForm()
+ area_widget = AreaWidget().render('area', None)
+ response_dct.update({
+ 'map_layer':settings.CHIMERE_DEFAULT_MAP_LAYER,
+ 'extra_head':form.media,
+ 'form':form,
+ 'category_rss_feed':'area',
+ 'area_id':Area.getAvailable(),
+ 'area_widget':area_widget
+ })
+ return render_to_response('chimere/feeds/rss.html',
+ response_dct,
+ context_instance=RequestContext(request))
+ # Error when submitting the form
+ else:
+ error = _("Incorrect choice in the list")
+ response_dct.update({'error_message':error,
+ 'category_rss_feed':'',
+ 'sub_categories':SubCategory.getAvailable()})
+ return render_to_response('chimere/feeds/rss.html',
+ response_dct,
+ context_instance=RequestContext(request))
+
+ # User has specified the category or subcategory he wants to follow =>
+ # we redirect him towards the related rss feed
+ if 'subcategory' in request.POST and request.POST['subcategory'] != '':
+ cat_id = request.POST['subcategory']
+ if cat_id.find("cat_") != -1 :
+ cat_id = cat_id.split('_')[1]
+ feeds_link = reverse('chimere:feeds-cat',
+ kwargs={'category_id':cat_id})
+ return redirect(feeds_link)
+
+ else:
+ feeds_link = reverse('chimere:feeds-subcat',
+ kwargs={'category_id':cat_id})
+ return redirect(feeds_link)
+
+ # User has specified the ID of the area he wants to follow
+ if 'id_area' in request.POST and request.POST['id_area'] != '':
+ feeds_link = reverse('chimere:feeds-areaid',
+ kwargs={'area_id':request.POST['id_area']})
+ return redirect(feeds_link)
+
+ # User has specified the area he wants to follow => we redirect him
+ # towards the related rss feed (using upper left and lower right
+ # coordinates)
+ elif 'upper_left_lat' in request.POST and \
+ request.POST['upper_left_lat'] != '' and \
+ 'upper_left_lon' in request.POST and \
+ request.POST['upper_left_lon'] != '' and \
+ 'lower_right_lon' in request.POST and \
+ request.POST['lower_right_lon'] != '' and \
+ 'lower_right_lat' in request.POST and \
+ request.POST['lower_right_lat'] != '' :
+ coords = request.POST['upper_left_lat'] + '_' + \
+ request.POST['upper_left_lon'] + '_' + \
+ request.POST['lower_right_lat'] + '_' + \
+ request.POST['lower_right_lon']
+ feeds_link = reverse('chimere:feeds-area',
+ kwargs={'area':coords})
+ return redirect(feeds_link)
+
+ # GET method is used for linking with the RSS icon in the browser when user
+ # wants to choose a category to follow
+ elif request.method == "GET" and 'rss_category' in request.GET:
+ if request.GET['rss_category'] == 'global':
+ feeds_link = reverse('chimere:feeds-global')
+ return redirect(feeds_link)
+ if request.GET['rss_category'] == 'poi':
+ response_dct['category_rss_feed'] = 'category'
+ response_dct['sub_categories'] = SubCategory.getAvailable(['M','B'])
+ return render_to_response('chimere/feeds/rss.html', response_dct,
+ context_instance=RequestContext(request))
+ if request.GET['rss_category'] == 'area':
+ # An unbound form
+ form = AreaForm()
+ response_dct.update({'map_layer':settings.MAP_LAYER,
+ 'extra_head':form.media,
+ 'form':form,
+ 'category_rss_feed':'area',
+ 'area_id':Area.getAvailable(),
+ 'area_widget':AreaWidget().render('area', None)})
+ return render_to_response('chimere/feeds/rss.html', response_dct,
+ context_instance=RequestContext(request))
+
+ # User access to the RSS tab
+ else:
+ return render_to_response('chimere/feeds/rss.html', response_dct,
+ context_instance=RequestContext(request))
diff --git a/chimere/widgets.py b/chimere/widgets.py
index e9a391e..da4ff26 100644
--- a/chimere/widgets.py
+++ b/chimere/widgets.py
@@ -361,13 +361,13 @@ class AreaWidget(forms.TextInput):
u"<input type='hidden' name='lower_right_lon' id='lower_right_lon' "\
u"value='%f'/>\n" % (
upper_left_lat, upper_left_lon, lower_right_lat, lower_right_lon)
- tpl += u"<script type='text/javascript'><!--\ninit();"
+ tpl += u"<script type='text/javascript'>\ninit();"
if value:
tpl += u"var extent = new OpenLayers.Bounds(%f, %f, %f, %f);\n"\
u"extent.transform(epsg_display_projection, epsg_projection);\n"\
u"map.zoomToExtent(extent, true);""" % (upper_left_lon,
upper_left_lat, lower_right_lon, lower_right_lat)
- tpl += u"// --></script>\n<hr class='spacer'/>\n"
+ tpl += u"</script>\n<hr class='spacer'/>\n"
return mark_safe(tpl)
def value_from_datadict(self, data, files, name):