Ticket #6547: georss_syndication_patch_v3.diff
File georss_syndication_patch_v3.diff, 15.0 KB (added by , 16 years ago) |
---|
-
django/contrib/gis/feeds.py
1 from django.contrib.syndication.feeds import Feed as BaseFeed, FeedDoesNotExist 2 from django.utils.feedgenerator import Atom1Feed, Rss201rev2Feed 3 4 class GeoFeedMixin(object): 5 w3c_geo = False 6 7 def georss_coords(self, coords): 8 """ 9 In GeoRSS coordinate pairs are ordered by lat/lon and separated by 10 a single white space. Given a tuple of coordinates, this will return 11 a unicode GeoRSS representation. 12 """ 13 return u' '.join([u'%f %f' % (coord[1], coord[0]) for coord in coords]) 14 15 def add_georss_point(self, handler, coords): 16 """ 17 Adds a GeoRSS point with the given coords using the given handler. 18 Handles the differences between simple GeoRSS and the more pouplar 19 W3C Geo specification. 20 """ 21 if self.w3c_geo: 22 lon, lat = coords[:2] 23 handler.addQuickElement(u'geo:lat', u'%f' % lat) 24 handler.addQuickElement(u'geo:lon', u'%f' % lon) 25 else: 26 handler.addQuickElement(u'georss:point', self.georss_coords((coords,))) 27 28 def add_georss_element(self, handler, item): 29 """ 30 This routine adds a GeoRSS XML element using the given item and handler. 31 """ 32 # Getting the Geometry object. 33 geom = item.get('geometry', None) 34 if not geom is None: 35 if isinstance(geom, (list, tuple)): 36 # Special case if a tuple/list was passed in. The tuple may be 37 # a point or a box 38 box_coords = None 39 if isinstance(geom[0], (list, tuple)): 40 # Box: ( (X0, Y0), (X1, Y1) ) 41 if len(geom) == 2: 42 box_coords = geom 43 else: 44 raise ValueError('Only should be two sets of coordinates.') 45 else: 46 if len(geom) == 2: 47 # Point: (X, Y) 48 self.add_georss_point(handler, geom) 49 elif len(geom) == 4: 50 # Box: (X0, Y0, X1, Y1) 51 box_coords = (geom[:2], geom[2:]) 52 else: 53 raise ValueError('Only should be 2 or 4 numeric elements.') 54 # If a GeoRSS box was given via tuple. 55 if not box_coords is None: 56 if self.w3c_geo: raise ValueError('Cannot use simple GeoRSS box in W3C Geo feeds.') 57 handler.addQuickElement(u'georss:box', self.georss_coords(box_coords)) 58 else: 59 # Getting the lower-case geometry type. 60 gtype = str(geom.geom_type).lower() 61 62 if gtype == 'point': 63 self.add_georss_point(handler, geom.coords) 64 else: 65 if self.w3c_geo: raise TypeError('W3C Geo only supports Point geometries.') 66 # For formatting consistent w/the GeoRSS simple standard: 67 # http://georss.org/1.0#simple 68 if gtype in ('linestring', 'linearring'): 69 handler.addQuickElement(u'georss:line', self.georss_coords(geom.coords)) 70 elif gtype in ('polygon',): 71 # Only support the exterior ring. 72 handler.addQuickElement(u'georss:polygon', self.georss_coords(geom[0].coords)) 73 else: 74 raise TypeError('Geometry type "%s" not supported.' % geom.geom_type) 75 76 ### SyndicationFeed subclasses ### 77 class W3CGeoFeed(Rss201rev2Feed, GeoFeedMixin): 78 w3c_geo = True 79 80 def rss_attributes(self): 81 attrs = super(W3CGeoFeed, self).rss_attributes() 82 attrs[u'xmlns:geo'] = u'http://www.w3.org/2003/01/geo/wgs84_pos#' 83 return attrs 84 85 def add_item_elements(self, handler, item): 86 super(W3CGeoFeed, self).add_item_elements(handler, item) 87 self.add_georss_element(handler, item) 88 89 def add_root_elements(self, handler): 90 super(W3CGeoFeed, self).add_root_elements(handler) 91 self.add_georss_element(handler, self.feed) 92 93 class GeoRSSFeed(Rss201rev2Feed, GeoFeedMixin): 94 def rss_attributes(self): 95 attrs = super(GeoRSSFeed, self).rss_attributes() 96 attrs[u'xmlns:georss'] = u'http://www.georss.org/georss' 97 return attrs 98 99 def add_item_elements(self, handler, item): 100 super(GeoRSSFeed, self).add_item_elements(handler, item) 101 self.add_georss_element(handler, item) 102 103 def add_root_elements(self, handler): 104 super(GeoRSSFeed, self).add_root_elements(handler) 105 self.add_georss_element(handler, self.feed) 106 107 class GeoAtom1Feed(Atom1Feed, GeoFeedMixin): 108 def root_attributes(self): 109 attrs = super(GeoAtom1Feed, self).root_attributes() 110 attrs[u'xmlns:georss'] = u'http://www.georss.org/georss' 111 return attrs 112 113 def add_item_elements(self, handler, item): 114 super(GeoAtom1Feed, self).add_item_elements(handler, item) 115 self.add_georss_element(handler, item) 116 117 def add_root_elements(self, handler): 118 super(GeoAtom1Feed, self).add_root_elements(handler) 119 self.add_georss_element(handler, self.feed) 120 121 class Feed(BaseFeed): 122 """ 123 This is a subclass of the `Feed` from `django.contrib.syndication`. 124 This allows users to define a `geometry(obj)` and/or `item_geometry(item)` 125 methods on their own subclasses so that geo-referenced information may 126 placed in the feed. 127 """ 128 feed_type = GeoRSSFeed 129 130 def get_feed_kwargs(self, obj): 131 kwargs = super(Feed, self).get_feed_kwargs(obj) 132 kwargs['geometry'] = self.__get_dynamic_attr('geometry', obj) 133 return kwargs 134 135 def get_item_kwargs(self, item): 136 kwargs = super(Feed, self).get_item_kwargs(item) 137 kwargs['geometry'] = self.__get_dynamic_attr('item_geometry', item) 138 return kwargs -
django/contrib/syndication/feeds.py
62 62 def get_object(self, bits): 63 63 return None 64 64 65 def get_feed_kwargs(self, obj): 66 """ 67 Returns the keyword arguments to instatiate the `SyndicationFeed` 68 class specified by the `feed_type` attribute. 69 """ 70 return { 71 'title' : self.__get_dynamic_attr('title', obj), 72 'subtitle' : self.__get_dynamic_attr('subtitle', obj), 73 'link' : add_domain(self.current_site.domain, 74 self.__get_dynamic_attr('link', obj)), 75 'description' : self.__get_dynamic_attr('description', obj), 76 'language' : settings.LANGUAGE_CODE.decode(), 77 'feed_url' : add_domain(self.current_site.domain, 78 self.__get_dynamic_attr('feed_url', obj)), 79 'author_name' : self.__get_dynamic_attr('author_name', obj), 80 'author_link' : self.__get_dynamic_attr('author_link', obj), 81 'author_email' : self.__get_dynamic_attr('author_email', obj), 82 'categories' : self.__get_dynamic_attr('categories', obj), 83 'feed_copyright' : self.__get_dynamic_attr('feed_copyright', obj), 84 'feed_guid' : self.__get_dynamic_attr('feed_guid', obj), 85 'ttl' : self.__get_dynamic_attr('ttl', obj), 86 } 87 88 def get_item_kwargs(self, item): 89 """ 90 Returns the keyword arguments to pass to the `add_item` method of the 91 `SyndicationFeed` instance (specified by the `feed_type` attribute). 92 """ 93 link = add_domain(self.current_site.domain, self.__get_dynamic_attr('item_link', item)) 94 enc = None 95 enc_url = self.__get_dynamic_attr('item_enclosure_url', item) 96 if enc_url: 97 enc = feedgenerator.Enclosure( 98 url = smart_unicode(enc_url), 99 length = smart_unicode(self.__get_dynamic_attr('item_enclosure_length', item)), 100 mime_type = smart_unicode(self.__get_dynamic_attr('item_enclosure_mime_type', item)) 101 ) 102 author_name = self.__get_dynamic_attr('item_author_name', item) 103 if author_name is not None: 104 author_email = self.__get_dynamic_attr('item_author_email', item) 105 author_link = self.__get_dynamic_attr('item_author_link', item) 106 else: 107 author_email = author_link = None 108 109 pubdate = self.__get_dynamic_attr('item_pubdate', item) 110 if pubdate: 111 now = datetime.now() 112 utcnow = datetime.utcnow() 113 114 # Must always subtract smaller time from larger time here. 115 if utcnow > now: 116 sign = -1 117 tzDifference = (utcnow - now) 118 else: 119 sign = 1 120 tzDifference = (now - utcnow) 121 122 # Round the timezone offset to the nearest half hour. 123 tzOffsetMinutes = sign * ((tzDifference.seconds / 60 + 15) / 30) * 30 124 tzOffset = timedelta(minutes=tzOffsetMinutes) 125 pubdate = pubdate.replace(tzinfo=FixedOffset(tzOffset)) 126 127 return {'title' : self.title_tmp.render(RequestContext(self.request, {'obj': item, 'site': self.current_site})), 128 'link' : link, 129 'description' : self.description_tmp.render(RequestContext(self.request, {'obj': item, 'site': self.current_site})), 130 'unique_id' : self.__get_dynamic_attr('item_guid', item, link), 131 'enclosure' : enc, 132 'pubdate' : pubdate, 133 'author_name' : author_name, 134 'author_email' : author_email, 135 'author_link' : author_link, 136 'categories' : self.__get_dynamic_attr('item_categories', item), 137 'item_copyright' : self.__get_dynamic_attr('item_copyright', item), 138 } 139 65 140 def get_feed(self, url=None): 66 141 """ 67 142 Returns a feedgenerator.DefaultFeed object, fully populated, for … … 77 152 except ObjectDoesNotExist: 78 153 raise FeedDoesNotExist 79 154 155 # Getting the current site. 80 156 if Site._meta.installed: 81 current_site = Site.objects.get_current()157 self.current_site = Site.objects.get_current() 82 158 else: 83 current_site = RequestSite(self.request) 84 85 link = self.__get_dynamic_attr('link', obj) 86 link = add_domain(current_site.domain, link) 159 self.current_site = RequestSite(self.request) 87 160 88 feed = self.feed_type( 89 title = self.__get_dynamic_attr('title', obj), 90 subtitle = self.__get_dynamic_attr('subtitle', obj), 91 link = link, 92 description = self.__get_dynamic_attr('description', obj), 93 language = settings.LANGUAGE_CODE.decode(), 94 feed_url = add_domain(current_site.domain, 95 self.__get_dynamic_attr('feed_url', obj)), 96 author_name = self.__get_dynamic_attr('author_name', obj), 97 author_link = self.__get_dynamic_attr('author_link', obj), 98 author_email = self.__get_dynamic_attr('author_email', obj), 99 categories = self.__get_dynamic_attr('categories', obj), 100 feed_copyright = self.__get_dynamic_attr('feed_copyright', obj), 101 feed_guid = self.__get_dynamic_attr('feed_guid', obj), 102 ttl = self.__get_dynamic_attr('ttl', obj), 103 ) 161 # Initializing the feed. 162 feed = self.feed_type(**self.get_feed_kwargs(obj)) 104 163 164 # Getting instances of the title and description templates. 105 165 try: 106 title_tmp = loader.get_template(self.title_template_name)166 self.title_tmp = loader.get_template(self.title_template_name) 107 167 except TemplateDoesNotExist: 108 title_tmp = Template('{{ obj }}')168 self.title_tmp = Template('{{ obj }}') 109 169 try: 110 description_tmp = loader.get_template(self.description_template_name)170 self.description_tmp = loader.get_template(self.description_template_name) 111 171 except TemplateDoesNotExist: 112 description_tmp = Template('{{ obj }}')172 self.description_tmp = Template('{{ obj }}') 113 173 174 # Creating the entry for each item. 114 175 for item in self.__get_dynamic_attr('items', obj): 115 link = add_domain(current_site.domain, self.__get_dynamic_attr('item_link', item)) 116 enc = None 117 enc_url = self.__get_dynamic_attr('item_enclosure_url', item) 118 if enc_url: 119 enc = feedgenerator.Enclosure( 120 url = smart_unicode(enc_url), 121 length = smart_unicode(self.__get_dynamic_attr('item_enclosure_length', item)), 122 mime_type = smart_unicode(self.__get_dynamic_attr('item_enclosure_mime_type', item)) 123 ) 124 author_name = self.__get_dynamic_attr('item_author_name', item) 125 if author_name is not None: 126 author_email = self.__get_dynamic_attr('item_author_email', item) 127 author_link = self.__get_dynamic_attr('item_author_link', item) 128 else: 129 author_email = author_link = None 130 131 pubdate = self.__get_dynamic_attr('item_pubdate', item) 132 if pubdate: 133 now = datetime.now() 134 utcnow = datetime.utcnow() 135 136 # Must always subtract smaller time from larger time here. 137 if utcnow > now: 138 sign = -1 139 tzDifference = (utcnow - now) 140 else: 141 sign = 1 142 tzDifference = (now - utcnow) 143 144 # Round the timezone offset to the nearest half hour. 145 tzOffsetMinutes = sign * ((tzDifference.seconds / 60 + 15) / 30) * 30 146 tzOffset = timedelta(minutes=tzOffsetMinutes) 147 pubdate = pubdate.replace(tzinfo=FixedOffset(tzOffset)) 148 149 feed.add_item( 150 title = title_tmp.render(RequestContext(self.request, {'obj': item, 'site': current_site})), 151 link = link, 152 description = description_tmp.render(RequestContext(self.request, {'obj': item, 'site': current_site})), 153 unique_id = self.__get_dynamic_attr('item_guid', item, link), 154 enclosure = enc, 155 pubdate = pubdate, 156 author_name = author_name, 157 author_email = author_email, 158 author_link = author_link, 159 categories = self.__get_dynamic_attr('item_categories', item), 160 item_copyright = self.__get_dynamic_attr('item_copyright', item), 161 ) 176 feed.add_item(**self.get_item_kwargs(item)) 162 177 return feed