aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bootstrap.py5
-rw-r--r--conf.py133
-rw-r--r--conf/conf.cfg-sample2
-rw-r--r--pyaggr3g470r/controllers/abstract.py13
-rw-r--r--pyaggr3g470r/models/feed.py2
-rw-r--r--pyaggr3g470r/templates/home.html8
-rw-r--r--pyaggr3g470r/templates/layout.html12
-rw-r--r--pyaggr3g470r/templates/search.html48
-rw-r--r--pyaggr3g470r/views/views.py114
-rwxr-xr-xrunserver.py3
10 files changed, 149 insertions, 191 deletions
diff --git a/bootstrap.py b/bootstrap.py
index 9c2ce049..7a5a9b6e 100644
--- a/bootstrap.py
+++ b/bootstrap.py
@@ -22,13 +22,12 @@ from flask.ext.sqlalchemy import SQLAlchemy
# Create Flask application
application = Flask('pyaggr3g470r')
-application.debug = conf.WEBSERVER_DEBUG
+application.debug = conf.LOG_LEVEL <= logging.DEBUG
scheme, domain, _, _, _ = urlsplit(conf.PLATFORM_URL)
application.config['SERVER_NAME'] = domain
application.config['PREFERRED_URL_SCHEME'] = scheme
-set_logging(conf.LOG_PATH,
- log_level=logging.DEBUG if conf.WEBSERVER_DEBUG else logging.INFO)
+set_logging(conf.LOG_PATH, log_level=conf.LOG_LEVEL)
# Create dummy secrey key so we can use sessions
application.config['SECRET_KEY'] = getattr(conf, 'WEBSERVER_SECRET', None)
diff --git a/conf.py b/conf.py
index 03ff23a2..bd7ba0e9 100644
--- a/conf.py
+++ b/conf.py
@@ -1,12 +1,11 @@
#! /usr/bin/env python
-#-*- coding: utf-8 -*-
-
+# -*- coding: utf-8 -*-
""" Program variables.
This file contain the variables used by the application.
"""
-
import os
+import logging
basedir = os.path.abspath(os.path.dirname(__file__))
PATH = os.path.abspath(".")
@@ -29,11 +28,11 @@ DEFAULTS = {"python": "/usr/bin/python3.4",
"nb_worker": "100",
"default_max_error": "3",
"log_path": "pyaggr3g470r.log",
- "user_agent": "pyAggr3g470r " \
+ "log_level": "info",
+ "user_agent": "pyAggr3g470r "
"(https://bitbucket.org/cedricbonhomme/pyaggr3g470r)",
"resolve_article_url": "false",
"http_proxy": "",
- "debug": "true",
"secret": "",
"enabled": "false",
"email": "",
@@ -42,7 +41,8 @@ DEFAULTS = {"python": "/usr/bin/python3.4",
"host": "0.0.0.0",
"port": "5000",
"crawling_method": "classic",
-}
+ "webzine_root": "/tmp",
+ }
if not ON_HEROKU:
try:
@@ -52,71 +52,64 @@ if not ON_HEROKU:
# load the configuration
config = confparser.SafeConfigParser(defaults=DEFAULTS)
config.read(os.path.join(basedir, "conf/conf.cfg"))
-
- PLATFORM_URL = config.get('misc', 'platform_url')
- ADMIN_EMAIL = config.get('misc', 'admin_email')
- RECAPTCHA_PUBLIC_KEY = config.get('misc', 'recaptcha_public_key')
- RECAPTCHA_PRIVATE_KEY = config.get('misc',
- 'recaptcha_private_key')
- LOG_PATH = config.get('misc', 'log_path')
- PYTHON = config.get('misc', 'python')
- NB_WORKER = config.getint('misc', 'nb_worker')
-
- WHOOSH_ENABLED = True
-
- SQLALCHEMY_DATABASE_URI = config.get('database', 'uri')
-
- HTTP_PROXY = config.get('feedparser', 'http_proxy')
- USER_AGENT = config.get('feedparser', 'user_agent')
- RESOLVE_ARTICLE_URL = config.getboolean('feedparser',
- 'resolve_article_url')
- DEFAULT_MAX_ERROR = config.getint('feedparser',
- 'default_max_error')
- CRAWLING_METHOD = config.get('feedparser', 'crawling_method')
-
- WEBSERVER_DEBUG = config.getboolean('webserver', 'debug')
- WEBSERVER_HOST = config.get('webserver', 'host')
- WEBSERVER_PORT = config.getint('webserver', 'port')
- WEBSERVER_SECRET = config.get('webserver', 'secret')
-
- NOTIFICATION_EMAIL = config.get('notification', 'email')
- NOTIFICATION_HOST = config.get('notification', 'host')
- NOTIFICATION_PORT = config.getint('notification', 'port')
- NOTIFICATION_TLS = config.getboolean('notification', 'tls')
- NOTIFICATION_SSL = config.getboolean('notification', 'ssl')
- NOTIFICATION_USERNAME = config.get('notification', 'username')
- NOTIFICATION_PASSWORD = config.get('notification', 'password')
-
- WEBZINE_ROOT = PATH + "/pyaggr3g470r/var/export/"
-
else:
- PLATFORM_URL = os.environ.get('PLATFORM_URL',
- 'https://pyaggr3g470r.herokuapp.com/')
- ADMIN_EMAIL = os.environ.get('ADMIN_EMAIL', '')
- RECAPTCHA_PUBLIC_KEY = os.environ.get('RECAPTCHA_PUBLIC_KEY', '')
- RECAPTCHA_PRIVATE_KEY = os.environ.get('RECAPTCHA_PRIVATE_KEY', '')
- LOG_PATH = os.environ.get('LOG_PATH', 'pyaggr3g470r.log')
- PYTHON = 'python'
-
- SQLALCHEMY_DATABASE_URI = os.environ['DATABASE_URL']
-
- HTTP_PROXY = ""
- USER_AGENT = "Mozilla/5.0 " \
- "(X11; Debian; Linux x86_64; rv:28.0) Gecko/20100101 Firefox/28.0"
- RESOLVE_ARTICLE_URL = int(os.environ.get('RESOLVE_ARTICLE_URL', 0)) == 1
- DEFAULT_MAX_ERROR = int(os.environ.get('DEFAULT_MAX_ERROR', 6))
- CRAWLING_METHOD = os.environ.get('CRAWLING_METHOD', DEFAULTS['crawling_method'])
-
- WEBSERVER_DEBUG = False
- WEBSERVER_HOST = '0.0.0.0'
- WEBSERVER_PORT = int(os.environ.get('PORT', 5000))
- WEBSERVER_SECRET = os.environ.get('SECRET_KEY', None)
-
- NOTIFICATION_EMAIL = os.environ.get('NOTIFICATION_EMAIL', '')
- POSTMARK_API_KEY = os.environ.get('POSTMARK_API_KEY', '')
-
- WEBZINE_ROOT = "/tmp/"
-
+ class Config(object):
+ def get(self, _, name):
+ return os.environ.get(name.upper(), DEFAULTS.get(name))
+
+ def getint(self, _, name):
+ return int(self.get(_, name))
+
+ def getboolean(self, _, name):
+ value = self.get(_, name)
+ if value == 'true':
+ return True
+ elif value == 'false':
+ return False
+ return None
+ config = Config()
+
+
+PLATFORM_URL = config.get('misc', 'platform_url')
+ADMIN_EMAIL = config.get('misc', 'admin_email')
+RECAPTCHA_PUBLIC_KEY = config.get('misc', 'recaptcha_public_key')
+RECAPTCHA_PRIVATE_KEY = config.get('misc',
+ 'recaptcha_private_key')
+LOG_PATH = config.get('misc', 'log_path')
+PYTHON = config.get('misc', 'python')
+NB_WORKER = config.getint('misc', 'nb_worker')
+
+WHOOSH_ENABLED = True
+
+SQLALCHEMY_DATABASE_URI = config.get('database', 'uri')
+
+HTTP_PROXY = config.get('feedparser', 'http_proxy')
+USER_AGENT = config.get('feedparser', 'user_agent')
+RESOLVE_ARTICLE_URL = config.getboolean('feedparser',
+ 'resolve_article_url')
+DEFAULT_MAX_ERROR = config.getint('feedparser',
+ 'default_max_error')
+CRAWLING_METHOD = config.get('feedparser', 'crawling_method')
+
+LOG_LEVEL = {'debug': logging.DEBUG,
+ 'info': logging.INFO,
+ 'warn': logging.WARN,
+ 'error': logging.ERROR,
+ 'fatal': logging.FATAL}[config.get('misc', 'log_level')]
+
+WEBSERVER_HOST = config.get('webserver', 'host')
+WEBSERVER_PORT = config.getint('webserver', 'port')
+WEBSERVER_SECRET = config.get('webserver', 'secret')
+
+NOTIFICATION_EMAIL = config.get('notification', 'email')
+NOTIFICATION_HOST = config.get('notification', 'host')
+NOTIFICATION_PORT = config.getint('notification', 'port')
+NOTIFICATION_TLS = config.getboolean('notification', 'tls')
+NOTIFICATION_SSL = config.getboolean('notification', 'ssl')
+NOTIFICATION_USERNAME = config.get('notification', 'username')
+NOTIFICATION_PASSWORD = config.get('notification', 'password')
+
+WEBZINE_ROOT = config.get('webserver', 'webzine_root')
CSRF_ENABLED = True
# slow database query threshold (in seconds)
diff --git a/conf/conf.cfg-sample b/conf/conf.cfg-sample
index 159af449..c594cd4a 100644
--- a/conf/conf.cfg-sample
+++ b/conf/conf.cfg-sample
@@ -6,6 +6,7 @@ recaptcha_private_key =
log_path = ./pyaggr3g470r/var/pyaggr3g470r.log
python = python3.3
nb_worker = 5
+log_level = info
[database]
uri = postgres://pgsqluser:pgsqlpwd@127.0.0.1:5432/aggregator
[feedparser]
@@ -15,7 +16,6 @@ resolve_article_url = false
default_max_error = 6
crawling_method = classic
[webserver]
-debug = true
host = 0.0.0.0
port = 5000
secret = a secret only you know
diff --git a/pyaggr3g470r/controllers/abstract.py b/pyaggr3g470r/controllers/abstract.py
index 8f0a8e3f..9a9004af 100644
--- a/pyaggr3g470r/controllers/abstract.py
+++ b/pyaggr3g470r/controllers/abstract.py
@@ -1,5 +1,6 @@
import logging
from bootstrap import db
+from sqlalchemy import or_
from werkzeug.exceptions import Forbidden, NotFound
logger = logging.getLogger(__name__)
@@ -25,13 +26,13 @@ class AbstractController(object):
each parameters of the function is treated as an equality unless the
name of the parameter ends with either "__gt", "__lt", "__ge", "__le",
- "__ne" or "__in".
+ "__ne", "__in" ir "__like".
"""
- if self.user_id is not None:
- filters[self._user_id_key] = self.user_id
db_filters = set()
for key, value in filters.items():
- if key.endswith('__gt'):
+ if key == '__or__':
+ db_filters.add(or_(*self._to_filters(**value)))
+ elif key.endswith('__gt'):
db_filters.add(getattr(self._db_cls, key[:-4]) > value)
elif key.endswith('__lt'):
db_filters.add(getattr(self._db_cls, key[:-4]) < value)
@@ -43,11 +44,15 @@ class AbstractController(object):
db_filters.add(getattr(self._db_cls, key[:-4]) != value)
elif key.endswith('__in'):
db_filters.add(getattr(self._db_cls, key[:-4]).in_(value))
+ elif key.endswith('__like'):
+ db_filters.add(getattr(self._db_cls, key[:-6]).like(value))
else:
db_filters.add(getattr(self._db_cls, key) == value)
return db_filters
def _get(self, **filters):
+ if self.user_id is not None:
+ filters[self._user_id_key] = self.user_id
return self._db_cls.query.filter(*self._to_filters(**filters))
def get(self, **filters):
diff --git a/pyaggr3g470r/models/feed.py b/pyaggr3g470r/models/feed.py
index a36d9573..aff11460 100644
--- a/pyaggr3g470r/models/feed.py
+++ b/pyaggr3g470r/models/feed.py
@@ -36,7 +36,7 @@ class Feed(db.Model):
Represent a feed.
"""
id = db.Column(db.Integer, primary_key=True)
- title = db.Column(db.String(), default="No title")
+ title = db.Column(db.String(), default="")
description = db.Column(db.String(), default="FR")
link = db.Column(db.String())
site_link = db.Column(db.String(), default="")
diff --git a/pyaggr3g470r/templates/home.html b/pyaggr3g470r/templates/home.html
index 461b6928..a31d65e1 100644
--- a/pyaggr3g470r/templates/home.html
+++ b/pyaggr3g470r/templates/home.html
@@ -14,7 +14,7 @@
{% if not feed_id %}</b>{% endif %}
</a></li>
{% for fid, nbunread in unread|dictsort(by='value')|reverse %}
- <li class="feed-menu"><a href="{{ gen_url(feed=fid) }}">
+ <li class="feed-menu"><a href="{{ gen_url(feed_id=fid) }}">
{% if feed_id == fid %}<b>{% endif %}
{% if in_error.get(fid, 0) > 0 %}
<span style="background-color: {{ "red" if in_error[fid] > conf.DEFAULT_MAX_ERROR -1 else "orange" }} ;" class="badge pull-right" title="Some errors occured while trying to retrieve that feed.">{{ in_error[fid] }} {{ _("error") }}{% if in_error[fid] > 1 %}s{% endif %}</span>
@@ -33,7 +33,7 @@
</span></li>
{% endfor %}
{% for fid, ftitle in feeds|dictsort(case_sensitive=False, by='value') if not fid in unread %}
- <li class="feed-menu"><a href="{{ gen_url(feed=fid) }}">
+ <li class="feed-menu"><a href="{{ gen_url(feed_id=fid) }}">
{% if in_error.get(fid, 0) > 0 %}
<span style="background-color: {{ "red" if in_error[fid] > conf.DEFAULT_MAX_ERROR - 1 else "orange" }} ;" class="badge pull-right" title="Some errors occured while trying to retrieve that feed.">{{ in_error[fid] }} {{ _("error") }}{% if in_error[fid] > 1 %}s{% endif %}</span>
{% endif %}
@@ -73,9 +73,9 @@
<thead>
<tr>
<th></th>
- <th><a href="{{ gen_url(sort_='feed') }}">{{ _('Feed') }}</a></th>
+ <th><a href="{{ gen_url(sort_='-feed' if sort_ == 'feed' else 'feed') }}">{{ _('Feed') }}</a></th>
<th>{{ _('Article') }}</th>
- <th><a href="{{ gen_url(sort_='date') }}">{{ _('Date') }}</a></th>
+ <th><a href="{{ gen_url(sort_='-date' if sort_ == 'date' else 'date') }}">{{ _('Date') }}</a></th>
</tr>
</thead>
<tbody>
diff --git a/pyaggr3g470r/templates/layout.html b/pyaggr3g470r/templates/layout.html
index 484bbdc7..a54125ea 100644
--- a/pyaggr3g470r/templates/layout.html
+++ b/pyaggr3g470r/templates/layout.html
@@ -86,6 +86,7 @@
<li><a href="{{ url_for("profile") }}"><span class="glyphicon glyphicon-user"></span> {{ _('Profile') }}</a></li>
<li><a href="{{ url_for("management") }}"><span class="glyphicon glyphicon-cog"></span> {{ _('Your data') }}</a></li>
{% if g.user.is_admin() %}
+ <li><a href="{{ url_for("about") }}"><span class="glyphicon glyphicon-question-sign"></span> {{ _('About') }}</a></li>
<li role="presentation" class="divider"></li>
<li><a href="{{ url_for("dashboard") }}"><span class="glyphicon glyphicon-dashboard"></span> {{ _('Dashboard') }}</a></li>
<li role="presentation" class="divider"></li>
@@ -101,9 +102,16 @@
</a>
<ul class="dropdown-menu">
<li>
- <form class="navbar-form" method=get action="/search" role="search">
+ <form class="navbar-form" method=get action="{{ url_for("search") }}" role="search">
<div class="input-group">
- <input type="text" class="form-control" name="query" placeholder="Search">
+ {% if filter_ %}<input type="hidden" name="filter_" value="{{ filter_ }}" />{% endif %}
+ {% if limit %}<input type="hidden" name="limit" value="{{ limit }}" />{% endif %}
+ {% if feed_id %}<input type="hidden" name="feed_id" value="{{ feed_id }}" />{% endif %}
+ <label for="search_title">{{ _("Title") }}</label>
+ <input type="checkbox" name="search_title" {% if search_title == 'on' or not (search_title == 'on' or search_content == 'on') %}checked{%endif%}/>
+ <label for="search_content">{{ _("Content") }}</label>
+ <input type="checkbox" name="search_content" {% if search_content == 'on' %}checked{%endif%}/>
+ <input type="text" class="form-control" name="query" placeholder="{{ _("Search") }}" {% if search_query %} value="{{ search_query }}"{% endif %} />
</div>
</form>
</li>
diff --git a/pyaggr3g470r/templates/search.html b/pyaggr3g470r/templates/search.html
deleted file mode 100644
index d3a1558d..00000000
--- a/pyaggr3g470r/templates/search.html
+++ /dev/null
@@ -1,48 +0,0 @@
- {% extends "layout.html" %}
-{% block content %}
-<div class="container">
- {% if feeds|count == 0 %}
- <div class="page-header">
- <h1>No results</h1>
- </div>
- {% else %}
- <div class="page-header">
- <h1>{{ nb_articles }} {% if nb_articles !=1 %} results {% else %} result {% endif %} for <i>{{ query }}</i></h1>
- </div>
- {% for feed in feeds|sort(attribute="title") %}
- <div class="row">
- <div class="col-md-6 col-md-offset-3">
- <h1>{{ feed.title|safe }}</h1>
- <a href="/articles/{{ feed.id }}/100"><i class="glyphicon glyphicon-th-list" title="More articles"></i></a>
- <a href="/feed/{{ feed.id }}"><i class="glyphicon glyphicon-info-sign" title="Details"></i></a>
- <a href="/edit_feed/{{ feed.id }}"><i class="glyphicon glyphicon-edit" title="Edit this feed"></i></a>
- </div>
- </div>
- {% for number in range(0, feed.articles|length-(feed.articles|length % 3), 3) %}
- <div class="row">
- {% for n in range(number, number+3) %}
- <div class="col-xs-6 col-sm-4 col-md-4">
- {% if feed.articles[n].readed %}<h3>{% else %}<h1>{% endif %}
- <a href="/article/{{ feed.articles[n].id }}">{{ feed.articles[n].title|safe }}</a>
- {% if feed.articles[n].readed %}</h3>{% else %}</h1>{% endif %}
- <h6>{{ feed.articles[n].date }}</h6>
- </div>
- {% endfor %}
- </div>
- {% endfor %}
- {% if feed.articles|length % 3 != 0 %}
- <div class="row">
- {% for n in range(feed.articles|length-(feed.articles|length % 3), feed.articles|length) %}
- <div class="col-xs-6 col-sm-4 col-md-4">
- {% if feed.articles[n].readed %}<h3>{% else %}<h1>{% endif %}
- <a href="/article/{{ feed.articles[n].id }}">{{ feed.articles[n].title|safe }}</a>
- {% if feed.articles[n].readed %}</h3>{% else %}</h1>{% endif %}
- <h6>{{ feed.articles[n].date }}</h6>
- </div>
- {% endfor %}
- </div>
- {% endif %}
- {% endfor %}
- {% endif %}
-</div><!-- /.container -->
-{% endblock %}
diff --git a/pyaggr3g470r/views/views.py b/pyaggr3g470r/views/views.py
index d0c06da0..3b018c6d 100644
--- a/pyaggr3g470r/views/views.py
+++ b/pyaggr3g470r/views/views.py
@@ -235,13 +235,10 @@ def signup():
return render_template('signup.html', form=form)
-@app.route('/')
-@login_required
-def home(favorites=False):
- """
- Home page for connected users. Displays by default unread articles.
- """
- head_title = gettext('Favorites') if favorites else ''
+
+def render_home(filters=None, head_title='', page_to_render='home', **kwargs):
+ if filters is None:
+ filters = {}
feed_contr = FeedController(g.user.id)
arti_contr = ArticleController(g.user.id)
feeds = {feed.id: feed.title for feed in feed_contr.read()}
@@ -250,48 +247,83 @@ def home(favorites=False):
in_error = {feed.id: feed.error_count for feed in
feed_contr.read(error_count__gt=2)}
- filter_ = request.args.get('filter_', 'all' if favorites else 'unread')
+ filter_ = request.args.get('filter_',
+ 'unread' if page_to_render == 'home' else 'all')
sort_ = request.args.get('sort_', 'date')
- feed_id = int(request.args.get('feed', 0))
+ feed_id = int(request.args.get('feed_id', 0))
limit = request.args.get('limit', 1000)
- filters = {}
- if favorites:
- filters['like'] = True
if filter_ != 'all':
filters['readed'] = filter_ == 'read'
if feed_id:
filters['feed_id'] = feed_id
- if head_title:
- head_title += ' - '
- head_title += feed_contr.get(id=feed_id).title
+ head_title = "%s%s" % (feed_contr.get(id=feed_id).title,
+ (' - %s' % head_title) if head_title else '')
- articles = arti_contr.read(**filters).order_by(Article.date.desc())
+ sort_param = {"feed": Article.title.desc(),
+ "date": Article.date.desc(),
+ "-feed": Article.title.asc(),
+ "-date": Article.date.asc(),
+ }.get(sort_, Article.date.desc())
+
+ articles = arti_contr.read(**filters).order_by(sort_param)
if limit != 'all':
limit = int(limit)
articles = articles.limit(limit)
- def gen_url(filter_=filter_, sort_=sort_, limit=limit, feed=feed_id):
- return url_for('favorites' if favorites else 'home',
- filter_=filter_, sort_=sort_, limit=limit, feed=feed)
+ def gen_url(filter_=filter_, sort_=sort_, limit=limit, feed_id=feed_id,
+ **kwargs):
+ if page_to_render == 'search':
+ kwargs['query'] = request.args.get('query', '')
+ kwargs['search_title'] = request.args.get('search_title', 'on')
+ kwargs['search_content'] = request.args.get('searc_content', 'off')
+ return url_for(page_to_render, filter_=filter_, sort_=sort_,
+ limit=limit, feed_id=feed_id, **kwargs)
articles = list(articles)
- if not articles and not favorites and feed_id:
+ if (page_to_render == 'home' and feed_id or page_to_render == 'search') \
+ and filter_ != 'all' and not articles:
return redirect(gen_url(filter_='all'))
- if sort_ == "feed":
- articles.sort(key=lambda article: article.source.title)
-
return render_template('home.html', gen_url=gen_url, feed_id=feed_id,
filter_=filter_, limit=limit, feeds=feeds,
unread=unread, articles=articles, in_error=in_error,
- head_title=head_title, favorites=favorites)
+ head_title=head_title, sort_=sort_, **kwargs)
+
+
+@app.route('/')
+@login_required
+def home():
+ "Home page for connected users. Displays by default unread articles."
+ return render_home()
@app.route('/favorites')
@login_required
def favorites():
- return home(favorites=True)
+ return render_home({'like': True}, gettext('Favorites'), 'favorites')
+
+
+@app.route('/search', methods=['GET'])
+@login_required
+def search():
+ "Search articles corresponding to the query."
+ if 'query' not in request.args:
+ flash(gettext("No text to search were provided."), "warning")
+ return render_home()
+ query = request.args['query']
+ filters = {}
+ search_title = request.args.get('search_title')
+ search_content = request.args.get('search_content')
+ if search_title == 'on':
+ filters['title__like'] = "%%%s%%" % query
+ if search_content == 'on':
+ filters['content__like'] = "%%%s%%" % query
+ if len(filters) > 1:
+ filters = {"__or__": filters}
+ return render_home(filters, "%s %s" % (gettext('Search:'), query),
+ 'search', search_query=query, search_title=search_title,
+ search_content=search_content)
@app.route('/fetch', methods=['GET'])
@@ -472,38 +504,6 @@ def export_opml():
response.headers['Content-Disposition'] = 'attachment; filename=feeds.opml'
return response
-@app.route('/search', methods=['GET'])
-@login_required
-def search():
- """
- Search articles corresponding to the query.
- """
- if conf.ON_HEROKU:
- flash(gettext("Full text search is not yet implemented for Heroku."), "warning")
- return redirect(url_for('home'))
- user = User.query.filter(User.id == g.user.id).first()
-
- search_result, result = [], []
- nb_articles = 0
-
- query = request.args.get('query', None)
- if query is not None:
- try:
- search_result, nb_articles = fastsearch.search(user.id, query)
- except Exception as e:
- flash(gettext('An error occured') + ' (%s).' % e, 'danger')
- light_feed = namedtuple('Feed', ['id', 'title', 'articles'], verbose=False, rename=False)
- for feed_id in search_result:
- for feed in user.feeds:
- if feed.id == feed_id:
- articles = []
- for article_id in search_result[feed_id]:
- current_article = Article.query.filter(Article.user_id == g.user.id, Article.id == article_id).first()
- articles.append(current_article)
- articles = sorted(articles, key=lambda t: t.date, reverse=True)
- result.append(light_feed(feed.id, feed.title, articles))
- break
- return render_template('search.html', feeds=result, nb_articles=nb_articles, query=query)
@app.route('/management', methods=['GET', 'POST'])
@login_required
diff --git a/runserver.py b/runserver.py
index a80b0c39..5f20ddd4 100755
--- a/runserver.py
+++ b/runserver.py
@@ -18,6 +18,7 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import logging
import calendar
from bootstrap import conf, application, populate_g
from flask.ext.babel import Babel
@@ -55,4 +56,4 @@ with application.app_context():
if __name__ == '__main__':
application.run(host=conf.WEBSERVER_HOST,
port=conf.WEBSERVER_PORT,
- debug=conf.WEBSERVER_DEBUG)
+ debug=conf.LOG_LEVEL <= logging.DEBUG)
bgstack15