aboutsummaryrefslogtreecommitdiff
path: root/pyaggr3g470r/views
diff options
context:
space:
mode:
Diffstat (limited to 'pyaggr3g470r/views')
-rw-r--r--pyaggr3g470r/views/api/article.py3
-rw-r--r--pyaggr3g470r/views/api/common.py26
-rw-r--r--pyaggr3g470r/views/api/feed.py9
-rw-r--r--pyaggr3g470r/views/article.py10
-rw-r--r--pyaggr3g470r/views/feed.py11
-rw-r--r--pyaggr3g470r/views/views.py45
6 files changed, 61 insertions, 43 deletions
diff --git a/pyaggr3g470r/views/api/article.py b/pyaggr3g470r/views/api/article.py
index 17881412..c3ec2d34 100644
--- a/pyaggr3g470r/views/api/article.py
+++ b/pyaggr3g470r/views/api/article.py
@@ -1,3 +1,6 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -
+
from flask import g
import dateutil.parser
diff --git a/pyaggr3g470r/views/api/common.py b/pyaggr3g470r/views/api/common.py
index 48a0d0ac..b8477d4b 100644
--- a/pyaggr3g470r/views/api/common.py
+++ b/pyaggr3g470r/views/api/common.py
@@ -1,7 +1,10 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -
+
"""For a given resources, classes in the module intend to create the following
routes :
GET resource/<id>
- -> to retreive one
+ -> to retrieve one
POST resource
-> to create one
PUT resource/<id>
@@ -10,7 +13,7 @@ routes :
-> to delete one
GET resources
- -> to retreive several
+ -> to retrieve several
POST resources
-> to create several
PUT resources
@@ -21,7 +24,6 @@ routes :
import json
import logging
import dateutil.parser
-from copy import deepcopy
from functools import wraps
from werkzeug.exceptions import Unauthorized, BadRequest
from flask import request, g, session, Response
@@ -54,13 +56,11 @@ def authenticate(func):
and user.activation_key == "":
g.user = user
logged_in = True
-
if logged_in:
return func(*args, **kwargs)
raise Unauthorized({'WWWAuthenticate': 'Basic realm="Login Required"'})
return wrapper
-
def to_response(func):
"""Will cast results of func as a result, and try to extract
a status_code for the Response object"""
@@ -146,12 +146,16 @@ class PyAggResourceExisting(PyAggAbstractResource):
class PyAggResourceMulti(PyAggAbstractResource):
def get(self):
- """retreive several objects. filters can be set in the payload on the
+ """retrieve several objects. filters can be set in the payload on the
different fields of the object, and a limit can be set in there as well
"""
- if 'application/json' != request.headers.get('Content-Type'):
+ if 'application/json' not in request.headers.get('Content-Type'):
raise BadRequest("Content-Type must be application/json")
- limit = request.json.pop('limit', 10)
+ limit = 10
+ try:
+ limit = request.json.pop('limit', 10)
+ except:
+ return [res for res in self.controller.read().limit(limit)]
if not limit:
return [res for res in self.controller.read(**request.json).all()]
return [res for res in self.controller.read(**request.json).limit(limit)]
@@ -159,7 +163,7 @@ class PyAggResourceMulti(PyAggAbstractResource):
def post(self):
"""creating several objects. payload should be a list of dict.
"""
- if 'application/json' != request.headers.get('Content-Type'):
+ if 'application/json' not in request.headers.get('Content-Type'):
raise BadRequest("Content-Type must be application/json")
status = 201
results = []
@@ -180,7 +184,7 @@ class PyAggResourceMulti(PyAggAbstractResource):
[[obj_id1, {attr1: val1, attr2: val2}]
[obj_id2, {attr1: val1, attr2: val2}]]
"""
- if 'application/json' != request.headers.get('Content-Type'):
+ if 'application/json' not in request.headers.get('Content-Type'):
raise BadRequest("Content-Type must be application/json")
status = 200
results = []
@@ -201,7 +205,7 @@ class PyAggResourceMulti(PyAggAbstractResource):
def delete(self):
"""will delete several objects,
a list of their ids should be in the payload"""
- if 'application/json' != request.headers.get('Content-Type'):
+ if 'application/json' not in request.headers.get('Content-Type'):
raise BadRequest("Content-Type must be application/json")
status = 204
results = []
diff --git a/pyaggr3g470r/views/api/feed.py b/pyaggr3g470r/views/api/feed.py
index 0d83ea43..7d0e2862 100644
--- a/pyaggr3g470r/views/api/feed.py
+++ b/pyaggr3g470r/views/api/feed.py
@@ -1,3 +1,6 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -
+
from flask import g
from pyaggr3g470r.controllers.feed import FeedController, \
@@ -8,7 +11,6 @@ from pyaggr3g470r.views.api.common import PyAggAbstractResource, \
PyAggResourceExisting, \
PyAggResourceMulti
-
FEED_ATTRS = {'title': {'type': str},
'description': {'type': str},
'link': {'type': str},
@@ -20,25 +22,21 @@ FEED_ATTRS = {'title': {'type': str},
'last_error': {'type': str},
'error_count': {'type': int, 'default': 0}}
-
class FeedNewAPI(PyAggResourceNew):
controller_cls = FeedController
attrs = FEED_ATTRS
to_date = ['date', 'last_retrieved']
-
class FeedAPI(PyAggResourceExisting):
controller_cls = FeedController
attrs = FEED_ATTRS
to_date = ['date', 'last_retrieved']
-
class FeedsAPI(PyAggResourceMulti):
controller_cls = FeedController
attrs = FEED_ATTRS
to_date = ['date', 'last_retrieved']
-
class FetchableFeedAPI(PyAggAbstractResource):
controller_cls = FeedController
to_date = ['date', 'last_retrieved']
@@ -49,7 +47,6 @@ class FetchableFeedAPI(PyAggAbstractResource):
return [feed for feed in self.controller.list_fetchable(
**self.reqparse_args())]
-
g.api.add_resource(FeedNewAPI, '/feed', endpoint='feed_new.json')
g.api.add_resource(FeedAPI, '/feed/<int:obj_id>', endpoint='feed.json')
g.api.add_resource(FeedsAPI, '/feeds', endpoint='feeds.json')
diff --git a/pyaggr3g470r/views/article.py b/pyaggr3g470r/views/article.py
index 66cc0f37..08c92686 100644
--- a/pyaggr3g470r/views/article.py
+++ b/pyaggr3g470r/views/article.py
@@ -1,3 +1,6 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -
+
from flask import Blueprint, g, render_template, redirect
from sqlalchemy import desc
@@ -18,10 +21,9 @@ def articles(feed_id=None, nb_articles=-1):
feed.articles = controllers.ArticleController(g.user.id)\
.read(feed_id=feed.id)\
.order_by(desc("Article.date"))
- if len(feed.articles.all()) <= nb_articles:
- nb_articles = -1
- if nb_articles == -1:
- feed.articles = feed.articles.limit(nb_articles)
+ if len(feed.articles.all()) <= nb_articles or nb_articles == -1:
+ nb_articles = int(1e9)
+ feed.articles = feed.articles.limit(nb_articles)
return render_template('articles.html', feed=feed, nb_articles=nb_articles)
diff --git a/pyaggr3g470r/views/feed.py b/pyaggr3g470r/views/feed.py
index 2af502a7..4fe4e5da 100644
--- a/pyaggr3g470r/views/feed.py
+++ b/pyaggr3g470r/views/feed.py
@@ -1,5 +1,9 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -
+
from datetime import datetime
from flask import Blueprint, g, render_template
+from sqlalchemy import desc
from pyaggr3g470r import controllers, utils
from pyaggr3g470r.decorators import pyagg_default_decorator, \
@@ -22,9 +26,9 @@ def feed(feed_id=None):
"Presents detailed information about a feed."
feed = controllers.FeedController(g.user.id).get(id=feed_id)
word_size = 6
- articles = controllers.ArticleController(g.user.id)\
- .read(feed_id=feed_id).all()
- nb_articles = controllers.ArticleController(g.user.id).read().count()
+ articles = controllers.ArticleController(g.user.id) \
+ .read(feed_id=feed_id) \
+ .order_by(desc("Article.date")).all()
top_words = utils.top_words(articles, n=50, size=int(word_size))
tag_cloud = utils.tag_cloud(top_words)
@@ -46,5 +50,4 @@ def feed(feed_id=None):
feed=feed, tag_cloud=tag_cloud,
first_post_date=first_article,
end_post_date=last_article,
- nb_articles=nb_articles,
average=average, delta=delta, elapsed=elapsed)
diff --git a/pyaggr3g470r/views/views.py b/pyaggr3g470r/views/views.py
index 7934eef8..e202ad4d 100644
--- a/pyaggr3g470r/views/views.py
+++ b/pyaggr3g470r/views/views.py
@@ -27,14 +27,13 @@ __copyright__ = "Copyright (c) Cedric Bonhomme"
__license__ = "AGPLv3"
import os
-import json
import string
import random
import hashlib
import datetime
from collections import namedtuple
from bootstrap import application as app, db
-from flask import render_template, request, flash, session, Response, \
+from flask import render_template, request, flash, session, \
url_for, redirect, g, current_app, make_response, jsonify
from flask.ext.login import LoginManager, login_user, logout_user, \
login_required, current_user, AnonymousUserMixin
@@ -47,7 +46,8 @@ from sqlalchemy.exc import IntegrityError
from werkzeug import generate_password_hash
import conf
-from pyaggr3g470r import utils, notifications, export, duplicate
+from pyaggr3g470r import utils, notifications, export
+from pyaggr3g470r import controllers
from pyaggr3g470r.models import User, Feed, Article, Role
from pyaggr3g470r.decorators import feed_access_required
from pyaggr3g470r.forms import SignupForm, SigninForm, AddFeedForm, \
@@ -93,7 +93,7 @@ def before_request():
@login_manager.user_loader
def load_user(email):
# Return an instance of the User model
- return User.query.filter(User.email == email).first()
+ return controllers.UserController(email).get(email=email)
#
@@ -153,7 +153,7 @@ def login():
form = SigninForm()
if form.validate_on_submit():
- user = User.query.filter(User.email == form.email.data).first()
+ user = controllers.UserController(form.email.data).get(email=form.email.data)
login_user(user)
g.user = user
session['email'] = form.email.data
@@ -265,8 +265,12 @@ def fetch(feed_id=None):
Triggers the download of news.
News are downloaded in a separated process, mandatory for Heroku.
"""
- utils.fetch(g.user.id, feed_id)
- flash(gettext("Downloading articles..."), 'info')
+ if not conf.ON_HEROKU or g.user.is_admin():
+ utils.fetch(g.user.id, feed_id)
+ flash(gettext("Downloading articles..."), "info")
+ else:
+ flash(gettext("The manual retrieving of news is only available " +
+ "for administrator, on the Heroku platform."), "info")
return redirect(redirect_url())
@app.route('/about', methods=['GET'])
@@ -378,7 +382,7 @@ def inactives():
List of inactive feeds.
"""
nb_days = int(request.args.get('nb_days', 365))
- user = User.query.filter(User.id == g.user.id).first()
+ user = controllers.UserController(g.user.email).get(email=g.user.email)
today = datetime.datetime.now()
inactives = []
for feed in user.feeds:
@@ -399,7 +403,7 @@ def duplicates(feed_id=None):
"""
feed = Feed.query.filter(Feed.user_id == g.user.id, Feed.id == feed_id).first()
duplicates = []
- duplicates = duplicate.compare_documents(feed)
+ duplicates = utils.compare_documents(feed)
return render_template('duplicates.html', duplicates=duplicates, feed=feed)
@app.route('/index_database', methods=['GET'])
@@ -425,7 +429,7 @@ def export_articles():
"""
Export all articles to HTML or JSON.
"""
- user = User.query.filter(User.id == g.user.id).first()
+ user = controllers.UserController(g.user.email).get(id=g.user.id)
if request.args.get('format') == "HTML":
# Export to HTML
try:
@@ -457,7 +461,7 @@ def export_opml():
"""
Export all feeds to OPML.
"""
- user = User.query.filter(User.id == g.user.id).first()
+ user = controllers.UserController(g.user.email).get(id=g.user.id)
response = make_response(render_template('opml.xml', user=user, now=datetime.datetime.now()))
response.headers['Content-Type'] = 'application/xml'
response.headers['Content-Disposition'] = 'attachment; filename=feeds.opml'
@@ -506,7 +510,7 @@ def management():
if None != request.files.get('opmlfile', None):
# Import an OPML file
data = request.files.get('opmlfile', None)
- if not g.allowed_file(data.filename):
+ if not utils.allowed_file(data.filename):
flash(gettext('File not allowed.'), 'danger')
else:
try:
@@ -519,7 +523,7 @@ def management():
elif None != request.files.get('jsonfile', None):
# Import an account
data = request.files.get('jsonfile', None)
- if not g.allowed_file(data.filename):
+ if not utils.allowed_file(data.filename):
flash(gettext('File not allowed.'), 'danger')
else:
try:
@@ -540,10 +544,15 @@ def management():
not_on_heroku = not conf.ON_HEROKU)
@app.route('/history', methods=['GET'])
+@app.route('/history/<int:year>', methods=['GET'])
+@app.route('/history/<int:year>/<int:month>', methods=['GET'])
@login_required
-def history():
- #user = User.query.filter(User.id == g.user.id).first()
- return render_template('history.html')
+def history(year=None, month=None):
+ articles_counter, articles = utils.history(g.user.id, year, month)
+ return render_template('history.html',
+ articles_counter=articles_counter,
+ articles=articles,
+ year=year, month=month)
@app.route('/bookmarklet', methods=['GET'])
@app.route('/create_feed', methods=['GET', 'POST'])
@@ -628,7 +637,7 @@ def profile():
"""
Edit the profile of the currently logged user.
"""
- user = User.query.filter(User.email == g.user.email).first()
+ user = controllers.UserController(g.user.email).get(id=g.user.id)
form = ProfileForm()
if request.method == 'POST':
@@ -654,7 +663,7 @@ def delete_account():
"""
Delete the account of the user (with all its data).
"""
- user = User.query.filter(User.email == g.user.email).first()
+ user = controllers.UserController(g.user.email).get(id=g.user.id)
if user is not None:
db.session.delete(user)
db.session.commit()
bgstack15