diff options
Diffstat (limited to 'src/web/controllers')
-rw-r--r-- | src/web/controllers/__init__.py | 5 | ||||
-rw-r--r-- | src/web/controllers/abstract.py | 15 | ||||
-rw-r--r-- | src/web/controllers/article.py | 41 | ||||
-rw-r--r-- | src/web/controllers/category.py | 12 | ||||
-rw-r--r-- | src/web/controllers/feed.py | 42 | ||||
-rw-r--r-- | src/web/controllers/user.py | 25 |
6 files changed, 120 insertions, 20 deletions
diff --git a/src/web/controllers/__init__.py b/src/web/controllers/__init__.py index ad77fa1d..a1b89ea8 100644 --- a/src/web/controllers/__init__.py +++ b/src/web/controllers/__init__.py @@ -1,8 +1,9 @@ from .feed import FeedController +from .category import CategoryController from .article import ArticleController from .user import UserController from .icon import IconController -__all__ = ['FeedController', 'ArticleController', 'UserController', - 'IconController'] +__all__ = ['FeedController', 'CategoryController', 'ArticleController', + 'UserController', 'IconController'] diff --git a/src/web/controllers/abstract.py b/src/web/controllers/abstract.py index f33d241e..828e6a29 100644 --- a/src/web/controllers/abstract.py +++ b/src/web/controllers/abstract.py @@ -1,7 +1,7 @@ import logging from flask import g from bootstrap import db -from sqlalchemy import or_ +from sqlalchemy import or_, func from werkzeug.exceptions import Forbidden, NotFound logger = logging.getLogger(__name__) @@ -83,12 +83,12 @@ class AbstractController(object): return obj def create(self, **attrs): + if self._user_id_key is not None and self._user_id_key not in attrs: + attrs[self._user_id_key] = self.user_id assert self._user_id_key is None or self._user_id_key in attrs \ - or self.user_id is not None, \ + or self.user_id is None, \ "You must provide user_id one way or another" - if self._user_id_key is not None and self._user_id_key not in attrs: - attrs[self._user_id_key] = self.user_id obj = self._db_cls(**attrs) db.session.add(obj) db.session.commit() @@ -114,3 +114,10 @@ class AbstractController(object): return True return self.user_id is None \ or getattr(obj, self._user_id_key, None) == self.user_id + + def _count_by(self, elem_to_group_by, filters): + if self.user_id: + filters['user_id'] = self.user_id + return dict(db.session.query(elem_to_group_by, func.count('id')) + .filter(*self._to_filters(**filters)) + .group_by(elem_to_group_by).all()) diff --git a/src/web/controllers/article.py b/src/web/controllers/article.py index 8b6926b7..bc9ef36e 100644 --- a/src/web/controllers/article.py +++ b/src/web/controllers/article.py @@ -1,6 +1,8 @@ import re import logging +import sqlalchemy from sqlalchemy import func +from collections import Counter from bootstrap import db from .abstract import AbstractController @@ -13,12 +15,6 @@ logger = logging.getLogger(__name__) class ArticleController(AbstractController): _db_cls = Article - def get(self, **filters): - article = super(ArticleController, self).get(**filters) - if not article.readed: - self.update({'id': article.id}, {'readed': True}) - return article - def challenge(self, ids): """Will return each id that wasn't found in the database.""" for id_ in ids: @@ -26,16 +22,14 @@ class ArticleController(AbstractController): continue yield id_ + def count_by_category(self, **filters): + return self._count_by(Article.category_id, filters) + def count_by_feed(self, **filters): - if self.user_id: - filters['user_id'] = self.user_id - return dict(db.session.query(Article.feed_id, func.count(Article.id)) - .filter(*self._to_filters(**filters)) - .group_by(Article.feed_id).all()) + return self._count_by(Article.feed_id, filters) def count_by_user_id(self, **filters): - return dict(db.session.query(Article.user_id, - func.count(Article.id)) + return dict(db.session.query(Article.user_id, func.count(Article.id)) .filter(*self._to_filters(**filters)) .group_by(Article.user_id).all()) @@ -46,7 +40,7 @@ class ArticleController(AbstractController): attrs.get('user_id', self.user_id)).get(id=attrs['feed_id']) if 'user_id' in attrs: assert feed.user_id == attrs['user_id'] or self.user_id is None - attrs['user_id'] = feed.user_id + attrs['user_id'], attrs['category_id'] = feed.user_id, feed.category_id # handling feed's filters for filter_ in feed.filters or []: @@ -71,3 +65,22 @@ class ArticleController(AbstractController): attrs['link']) return super().create(**attrs) + + def get_history(self, year=None, month=None): + """ + Sort articles by year and month. + """ + articles_counter = Counter() + articles = self.read() + if year is not None: + articles = articles.filter( + sqlalchemy.extract('year', Article.date) == year) + if month is not None: + articles = articles.filter( + sqlalchemy.extract('month', Article.date) == month) + for article in articles.all(): + if year is not None: + articles_counter[article.date.month] += 1 + else: + articles_counter[article.date.year] += 1 + return articles_counter, articles diff --git a/src/web/controllers/category.py b/src/web/controllers/category.py new file mode 100644 index 00000000..fef5ca81 --- /dev/null +++ b/src/web/controllers/category.py @@ -0,0 +1,12 @@ +from .abstract import AbstractController +from web.models import Category +from .feed import FeedController + + +class CategoryController(AbstractController): + _db_cls = Category + + def delete(self, obj_id): + FeedController(self.user_id).update({'category_id': obj_id}, + {'category_id': None}) + return super().delete(obj_id) diff --git a/src/web/controllers/feed.py b/src/web/controllers/feed.py index 15be8663..31a1ec41 100644 --- a/src/web/controllers/feed.py +++ b/src/web/controllers/feed.py @@ -20,12 +20,14 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import logging +import itertools from datetime import datetime, timedelta import conf from .abstract import AbstractController from .icon import IconController from web.models import Feed +from web.lib.utils import clear_string logger = logging.getLogger(__name__) DEFAULT_LIMIT = 5 @@ -54,6 +56,40 @@ class FeedController(AbstractController): {'last_retrieved': now}) return feeds + def get_duplicates(self, feed_id): + """ + Compare a list of documents by pair. + Pairs of duplicates are sorted by "retrieved date". + """ + feed = self.get(id=feed_id) + duplicates = [] + for pair in itertools.combinations(feed.articles, 2): + date1, date2 = pair[0].date, pair[1].date + if clear_string(pair[0].title) == clear_string(pair[1].title) \ + and (date1 - date2) < timedelta(days=1): + if pair[0].retrieved_date < pair[1].retrieved_date: + duplicates.append((pair[0], pair[1])) + else: + duplicates.append((pair[1], pair[0])) + return feed, duplicates + + def get_inactives(self, nb_days): + today = datetime.now() + inactives = [] + for feed in self.read(): + try: + last_post = feed.articles[0].date + except IndexError: + continue + elapsed = today - last_post + if elapsed > timedelta(days=nb_days): + inactives.append((feed, elapsed)) + inactives.sort(key=lambda tup: tup[1], reverse=True) + return inactives + + def count_by_category(self, **filters): + return self._count_by(Feed.category_id, filters) + def _ensure_icon(self, attrs): if not attrs.get('icon_url'): return @@ -66,5 +102,11 @@ class FeedController(AbstractController): return super().create(**attrs) def update(self, filters, attrs): + from .article import ArticleController self._ensure_icon(attrs) + if 'category_id' in attrs: + art_contr = ArticleController(self.user_id) + for feed in self.read(**filters): + art_contr.update({'feed_id': feed.id}, + {'category_id': attrs['category_id']}) return super().update(filters, attrs) diff --git a/src/web/controllers/user.py b/src/web/controllers/user.py index 3f96b185..d8bf1fa1 100644 --- a/src/web/controllers/user.py +++ b/src/web/controllers/user.py @@ -1,3 +1,6 @@ +import random +import hashlib +from werkzeug import generate_password_hash from .abstract import AbstractController from web.models import User @@ -5,3 +8,25 @@ from web.models import User class UserController(AbstractController): _db_cls = User _user_id_key = 'id' + + def unset_activation_key(self, obj_id): + self.update({'id': obj_id}, {'activation_key': ""}) + + def set_activation_key(self, obj_id): + key = str(random.getrandbits(256)).encode("utf-8") + key = hashlib.sha512(key).hexdigest()[:86] + self.update({'id': obj_id}, {'activation_key': key}) + + def _handle_password(self, attrs): + if attrs.get('password'): + attrs['pwdhash'] = generate_password_hash(attrs.pop('password')) + elif 'password' in attrs: + del attrs['password'] + + def create(self, **attrs): + self._handle_password(attrs) + return super().create(**attrs) + + def update(self, filters, attrs): + self._handle_password(attrs) + return super().update(filters, attrs) |