Grigoriy Petukhov avatar Grigoriy Petukhov committed 16630f9

Improve date parsing. Add some tests

Comments (0)

Files changed (3)

feedzilla/test_data/feed_with_rudate

+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
+<channel> 
+            <title>UbuntuNews.ru: Лента новостей</title> 
+            <link>http://ubuntunews.ru/</link> 
+            <description /> 
+            <language>ru</language> 
+            <copyright>Новости Ubuntu GNU/Linux 2011</copyright> 
+            <ttl>120</ttl> 
+<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/Ubuntunewsru" /><feedburner:info uri="ubuntunewsru" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:browserFriendly></feedburner:browserFriendly><item> 
+    <title>Gtk+ 3.2 позволяет запускать десктоп приложения в браузере</title> 
+    <link>http://ubuntunews.ru/news/gtk-32-run-any-application-in-firefox.html</link> 
+    <description><![CDATA[
+Gtk+ 3.2 теперь позволяет запускать любое приложение в браузере с помощью GDK HTML5 бэкенда, таким образом можно запустить  GIMP, Gedit или любую другую программу на самом компьютере или даже удаленно, воспользовавшись веб-браузером.
+
+Бэкенд все еще нах&hellip;
+    ]]></description> 
+    <pubDate>Птн, 18 Мар 2011 02:47:00 +0300</pubDate> 
+    <guid isPermaLink="false">news/gtk-32-run-any-application-in-firefox.html</guid> 
+    <dc:creator>Root</dc:creator> 
+</item> 
+<item> 
+    <title>Вышла LXDE редакция Linux Mint 10</title> 
+    <link>http://ubuntunews.ru/news/julia-lxde-linux-mint-10.html</link> 
+    <description><![CDATA[
+Представлена LXDE редакция Linux Mint 10 Julia, легковесного десктоп дистрибутива основанного на Ubuntu 10.10, 
+с обновлением приложений и новыми характеристиками,  делающие систему еще более дружелюбной и простой в использовании. Размер дистрибутива 683&hellip;
+    ]]></description> 
+    <pubDate>Птн, 18 Мар 2011 01:04:00 +0300</pubDate> 
+    <guid isPermaLink="false">news/julia-lxde-linux-mint-10.html</guid> 
+    <dc:creator>Root</dc:creator> 
+</item> 
+<item> 
+    <title>Что нового в Unity 3.6.4</title> 
+    <link>http://ubuntunews.ru/news/unity-364-ubuntu-11-04.html</link> 
+    <description><![CDATA[
+Рассмотрим, что нового в Ubuntu 11.04 из визуальных изменения в текущей  версии графической оболочки Unity 3.6.4:
+
+
+По нажатию ALT + F2 откроется окно Dash, в котором при написание команды выводится визуальный список доступных программ и утилит подходя&hellip;
+    ]]></description> 
+    <pubDate>Срд, 16 Мар 2011 04:13:00 +0300</pubDate> 
+    <guid isPermaLink="false">news/unity-364-ubuntu-11-04.html</guid> 
+    <dc:creator>Root</dc:creator> 
+</item> 
+<item> 
+    <title>Дополнение Thunderbird доступно для тестирования в Natty</title> 
+    <link>http://ubuntunews.ru/news/thunderbird-unity-extension-testing.html</link> 
+    <description><![CDATA[
+Как известно Ubuntu использует почтовый клиент Evolution по умолчанию, и поддержка Thunderbird в Unity не является приоритетной задачей, вот этой работой по интеграции Thunderbird на панель Unity и занялся Mike Conley из Mozilla.
+
+На текущий момент пред&hellip;
+    ]]></description> 
+    <pubDate>Срд, 16 Мар 2011 01:04:00 +0300</pubDate> 
+    <guid isPermaLink="false">news/thunderbird-unity-extension-testing.html</guid> 
+    <dc:creator>Root</dc:creator> 
+</item> 
+<item> 
+    <title>Релиз Ultimate Edition 2.9</title> 
+    <link>http://ubuntunews.ru/news/ultimate-edition-2-9.html</link> 
+    <description><![CDATA[
+Вышел релиз DVD дистрибутива Ultimate Edition 2.9, основанного на пакетной базе Ubuntu 10.10,  включающего обширную подборку приложений, а также мультимедийные кодеки и драйверы устройств. 
+
+
+
+Характеристики выпуска:
+
+
+В дистрибутиве были произведе&hellip;
+    ]]></description> 
+    <pubDate>Втр, 15 Мар 2011 23:34:47 +0300</pubDate> 
+    <guid isPermaLink="false">news/ultimate-edition-2-9.html</guid> 
+    <dc:creator>Root</dc:creator> 
+</item> 
+<item> 
+    <title>Новый дизайн полосы прокрутки в Ubuntu 11.04</title> 
+    <link>http://ubuntunews.ru/news/ubuntus-new-overlay-scrollbars-natty.html</link> 
+    <description><![CDATA[
+В Ubuntu 11.04 появилась новая минималистичная полоса прокрутки, появляющийся при наведение мыши, вместо удаленной старой, сплошной полосы идущий вдоль границ приложения. 
+
+Mark пишет, что вдохновением для нового дизайна скролбара послужили мобильные ус&hellip;
+    ]]></description> 
+    <pubDate>Вск, 13 Мар 2011 18:50:41 +0300</pubDate> 
+    <guid isPermaLink="false">news/ubuntus-new-overlay-scrollbars-natty.html</guid> 
+    <dc:creator>Root</dc:creator> 
+</item> 
+<item> 
+    <title>15 новых обоев для Ubuntu</title> 
+    <link>http://ubuntunews.ru/apps/15-new-wallpaper-ubuntu.html</link> 
+    <description><![CDATA[
+В сети появились несколько новых замечательных обоев для Ubuntu, которые сейчас и рассмотрим.
+
+ino1 автор alkore31
+
+UBUNTU WALL 2 автор sasomkd
+
+ino2 автор alkore31
+
+Ubuntu Wallpaper автор iHack6
+
+Ubuntu white автор Sishnizzle
+
+Ubuntu Wallpape&hellip;
+    ]]></description> 
+    <pubDate>Птн, 11 Мар 2011 03:32:00 +0300</pubDate> 
+    <guid isPermaLink="false">apps/15-new-wallpaper-ubuntu.html</guid> 
+    <dc:creator>Root</dc:creator> 
+</item> 
+<item> 
+    <title>Новый логотип Ubuntu One</title> 
+    <link>http://ubuntunews.ru/news/new-ubuntu-one-logo.html</link> 
+    <description><![CDATA[
+Облачный сервис Ubuntu One, предназначенный для обмена файлами и синхронизации между компьютерами и мобильными устройствами, сменил логотип.
+
+
+Изменениям также подвергся и дизайн сайта.&hellip;
+    ]]></description> 
+    <pubDate>Птн, 11 Мар 2011 02:42:00 +0300</pubDate> 
+    <guid isPermaLink="false">news/new-ubuntu-one-logo.html</guid> 
+    <dc:creator>Root</dc:creator> 
+</item> 
+<item> 
+    <title>Canonical объединяет редакции Ubuntu для нетбуков и десктопов</title> 
+    <link>http://ubuntunews.ru/news/end-ubuntu-netbook-edition.html</link> 
+    <description><![CDATA[
+Gerry Carr, являющийся директором по маркетингу платформы в компании Сanonical, заявил, что в следующем апрельском релизе Ubuntu 11.04 отпала необходимость в специальной редакции дистрибутива «Netbook edition»  для нетбуков. Объясняя это тем, что пользова&hellip;
+    ]]></description> 
+    <pubDate>Птн, 11 Мар 2011 02:15:00 +0300</pubDate> 
+    <guid isPermaLink="false">news/end-ubuntu-netbook-edition.html</guid> 
+    <dc:creator>Root</dc:creator> 
+</item> 
+<item> 
+    <title>Вышел облачный дистрибутив Joli OS 1.2</title> 
+    <link>http://ubuntunews.ru/news/new-jolicloud-joli-os-1.2.html</link> 
+    <description><![CDATA[
+Вышла новая версия дистрибутива Joli OS 1.2, ранее известного как Jolicloud. Дистрибутив основан на пакетной базе Ubuntu и предназначен для работы на нетбуках и планшетах, обладает дружелюбным пользовательским интерфейсом основанным на веб технологиях и н&hellip;
+    ]]></description> 
+    <pubDate>Птн, 11 Мар 2011 00:55:00 +0300</pubDate> 
+    <guid isPermaLink="false">news/new-jolicloud-joli-os-1.2.html</guid> 
+    <dc:creator>Root</dc:creator> 
+</item> 
+    </channel> 
+</rss>

feedzilla/tests.py

+# -*- coding: utf-8 -*-
+from datetime import datetime
+import os.path
+
+from django.test import TestCase
+
+from feedzilla.util.parse import guess_date, parse_feed
+
+ROOT = os.path.dirname(os.path.realpath(__file__))
+DATA_DIR = os.path.join(ROOT, 'test_data')
+
+class ParserTestCase(TestCase):
+    def test_guess_datetime(self):
+
+        def create_feed(custom_language):
+            class FeedMockup(object):
+                "Emulates feedparser.parse() object"
+
+                class Feed(object):
+                    language = custom_language
+
+                    def __getitem__(self, key):
+                        return getattr(self, key)
+                feed = Feed()
+            return FeedMockup()
+
+
+        date_string = 'Птн, 18 Мар 2011 02:47:00 +0300'
+        feed = create_feed('ru')
+        guessed = guess_date([date_string], feed)
+        self.assertEqual(guessed, datetime(2011, 3, 18, 2, 47, 0))
+
+        # set language to en, this should fail
+        feed = create_feed('en')
+        guessed = guess_date([date_string], feed)
+        self.assertEqual(guessed, None)
+
+        data = open(os.path.join(DATA_DIR, 'feed_with_rudate')).read()
+        resp = parse_feed(source_data=data)
+        # Птн, 18 Мар 2011 01:04:00 +0300
+        self.assertEqual(resp['entries'][0]['created'], datetime(2011, 3, 18, 2, 47, 0))

feedzilla/util/parse.py

 """
 Functions for easy parsing RSS and ATOM feeds.
 """
-
+import locale
 import sha
 import re
 from time import mktime
 from datetime import datetime
 import feedparser
 import logging
-#from dateutil import parser
 
 import clean
 
-def guess_date(chunks):
+def guess_date(dates, feed):
     """
-    Try to find date in chunks.
-
-    Yep, shit.
+    Try to parse date in non-standart format.
     """
 
-    #for chunk in chunks:
-        #try:
-            #print 'TRY', chunk
-            #return parser.parse(chunk)
-        #except ValueError:
-            #pass
+    parsed = None
+    oldlocale = locale.getlocale()
 
-    #regexps = (
-        #(re.compile(r'\d+-\d+-\d+T\d+-\d+-\d+'), '%Y-%m-%dT%H:%M:%S'),
-    #)
+    for date_string in dates:
+        tz_offset = re.compile(r'\s+\+(\d{2})(\d{2})$')
+        match = tz_offset.search(date_string)
+        # TODO: implement processing TZ offset
+        # and normalizing the date to the project's TZ
+        if match:
+            date_string = tz_offset.sub('', date_string)
+        else:
+            pass
 
-    #for chunk in chunks:
-        #print chunk
-        #for rex, format in regexps:
-            #match = rex.match(chunk)
-            #if match:
-                #return datetime.strptime(chunk, format)
-            #else:
-                #print 'bad rex'
-    return None
+        lang = feed.feed.language[:2]
+        # strptime fails on unicode
+        if isinstance(date_string, unicode):
+            date_string = date_string.encode('utf-8')
 
+        if not lang.startswith('en'):
+            try:
+                locale_name = str('%s_%s.UTF-8' % (lang.lower(), lang.upper()))
+                locale.setlocale(locale.LC_ALL, locale_name)
+            except locale.Error:
+                pass
+            # try localized RFC 822 format
+            try:
+                parsed = datetime.strptime(date_string, '%a, %d %b %Y %H:%M:%S')
+            except ValueError:
+                pass
+            else:
+                break
+    locale.setlocale(locale.LC_ALL, oldlocale)
+    return parsed
 
-def parse_modified_date(entry):
+
+def parse_modified_date(entry, feed):
     """
     Find out modified date of feed entry.
     """
         return datetime.fromtimestamp(mktime(time_tuple))
 
     if unparsed:
-        guessed = guess_date(unparsed)
+        guessed = guess_date(unparsed, feed)
         if guessed:
             return guessed
-
-    logging.error('Could not parse modified date of %s' % getattr(entry, 'link', ''))
+    
+    example = unparsed[0] if unparsed else ''
+    logging.error('Could not parse modified date %s of post %s' % (getattr(entry, 'link', ''), example))
     return None
 
 
     resp = {'feed': None, 'success': False, 'entries': [], 'error': None}
 
     try:
-        #if url:
-            #source_data = urllib.urlopen(url).read()
-
-        # Crazy hack
-        #if '<rss' in source_data[:100]:
-            #if '<lastBuildDate>' in source_data:
-                #source_data = source_data.replace('<lastBuildDate>', '<pubDate>')
-                #source_data = source_data.replace('</lastBuildDate>', '</pubDate>')
-                #logging.debug('Crazy lastBuildDate hack was applyed to feed %s' % url)
-                #print source_data
-
         resp['feed'] = feedparser.parse(url and url or source_data)
     except Exception, ex:
         resp['error'] = ex
         summary = clean.safe_html(summary)
         content = clean.safe_html(content)
 
-        created = parse_modified_date(entry)
+        created = parse_modified_date(entry, resp['feed'])
         if not created:
             continue
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.