aboutsummaryrefslogtreecommitdiff
path: root/src/web/controllers
diff options
context:
space:
mode:
Diffstat (limited to 'src/web/controllers')
-rw-r--r--src/web/controllers/__init__.py5
-rw-r--r--src/web/controllers/abstract.py15
-rw-r--r--src/web/controllers/article.py41
-rw-r--r--src/web/controllers/category.py12
-rw-r--r--src/web/controllers/feed.py42
-rw-r--r--src/web/controllers/user.py25
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)
bgstack15