From e6472738b5253aa328f8b2a4f4f2a23abc8582c2 Mon Sep 17 00:00:00 2001 From: cedricbonhomme Date: Sun, 15 Apr 2012 18:59:50 +0200 Subject: Reorganization of folders. --- source/pyAggr3g470r.py | 1271 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1271 insertions(+) create mode 100755 source/pyAggr3g470r.py (limited to 'source/pyAggr3g470r.py') diff --git a/source/pyAggr3g470r.py b/source/pyAggr3g470r.py new file mode 100755 index 00000000..1284ea3e --- /dev/null +++ b/source/pyAggr3g470r.py @@ -0,0 +1,1271 @@ +#! /usr/bin/env python +#-*- coding: utf-8 -*- + +# pyAggr3g470r - A Web based news aggregator. +# Copyright (C) 2010-2012 Cédric Bonhomme - http://cedricbonhomme.org/ +# +# For more information : http://bitbucket.org/cedricbonhomme/pyaggr3g470r/ +# +# 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 + +__author__ = "Cedric Bonhomme" +__version__ = "$Revision: 3.1 $" +__date__ = "$Date: 2010/01/29 $" +__revision__ = "$Date: 2012/03/09 $" +__copyright__ = "Copyright (c) Cedric Bonhomme" +__license__ = "GPLv3" + +# +# This file contains the "Root" class which describes +# all pages of pyAggr3g470r. These pages are: +# - main page; +# - management; +# - history; +# - favorites; +# - notifications; +# - unread; +# - feed summary. +# + +import os +import re +import time +import cherrypy +import calendar + +from collections import Counter +import datetime + +import utils +import export +import mongodb +import feedgetter +from qrcode.pyqrnative.PyQRNative import QRCode, QRErrorCorrectLevel, CodeOverflowException +from qrcode import qr + + +def error_page_404(status, message, traceback, version): + """ + Display an error if the page does not exist. + """ + html = htmlheader() + html += htmlnav + html += "

Error %s - This page does not exist." % status + html += "\n
\n" + htmlfooter + return html + +def handle_error(): + """ + Handle different type of errors. + """ + html = htmlheader() + html += htmlnav + html += "

Sorry, an error occured" + html += "\n
\n" + htmlfooter + cherrypy.response.status = 500 + cherrypy.response.body = [html] + +def htmlheader(nb_unread_articles=""): + """ + Return the header of the HTML page with the number of unread articles + in the 'title' HTML tag.. + """ + return '\n' + \ + '' + \ + '\n\t'+ nb_unread_articles +'pyAggr3g470r - News aggregator\n' + \ + '\t' + \ + '\n\t\n' + \ + '\n\t\n' + \ + '\n' + +htmlfooter = '

This software is under GPLv3 license. You are welcome to copy, modify or' + \ + ' redistribute the source code according to the' + \ + ' GPLv3 license.

\n' + \ + '\n' + +htmlnav = '\n

pyAggr3g470r - News aggregator

\n' + \ + 'pyAggr3g470r (source code)' + + +class Root: + """ + Root class. + All pages of pyAggr3g470r are described in this class. + """ + def __init__(self): + """ + """ + self.mongo = mongodb.Articles(utils.MONGODB_ADDRESS, utils.MONGODB_PORT) + + def index(self): + """ + Main page containing the list of feeds and articles. + """ + feeds = self.mongo.get_all_collections() + nb_unread_articles = self.mongo.nb_unread_articles() + nb_favorites = self.mongo.nb_favorites() + nb_mail_notifications = self.mongo.nb_mail_notifications() + + # if there are unread articles, display the number in the tab of the browser + html = htmlheader((nb_unread_articles and \ + ['(' + str(nb_unread_articles) +') '] or \ + [""])[0]) + html += htmlnav + html += self.create_right_menu() + html += """
\n""" + + if feeds: + html += '\n' + html += '\n' + html += '      \n' + + html += """\n""" % \ + (nb_favorites,) + + html += """\n""" % \ + (nb_mail_notifications,) + + html += '      ' + if nb_unread_articles != 0: + html += '\n' + html += """\n""" % \ + (nb_unread_articles,) + html += '\n' + + + # The main page display all the feeds. + for feed in feeds: + html += """

%s +

\n""" % \ + (feed["feed_id"], feed["feed_link"], feed["feed_title"], \ + feed["feed_link"], feed["feed_image"]) + + # The main page display only 10 articles by feeds. + for article in self.mongo.get_articles_from_collection(feed["feed_id"])[:10]: + if article["article_readed"] == False: + # not readed articles are in bold + not_read_begin, not_read_end = "", "" + else: + not_read_begin, not_read_end = "", "" + + # display a heart for faved articles + if article["article_like"] == True: + like = """ """ + else: + like = "" + + # Descrition for the CSS ToolTips + article_content = utils.clear_string(article["article_content"]) + if article_content: + description = " ".join(article_content.split(' ')[:55]) + else: + description = "No description." + # Title of the article + article_title = article["article_title"] + if len(article_title) >= 110: + article_title = article_title[:110] + " ..." + + # a description line per article (date, title of the article and + # CSS description tooltips on mouse over) + html += article["article_date"].ctime() + " - " + \ + """%s%s%s%s""" % \ + (feed["feed_id"], article["article_id"], not_read_begin, \ + article_title, not_read_end, description) + like + "
\n" + html += "
\n" + + # some options for the current feed + html += """All articles   """ % (feed["feed_id"],) + html += """Feed summary   """ % (feed["feed_id"],) + if self.mongo.nb_unread_articles(feed["feed_id"]) != 0: + html += """  Mark all as read""" % (feed["feed_id"],) + html += """     Unread article(s) (%s)""" % (feed["feed_id"], self.mongo.nb_unread_articles(feed["feed_id"])) + if feed["mail"] == "0": + html += """
\nStay tuned""" % (feed["feed_id"],) + else: + html += """
\nStop staying tuned""" % (feed["feed_id"],) + html += """

Top

""" + html += "
\n" + html += htmlfooter + return html + + index.exposed = True + + + def create_right_menu(self): + """ + Create the right menu. + """ + html = """
\n""" + html += """
\n""" + html += "
\n" + # insert the list of feeds in the menu + html += self.create_list_of_feeds() + html += "
\n" + + return html + + def create_list_of_feeds(self): + """ + Create the list of feeds. + """ + feeds = self.mongo.get_all_collections() + html = """" + + + def management(self, max_nb_articles=5): + """ + Management page. + Allows adding and deleting feeds. Export functions of the MongoDB data base + and display some statistics. + """ + feeds = self.mongo.get_all_collections() + nb_mail_notifications = self.mongo.nb_mail_notifications() + nb_favorites = self.mongo.nb_favorites() + nb_articles = self.mongo.nb_articles() + nb_unread_articles = self.mongo.nb_unread_articles() + + html = htmlheader() + html += htmlnav + html += """
\n""" + html += "

Add Feeds

\n" + # Form: add a feed + html += """
\n
\n""" + + if feeds: + # Form: delete a feed + html += "

Delete Feeds

\n" + html += """
\n""" + + html += """

Active e-mail notifications: %s

\n""" % \ + (nb_mail_notifications,) + html += """

You like %s article(s).

\n""" % \ + (nb_favorites, ) + + html += "
\n" + + # Informations about the data base of articles + html += """

%s article(s) are loaded from the database with + %s unread article(s).
\n""" % \ + (nb_articles, nb_unread_articles) + #html += """Database: %s.\n
Size: %s bytes.
\n""" % \ + #(os.path.abspath(utils.sqlite_base), os.path.getsize(utils.sqlite_base)) + html += 'Advanced statistics.

\n' + + html += """
\n
\n""" + html += """
\n
\n""" + + + html += '
\n' + html += "For each feed only load the " + html += """\n""" % (max_nb_articles) + html += " last articles." + if utils.MAX_NB_ARTICLES == -1: + html += "
All articles are currently loaded.\n" + else: + html += "
For each feed only " + str(utils.MAX_NB_ARTICLES) + " articles are currently loaded. " + html += 'Load all articles.
\n' + html += "
\n" + + # Export functions + html += "

Export articles

\n\n" + html += """
\n\t\n
\n""" + html += "
" + html += htmlfooter + return html + + management.exposed = True + + + def statistics(self, word_size=6): + """ + More advanced statistics. + """ + articles = self.mongo.get_all_articles() + html = htmlheader() + html += htmlnav + html += """
\n""" + + # Some statistics (most frequent word) + if articles: + top_words = utils.top_words(articles, n=50, size=int(word_size)) + html += "

Statistics

\n
\n" + # Tags cloud + html += 'Minimum size of a word:' + html += '
' + html += """""" % (word_size) + html += '
\n' + html += '

Tag cloud

\n' + html += '
' + \ + utils.tag_cloud(top_words) + '
' + html += "
\n" + + html += htmlfooter + return html + + statistics.exposed = True + + + def search(self, query=None): + """ + Simply search for the string 'query' + in the description of the article. + """ + param, _, value = query.partition(':') + wordre = re.compile(r'\b%s\b' % param, re.I) + feed_id = None + if param == "Feed": + feed_id, _, query = value.partition(':') + html = htmlheader() + html += htmlnav + html += """
""" + html += """

Articles containing the string %s


""" % (query,) + + if feed_id is not None: + for article in self.feeds[feed_id].articles.values(): + article_content = utils.clear_string(article.article_description) + if not article_content: + utils.clear_string(article.article_title) + if wordre.findall(article_content) != []: + if article.article_readed == "0": + # not readed articles are in bold + not_read_begin, not_read_end = "", "" + else: + not_read_begin, not_read_end = "", "" + + html += article.article_date + " - " + not_read_begin + \ + """%s""" % \ + (feed_id, article.article_id, article.article_title) + \ + not_read_end + """
\n""" + else: + feeds = self.mongo.get_all_collections() + for feed in feeds: + new_feed_section = True + for article in self.mongo.get_articles_from_collection(feed["feed_id"]): + article_content = utils.clear_string(article["article_content"]) + if not article_content: + utils.clear_string(article["article_title"]) + if wordre.findall(article_content) != []: + if new_feed_section is True: + new_feed_section = False + html += """

%s

\n""" % \ + (feed["feed_id"], feed["feed_title"], feed["feed_link"], feed["feed_image"]) + + if article["article_readed"] == False: + # not readed articles are in bold + not_read_begin, not_read_end = "", "" + else: + not_read_begin, not_read_end = "", "" + + # display a heart for faved articles + if article["article_like"] == True: + like = """ """ + else: + like = "" + + # descrition for the CSS ToolTips + article_content = utils.clear_string(article["article_content"]) + if article_content: + description = " ".join(article_content[:500].split(' ')[:-1]) + else: + description = "No description." + + # a description line per article (date, title of the article and + # CSS description tooltips on mouse over) + html += article["article_date"].ctime() + " - " + \ + """%s%s%s%s""" % \ + (feed["feed_id"], article["article_id"], not_read_begin, \ + article["article_title"][:150], not_read_end, description) + like + "
\n" + html += "
" + html += htmlfooter + return html + + search.exposed = True + + + def fetch(self): + """ + Fetch all feeds. + """ + feed_getter = feedgetter.FeedGetter() + feed_getter.retrieve_feed() + return self.index() + + fetch.exposed = True + + + def article(self, param): + """ + Display the article in parameter in a new Web page. + """ + try: + feed_id, article_id = param.split(':') + feed = self.mongo.get_collection(feed_id) + articles = self.mongo.get_articles_from_collection(feed_id) + article = self.mongo.get_article(feed_id, article_id) + except: + return self.error_page("Bad URL. This article do not exists.") + html = htmlheader() + html += htmlnav + html += """
""" + + if article["article_readed"] == False: + # if the current article is not yet readed, update the database + self.mark_as_read("Article:"+article["article_id"]+":"+feed["feed_id"]) + + html += '\n
\n' + # Title of the article + html += """

%s from %s

\n
\n""" % \ + (article["article_title"], feed_id, feed["feed_title"]) + if article["article_like"] == True: + html += """""" % \ + (feed_id, article["article_id"]) + else: + html += """""" % \ + (feed_id, article["article_id"]) + html += """  """ % \ + (feed_id, article["article_id"]) + html += "

" + + # Description (full content) of the article + description = article["article_content"] + if description: + p = re.compile(r'<') + q = re.compile(r'>') + + description = p.sub('<', description) + description = q.sub('>', description) + + html += description + "\n


" + else: + html += "No description available.\n


" + + # Generation of the QR Code for the current article + try: + os.makedirs("./var/qrcode/") + except OSError: + pass + if not os.path.isfile("./var/qrcode/" + article_id + ".png"): + # QR Code generation + try: + if len(utils.clear_string(description)) > 4296: + raise Exception() + f = qr.QRUrl(url = utils.clear_string(description)) + f.make() + except: + f = qr.QRUrl(url = article["article_link"]) + f.make() + f.save("./var/qrcode/"+article_id+".png") + + # Previous and following articles + articles_list = articles.distinct("article_id") + try: + following = articles[articles_list.index(article_id) - 1] + html += """
\n""" % \ + (feed_id, following["article_id"], following["article_title"]) + except Exception, e: + print e + try: + previous = articles[articles_list.index(article_id) + 1] + except: + previous = articles[0] + finally: + html += """
\n""" % \ + (feed_id, previous["article_id"], previous["article_title"]) + + html += "\n
\n" + + # Footer menu + html += "
\n" + html += """\nPlain text\n""" % (feed_id, article["article_id"]) + html += """ - Export to EPUB\n""" % (feed_id, article["article_id"]) + html += """
\nComplete story\n
\n""" % (article["article_link"],) + + # Share this article: + html += "Share this article:
\n" + # on Diaspora + html += """\n\t + \n""" % \ + (utils.DIASPORA_POD, article["article_link"], article["article_title"], "via pyAggr3g470r") + + # on Identi.ca + html += """\n\n""" % \ + (article["article_title"], article["article_link"]) + + # on Hacker News + html += """\n\n""" % \ + (article["article_link"], article["article_title"]) + + # on Pinboard + html += """\n\n\t\n + """ % \ + (article["article_link"], article["article_title"]) + + # on Digg + html += """\n\n\t\n + """ % \ + (article["article_link"], article["article_title"]) + # on reddit + html += """\n\n\t\n + """ % \ + (article["article_link"], article["article_title"]) + # on Scoopeo + html += """\n\n\t\n + """ % \ + (article["article_link"], article["article_title"]) + # on Blogmarks + html += """\n\n\t\n + """ % \ + (article["article_link"], article["article_title"]) + + # Google +1 button + html += """\n\n""" % \ + (article["article_link"],) + + + # QRCode (for smartphone) + html += """
\n""" % (article_id, article_id) + html += "
\n" + htmlfooter + return html + + article.exposed = True + + + def feed(self, feed_id, word_size=6): + """ + This page gives summary informations about a feed (number of articles, + unread articles, average activity, tag cloud, e-mail notification and + favourite articles for the current feed. + """ + try: + feed = self.mongo.get_collection(feed_id) + articles = self.mongo.get_articles_from_collection(feed_id) + except KeyError: + return self.error_page("This feed do not exists.") + html = htmlheader() + html += htmlnav + html += """
""" + html += "

The feed " + feed["feed_title"] + " contains " + str(self.mongo.nb_articles(feed_id)) + " articles. " + html += "Representing " + str((round(float(self.mongo.nb_articles(feed_id)) / 1000, 4)) * 100) + " % of the total " #hack + html += "(" + str(1000) + ").

" + if articles != []: + html += "

" + (self.mongo.nb_unread_articles(feed_id) == 0 and ["All articles are read"] or [str(self.mongo.nb_unread_articles(feed_id)) + \ + " unread article" + (self.mongo.nb_unread_articles(feed_id) == 1 and [""] or ["s"])[0]])[0] + ".

" + if feed["mail"] == True: + html += """

You are receiving articles from this feed to the address: %s. """ % \ + (utils.mail_to, utils.mail_to) + html += """Stop receiving articles from this feed.

""" % \ + (feed[feed_id], ) + + if articles != []: + last_article = utils.string_to_datetime(str(articles[0]["article_date"])) + first_article = utils.string_to_datetime(str(articles[self.mongo.nb_articles(feed_id)-2]["article_date"])) + delta = last_article - first_article + delta_today = datetime.datetime.fromordinal(datetime.date.today().toordinal()) - last_article + html += "

The last article was posted " + str(abs(delta_today.days)) + " day(s) ago.

" + if delta.days > 0: + html += """

Daily average: %s,""" % (str(round(float(self.mongo.nb_articles(feed_id))/abs(delta.days), 2)),) + html += """ between the %s and the %s.

\n""" % \ + (str(articles[self.mongo.nb_articles(feed_id)-2]["article_date"])[:10], str(articles[0]["article_date"])[:10]) + + html += "

Recent articles

" + for article in articles[:10]: + if article["article_readed"] == False: + # not readed articles are in bold + not_read_begin, not_read_end = "", "" + else: + not_read_begin, not_read_end = "", "" + + # display a heart for faved articles + if article["article_like"] == True: + like = """ """ + else: + like = "" + + # Descrition for the CSS ToolTips + article_content = utils.clear_string(article["article_content"]) + if article_content: + description = " ".join(article_content[:500].split(' ')[:-1]) + else: + description = "No description." + # Title of the article + article_title = article["article_title"] + if len(article_title) >= 110: + article_title = article_title[:110] + " ..." + + # a description line per article (date, title of the article and + # CSS description tooltips on mouse over) + html += article["article_date"].ctime() + " - " + \ + """%s%s%s%s""" % \ + (feed["feed_id"], article["article_id"], not_read_begin, \ + article_title, not_read_end, description) + like + "
\n" + html += "
\n" + html += """All articles   """ % (feed["feed_id"],) + + favs = [article for article in articles if article["article_like"] == True] + if len(favs) != 0: + html += "

Your favorites articles for this feed

" + for article in favs: + if article["like"] == True: + # descrition for the CSS ToolTips + article_content = utils.clear_string(article["article_content"]) + if article_content: + description = " ".join(article_content[:500].split(' ')[:-1]) + else: + description = "No description." + + # a description line per article (date, title of the article and + # CSS description tooltips on mouse over) + html += article["article_date"].ctime() + " - " + \ + """%s%s
\n""" % \ + (feed["feed_id"], article["article_id"], article["article_title"][:150], description) + + + # This section enables the user to edit informations about + # the current feed: + # - feed logo; + # - feed name; + # - URL of the feed (not the site); + html += "
\n

Edit this feed

\n" + html += '\n\n
' + \ + '' + \ + """
\n""" % \ + (feed["feed_link"],) + html += '\n\n
' + \ + '' + \ + """
\n""" % \ + (feed["feed_link"],) + html += '\n\n
' + \ + '' + \ + """
\n""" % \ + (feed["feed_link"],) + + dic = {} + top_words = utils.top_words(articles = self.mongo.get_articles_from_collection(feed_id), n=50, size=int(word_size)) + html += "

Tag cloud

\n
\n" + # Tags cloud + html += 'Minimum size of a word:' + html += """
""" % (feed["feed_id"],) + html += """""" % (word_size,) + html += '
\n' + html += '
' + \ + utils.tag_cloud(top_words) + '
' + + html += "
" + html += "
" + html += htmlfooter + return html + + feed.exposed = True + + + def articles(self, feed_id): + """ + This page displays all articles of a feed. + """ + try: + feed = self.mongo.get_collection(feed_id) + articles = self.mongo.get_articles_from_collection(feed_id) + except KeyError: + return self.error_page("This feed do not exists.") + html = htmlheader() + html += htmlnav + html += """
\n""" + html += """Mark all articles from this feed as read""" % (feed_id,) + html += """
\n
\n""" % ("Feed:"+feed_id,) + html += "
\n" + html += self.create_list_of_feeds() + html += """
""" + html += """

Articles of the feed %s


""" % (feed["feed_title"],) + + for article in articles: + + if article["article_readed"] == False: + # not readed articles are in bold + not_read_begin, not_read_end = "", "" + else: + not_read_begin, not_read_end = "", "" + + if article["article_like"] == True: + like = """ """ + else: + like = "" + + # descrition for the CSS ToolTips + article_content = utils.clear_string(article["article_content"]) + if article_content: + description = " ".join(article_content[:500].split(' ')[:-1]) + else: + description = "No description." + + # a description line per article (date, title of the article and + # CSS description tooltips on mouse over) + html += article["article_date"].ctime() + " - " + \ + """%s%s%s%s""" % \ + (feed_id, article["article_id"], not_read_begin, \ + article["article_title"][:150], not_read_end, description) + like + "
\n" + + html += """\n

All feeds

""" + html += "
\n" + html += htmlfooter + return html + + articles.exposed = True + + + def unread(self, feed_id=""): + """ + This page displays all unread articles of a feed. + """ + feeds = self.mongo.get_all_collections() + html = htmlheader() + html += htmlnav + html += """
""" + if self.mongo.nb_unread_articles() != 0: + + # List unread articles of all the database + if feed_id == "": + html += "

Unread article(s)

" + html += """\n
\nMark articles as read\n
\n""" + for feed in feeds: + new_feed_section = True + nb_unread = 0 + + # For all unread article of the current feed. + for article in self.mongo.get_articles_from_collection(feed["feed_id"], condition=("article_readed", False)): + nb_unread += 1 + if new_feed_section is True: + new_feed_section = False + html += """

%s

\n""" % \ + (feed["feed_id"], feed["site_link"], feed["feed_title"], feed["feed_link"], feed["feed_image"]) + + # descrition for the CSS ToolTips + article_content = utils.clear_string(article["article_content"]) + if article_content: + description = " ".join(article_content[:500].split(' ')[:-1]) + else: + description = "No description." + + # a description line per article (date, title of the article and + # CSS description tooltips on mouse over) + html += article["article_date"].ctime() + " - " + \ + """%s%s
\n""" % \ + (feed["feed_id"], article["article_id"], article["article_title"][:150], description) + + if nb_unread == self.mongo.nb_unread_articles(feed["feed_id"]): + html += """
\nMark all articles from this feed as read\n""" % \ + (feed["feed_id"],) + html += """
\nMark articles as read\n""" + + # List unread articles of a feed + else: + try: + feed = self.mongo.get_collection(feed_id) + except: + self.error_page("This feed do not exists.") + html += """

Unread article(s) of the feed %s

+
""" % (feed_id, feed["feed_title"]) + + # For all unread article of the feed. + for article in self.mongo.get_articles_from_collection(feed_id, condition=("article_readed", False)): + # descrition for the CSS ToolTips + article_content = utils.clear_string(article["article_content"]) + if article_content: + description = " ".join(article_content[:500].split(' ')[:-1]) + else: + description = "No description." + + # a description line per article (date, title of the article and + # CSS description tooltips on mouse over) + html += article["article_date"].ctime() + " - " + \ + """%s%s
\n""" % \ + (feed_id, article["article_id"], article["article_title"][:150], description) + + html += """
\nMark all as read""" % (feed_id,) + # No unread article + else: + html += '

No unread article(s)

\n
\nWhy not check for news?' + html += """\n

All feeds

""" + html += "
\n" + html += htmlfooter + return html + + unread.exposed = True + + + def history(self, query="all", m=""): + """ + This page enables to browse articles chronologically. + """ + feeds = self.mongo.get_all_collections() + html = htmlheader() + html += htmlnav + html += """
\n""" + + # Get the date from the tag cloud + # Format: /history/?query=year:2011-month:06 to get the + # list of articles of June, 2011. + if m != "": + query = """year:%s-month:%s""" % tuple(m.split('-')) + + if query == "all": + html += "

Search with tags cloud

\n" + html += "

Choose a year


\n" + if "year" in query: + the_year = query.split('-')[0].split(':')[1] + if "month" not in query: + html += "

Choose a month for " + the_year + "


\n" + if "month" in query: + the_month = query.split('-')[1].split(':')[1] + html += "

Articles of "+ calendar.month_name[int(the_month)] + \ + ", "+ the_year +".


\n" + + timeline = Counter() + for feed in feeds: + new_feed_section = True + for article in self.mongo.get_articles_from_collection(feed["feed_id"]): + + if query == "all": + timeline[str(article["article_date"]).split(' ')[0].split('-')[0]] += 1 + + elif query[:4] == "year": + + if str(article["article_date"]).split(' ')[0].split('-')[0] == the_year: + timeline[str(article["article_date"]).split(' ')[0].split('-')[1]] += 1 + + if "month" in query: + if str(article["article_date"]).split(' ')[0].split('-')[1] == the_month: + if article["article_readed"] == False: + # not readed articles are in bold + not_read_begin, not_read_end = "", "" + else: + not_read_begin, not_read_end = "", "" + + if article["article_like"] == True: + like = """ """ + else: + like = "" + # Descrition for the CSS ToolTips + article_content = utils.clear_string(article["article_content"]) + if article_content: + description = " ".join(article_content[:500].split(' ')[:-1]) + else: + description = "No description." + # Title of the article + article_title = article["article_title"] + if len(article_title) >= 110: + article_title = article_title[:110] + " ..." + + if new_feed_section is True: + new_feed_section = False + html += """

%s

\n""" % \ + (feed["feed_id"], feed["feed_link"], feed["feed_title"], feed["feed_link"], feed["feed_image"]) + + html += article["article_date"].strftime("%a %d (%H:%M:%S) ") + \ + """%s%s%s%s""" % \ + (feed["feed_id"], article["article_id"], not_read_begin, \ + article_title, not_read_end, description) + like + "
\n" + if query == "all": + query_string = "year" + elif "year" in query: + query_string = "year:" + the_year + "-month" + if "month" not in query: + html += '
' + \ + utils.tag_cloud([(elem, timeline[elem]) for elem in timeline.keys()], query_string) + '
' + html += '

Search with a month+year picker

\n' + html += '
\n\t\n\t\n
' + html += '
' + html += htmlfooter + return html + + history.exposed = True + + + def plain_text(self, target): + """ + Display an article in plain text (without HTML tags). + """ + try: + feed_id, article_id = target.split(':') + feed = self.mongo.get_collection(feed_id) + article = self.mongo.get_article(feed_id, article_id) + except: + return self.error_page("Bad URL. This article do not exists.") + html = htmlheader() + html += htmlnav + html += """
""" + html += """

%s from %s

\n
\n"""% \ + (article["article_title"], feed_id, feed["feed_title"]) + description = utils.clear_string(article["article_content"]) + if description: + html += description + else: + html += "No description available." + html += "\n
\n" + htmlfooter + return html + + plain_text.exposed = True + + + def error_page(self, message): + """ + Display a message (bad feed id, bad article id, etc.) + """ + html = htmlheader() + html += htmlnav + html += """
""" + html += """%s""" % message + html += "\n
\n" + htmlfooter + return html + + error_page.exposed = True + + + def mark_as_read(self, target=""): + """ + Mark one (or more) article(s) as read by setting the value of the field + 'article_readed' of the MongoDB database to 'True'. + """ + param, _, identifiant = target.partition(':') + + # Mark all articles as read. + if param == "": + self.mongo.mark_as_read(True, None, None) + # Mark all articles from a feed as read. + elif param == "Feed" or param == "Feed_FromMainPage": + self.mongo.mark_as_read(True, identifiant, None) + # Mark an article as read. + elif param == "Article": + self.mongo.mark_as_read(True, identifiant.split(':')[1], identifiant.split(':')[0]) + if param == "" or param == "Feed_FromMainPage": + return self.index() + elif param == "Feed": + return self.articles(identifiant) + + mark_as_read.exposed = True + + + def notifications(self): + """ + List all active e-mail notifications. + """ + html = htmlheader() + html += htmlnav + html += """
""" + feeds = self.mongo.get_all_collections(condition=("mail",True)) + if feeds != []: + html += "

You are receiving e-mails for the following feeds:

\n" + for feed in feeds: + html += """\t%s - Stop
\n""" % \ + (feed["feed_id"], feed["feed_title"], feed["feed_id"]) + else: + html += "

No active notifications.

\n" + html += """

Notifications are sent to: %s

""" % \ + (utils.mail_to, utils.mail_to) + html += "\n
\n" + htmlfooter + return html + + notifications.exposed = True + + + def mail_notification(self, param): + """ + Enable or disable to notifications of news for a feed. + """ + try: + action, feed_id = param.split(':') + feed = self.feeds[feed_id] + except: + return self.error_page("Bad URL. This feed do not exists.") + + return self.index() + + mail_notification.exposed = True + + + def like(self, param): + """ + Mark or unmark an article as favorites. + """ + try: + like, feed_id, article_id = param.split(':') + articles = self.mongo.get_article(feed_id, article_id) + except: + return self.error_page("Bad URL. This article do not exists.") + self.mongo.like_article("1"==like, feed_id, article_id) + return self.article(feed_id+":"+article_id) + + like.exposed = True + + + def favorites(self): + """ + List of favorites articles + """ + feeds = self.mongo.get_all_collections() + html = htmlheader() + html += htmlnav + html += """
""" + html += "

Your favorites articles

" + for feed in feeds: + new_feed_section = True + for article in self.mongo.get_articles_from_collection(feed["feed_id"]): + if article["article_like"] == True: + if new_feed_section is True: + new_feed_section = False + html += """

%s

\n""" % \ + (feed["feed_id"], feed["feed_link"], feed["feed_title"], feed["feed_link"], feed["feed_image"]) + + # descrition for the CSS ToolTips + article_content = utils.clear_string(article["article_content"]) + if article_content: + description = " ".join(article_content[:500].split(' ')[:-1]) + else: + description = "No description." + + # a description line per article (date, title of the article and + # CSS description tooltips on mouse over) + html += article["article_date"].ctime() + " - " + \ + """%s%s
\n""" % \ + (feed["feed_id"], article["article_id"], article["article_title"][:150], description) + html += "
\n" + html += htmlfooter + return html + + favorites.exposed = True + + + def add_feed(self, url): + """ + Add a new feed with the URL of a page. + """ + html = htmlheader() + html += htmlnav + html += """
""" + # search the feed in the HTML page with BeautifulSoup + feed_url = utils.search_feed(url) + if feed_url is None: + return self.error_page("Impossible to find a feed at this URL.") + # if a feed exists + else: + result = utils.add_feed(feed_url) + # if the feed is not in the file feed.lst + if result is False: + html += "

You are already following this feed!

" + else: + html += """

Feed added. You can now fetch your feeds.

""" + html += """\n
\nBack to the management page.
\n""" + html += "
\n" + html += htmlfooter + return html + + add_feed.exposed = True + + + def remove_feed(self, feed_id): + """ + Remove a feed from the file feed.lst and from the MongoDB database. + """ + html = htmlheader() + html += htmlnav + html += """
""" + + feed = self.mongo.get_collection(feed_id) + self.mongo.delete_feed(feed_id) + utils.remove_feed(feed["feed_link"]) + + html += """

All articles from the feed %s are now removed from the base.


""" % \ + (feed["feed_title"],) + html += """Back to the management page.
\n""" + html += "
\n" + html += htmlfooter + return html + + remove_feed.exposed = True + + + def change_feed_url(self, new_feed_url, old_feed_url): + """ + Enables to change the URL of a feed already present in the database. + """ + html = htmlheader() + html += htmlnav + html += """
""" + utils.change_feed_url(old_feed_url, new_feed_url) + html += "

The URL of the feed has been changed.

" + html += "
\n" + html += htmlfooter + return html + + change_feed_url.exposed = True + + def change_feed_name(self, feed_url, new_feed_name): + """ + Enables to change the name of a feed. + """ + html = htmlheader() + html += htmlnav + html += """
""" + utils.change_feed_name(feed_url, new_feed_name) + html += "

The name of the feed has been changed.

" + html += "
\n" + html += htmlfooter + return html + + change_feed_name.exposed = True + + def change_feed_logo(self, feed_url, new_feed_logo): + """ + Enables to change the name of a feed. + """ + html = htmlheader() + html += htmlnav + html += """
""" + utils.change_feed_logo(feed_url, new_feed_logo) + html += "

The logo of the feed has been changed.

" + html += "
\n" + html += htmlfooter + return html + + change_feed_logo.exposed = True + + + def set_max_articles(self, max_nb_articles=1): + """ + Enables to set the maximum of articles to be loaded per feed from + the data base. + """ + if max_nb_articles < -1 or max_nb_articles == 0: + max_nb_articles = 1 + utils.MAX_NB_ARTICLES = int(max_nb_articles) + return self.management() + + set_max_articles.exposed = True + + + def delete_article(self, param): + """ + Delete an article. + """ + try: + feed_id, article_id = param.split(':') + self.mongo.delete_article(feed_id, article_id) + except: + return self.error_page("Bad URL. This article do not exists.") + + return self.index() + + delete_article.exposed = True + + + def drop_base(self): + """ + Delete all articles. + """ + utils.drop_base() + return self.management() + + drop_base.exposed = True + + + def export(self, export_method): + """ + Export articles currently loaded from the MongoDB database with + the appropriate function of the 'export' module. + """ + try: + getattr(export, export_method)(self.feeds) + except Exception, e: + return self.error_page(e) + return self.management() + + export.exposed = True + + + def epub(self, param): + """ + Export an article to EPUB. + """ + try: + from epub import ez_epub + except Exception, e: + return self.error_page(e) + try: + feed_id, article_id = param.split(':') + except: + return self.error_page("Bad URL.") + try: + feed = self.feeds[feed_id] + article = feed.articles[article_id] + except: + self.error_page("This article do not exists.") + try: + folder = utils.path + "/var/export/epub/" + os.makedirs(folder) + except OSError: + # directories already exists (not a problem) + pass + section = ez_epub.Section() + section.title = article.article_title.decode('utf-8') + section.paragraphs = [utils.clear_string(article.article_description).decode('utf-8')] + ez_epub.makeBook(article.article_title.decode('utf-8'), [feed.feed_title.decode('utf-8')], [section], \ + os.path.normpath(folder) + "article.epub", lang='en-US', cover=None) + return self.article(param) + + epub.exposed = True + + +if __name__ == '__main__': + # Point of entry in execution mode + print "Launching pyAggr3g470r..." + + root = Root() + root.favicon_ico = cherrypy.tools.staticfile.handler(filename=os.path.join(utils.path + "/img/favicon.png")) + cherrypy.config.update({ 'server.socket_port': 12556, 'server.socket_host': "0.0.0.0"}) + cherrypy.config.update({'error_page.404': error_page_404}) + _cp_config = {'request.error_response': handle_error} + + cherrypy.quickstart(root, "/" ,config=utils.path + "/cfg/cherrypy.cfg") \ No newline at end of file -- cgit