From 12d48918d8eac2739783cf383154fe1245caefc9 Mon Sep 17 00:00:00 2001 From: pictuga Date: Wed, 9 Oct 2013 00:48:05 +0200 Subject: [PATCH] Add time handling in feeds.py Lots of time format can be passed as a value (RSS/Atom time strings, timestamp, anything supported by dateutil.parser). Implemented in twitter feedify hook. --- feeds.py | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/feeds.py b/feeds.py index aec4577..ce21195 100644 --- a/feeds.py +++ b/feeds.py @@ -1,6 +1,9 @@ #!/usr/bin/env python from lxml import etree +from datetime import datetime +import dateutil.parser +from dateutil import tz import re Element = etree.Element @@ -131,6 +134,38 @@ class FeedDescriptor(object): deleter = getattr(instance, 'del%s' % self.name.title()) return deleter() +class FeedTime(FeedDescriptor): + def __get__(self, instance, owner): + getter = getattr(instance, 'get%s' % self.name.title()) + raw = getter() + try: + time = parseTime(raw) + return time + except ValueError: + return None + + def __set__(self, instance, value): + try: + time = parseTime(value) + raw = time.strftime(instance.timeFormat) + setter = getattr(instance, 'set%s' % self.name.title()) + return setter(raw) + except ValueError: + pass + +def parseTime(value): + if isinstance(value, basestring): + if re.match(r'^[0-9]+$', value): + return datetime.fromtimestamp(int(value), tz.tzutc()) + else: + return dateutil.parser.parse(value, tzinfos=tz.tzutc) + elif isinstance(value, int): + return datetime.fromtimestamp(value, tz.tzutc()) + elif isinstance(value, datetime): + return value + else: + return False + class FeedList(object): """ Class to map a list of xml elements against a list of matching objects, @@ -334,6 +369,8 @@ class FeedParserAtom(FeedParser): return self.xpath('atom:entry|atom03:entry') class FeedItem(FeedBase): + timeFormat = '' + def __init__(self, xml=None, tag='atom:feed'): if xml is None: xml = Element(tagNS(self.base[tag])) @@ -381,10 +418,31 @@ class FeedItem(FeedBase): self.content = "" + def getTime(self): + return None + + def setTime(self, value): + pass + + def delTime(self): + self.time = None + + + def getUpdated(self): + return None + + def setUpdated(self, value): + pass + + def delUpdated(self): + self.updated = None + title = FeedDescriptor('title') link = FeedDescriptor('link') description = desc = FeedDescriptor('desc') content = FeedDescriptor('content') + time = FeedTime('time') + updated = FeedTime('updated') def pushContent(self, value): if not self.desc and self.content: @@ -396,6 +454,7 @@ class FeedItem(FeedBase): self.xml.getparent().remove(self.xml) class FeedItemRSS(FeedItem): + timeFormat = '%a, %d %b %Y %H:%M:%S %Z' base = { 'rdf:rdf': 'rssfake:item', 'channel': 'item'} @@ -447,7 +506,21 @@ class FeedItemRSS(FeedItem): element = self.xgetCreate(table) element.text = value + + def getTime(self): + return self.xval('rssfake:pubDate|pubDate') + + def setTime(self, value): + if not value: + return self.xdel('rssfake:pubDate|pubDate') + + table = { 'rdf:rdf': 'rssfake:pubDate', + 'channel': 'pubDate'} + element = self.xgetCreate(table) + element.text = value + class FeedItemAtom(FeedItem): + timeFormat = '%Y-%m-%dT%H:%M:%SZ' base = { 'atom:feed': 'atom:entry', 'atom03:feed': 'atom03:entry'} @@ -513,3 +586,28 @@ class FeedItemAtom(FeedItem): cleanNode(element) element.attrib['type'] = 'html' element.text = value + + def getTime(self): + return self.xval('atom:published|atom03:published') + + def setTime(self, value): + if not value: + return self.xdel('atom:published|atom03:published') + + table = { 'atom:feed': 'atom:published', + 'atom03:feed': 'atom03:published'} + element = self.xgetCreate(table) + element.text = value + + + def getUpdated(self): + return self.xval('atom:updated|atom03:updated') + + def setUpdated(self, value): + if not value: + return self.xdel('atom:updated|atom03:updated') + + table = { 'atom:feed': 'atom:updated', + 'atom03:feed': 'atom03:updated'} + element = self.xgetCreate(table) + element.text = value