[odrs-web/production] Convert to a more flask-like structure
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [odrs-web/production] Convert to a more flask-like structure
- Date: Thu, 6 Jul 2017 13:31:10 +0000 (UTC)
commit e93cd4df537aac78ff92723c1f940d29b3b21fa7
Author: Richard Hughes <richard hughsie com>
Date: Thu Jul 6 14:29:56 2017 +0100
Convert to a more flask-like structure
app/__init__.py | 48 ++
database.py => app/db.py | 614 +++++++++++++------------
app/models.py | 61 +++
{static => app/static}/Chart.js | 0
{static => app/static}/app-page.png | Bin 94032 -> 94032 bytes
{static => app/static}/bar.png | Bin 154 -> 154 bytes
{static => app/static}/favicon.ico | Bin 2550 -> 2550 bytes
{static => app/static}/foot.png | Bin 699 -> 699 bytes
{static => app/static}/general_bg.png | Bin 178 -> 178 bytes
{static => app/static}/general_separator.png | Bin 212 -> 212 bytes
{static => app/static}/gnome-16.png | Bin 650 -> 650 bytes
{static => app/static}/gnome-odrs.png | Bin 14485 -> 14485 bytes
{static => app/static}/layout.css | 0
{static => app/static}/review-submit.png | Bin 25010 -> 25010 bytes
{static => app/static}/search-icon.png | Bin 395 -> 395 bytes
{static => app/static}/style.css | 0
{static => app/static}/t.png | Bin 317 -> 317 bytes
{static => app/static}/top_bar-bg.png | Bin 185 -> 185 bytes
{templates => app/templates}/default.html | 1 -
{templates => app/templates}/delete.html | 0
{templates => app/templates}/distros.html | 0
{templates => app/templates}/error.html | 0
{templates => app/templates}/graph-month.html | 0
{templates => app/templates}/graph-year.html | 0
{templates => app/templates}/index.html | 0
{templates => app/templates}/login.html | 0
{templates => app/templates}/oars.html | 0
{templates => app/templates}/show-all.html | 0
{templates => app/templates}/show.html | 0
{templates => app/templates}/stats.html | 0
{templates => app/templates}/users.html | 0
api10.py => app/views.py | 217 ++++++----
admin.py => app/views_admin.py | 176 ++++----
event.py | 15 -
flaskapp.py | 74 +---
odrs.wsgi | 2 +-
review.py | 23 -
user.py | 31 --
38 files changed, 644 insertions(+), 618 deletions(-)
---
diff --git a/app/__init__.py b/app/__init__.py
new file mode 100644
index 0000000..861a48f
--- /dev/null
+++ b/app/__init__.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python2
+# -*- coding: utf-8 -*-
+#
+# pylint: disable=invalid-name,missing-docstring
+#
+# Copyright (C) 2015-2017 Richard Hughes <richard hughsie com>
+# Licensed under the GNU General Public License Version 2
+
+import os
+
+from flask import Flask, flash, render_template, g
+from flask_login import LoginManager
+
+from .db import Database
+
+app = Flask(__name__)
+app.config.from_object(__name__)
+app.secret_key = os.environ['ODRS_REVIEWS_SECRET']
+
+lm = LoginManager()
+lm.init_app(app)
+
+def get_db():
+ db = getattr(g, '_database', None)
+ if db is None:
+ db = g.db = Database(app)
+ return db
+
+@app.teardown_appcontext
+def teardown_request(exception):
+ db = getattr(g, 'db', None)
+ if db is not None:
+ db.close()
+
+@lm.user_loader
+def load_user(user_id):
+ db = get_db()
+ user = db.users.get_by_id(user_id)
+ return user
+
+@app.errorhandler(404)
+def error_page_not_found(msg=None):
+ """ Error handler: File not found """
+ flash(msg)
+ return render_template('error.html'), 404
+
+from app import views
+from app import views_admin
diff --git a/database.py b/app/db.py
similarity index 91%
rename from database.py
rename to app/db.py
index 920cc05..a4a68ed 100644
--- a/database.py
+++ b/app/db.py
@@ -1,18 +1,19 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
-# Copyright (C) 2016 Richard Hughes <richard hughsie com>
+# pylint: disable=invalid-name,missing-docstring
+#
+# Copyright (C) 2016-2017 Richard Hughes <richard hughsie com>
# Licensed under the GNU General Public License Version 3
-import pymysql as mdb
-import pymysql.cursors
+import os
import cgi
import datetime
import hashlib
-from user import OdrsUser
-from event import OdrsEvent
-from review import OdrsReview
+import pymysql as mdb
+
+from .models import User, Event, Review
class CursorError(Exception):
def __init__(self, cur, e):
@@ -22,7 +23,7 @@ class CursorError(Exception):
def _create_review(e):
""" Parse a review """
- review = OdrsReview()
+ review = Review()
review.review_id = int(e[0])
review.date_created = int(e[1].strftime("%s"))
review.app_id = e[2]
@@ -43,7 +44,7 @@ def _create_review(e):
def _create_event(e):
""" Parse an event """
- event = OdrsEvent()
+ event = Event()
event.eventlog_id = int(e[0])
event.date_created = int(e[1].strftime("%s"))
event.user_addr = e[2]
@@ -55,7 +56,7 @@ def _create_event(e):
def _create_user(e):
""" Parse a user """
- user = OdrsUser()
+ user = User()
user.id = int(e[0])
user.date_created = int(e[1].strftime("%s"))
user.user_hash = e[2]
@@ -71,17 +72,16 @@ def _password_hash(value):
def _get_datestr_from_dt(when):
return int("%04i%02i%02i" % (when.year, when.month, when.day))
-class ReviewsDatabase(object):
+class Database(object):
- def __init__(self, environ):
+ def __init__(self, app):
""" Constructor for object """
- assert environ
self._db = None
try:
- if 'MYSQL_DB_HOST' in environ:
- self._db = mdb.connect(environ['MYSQL_DB_HOST'],
- environ['MYSQL_DB_USERNAME'],
- environ['MYSQL_DB_PASSWORD'],
+ if 'MYSQL_DB_HOST' in os.environ:
+ self._db = mdb.connect(os.environ['MYSQL_DB_HOST'],
+ os.environ['MYSQL_DB_USERNAME'],
+ os.environ['MYSQL_DB_PASSWORD'],
'odrs',
use_unicode=True, charset='utf8')
else:
@@ -91,13 +91,243 @@ class ReviewsDatabase(object):
except mdb.Error as e:
print("Error %d: %s" % (e.args[0], e.args[1]))
assert self._db
+ self.users = DatabaseUsers(self._db)
+ self.reviews = DatabaseReviews(self._db)
+ self.eventlog = DatabaseEventlog(self._db)
- def __del__(self):
+ def close(self):
""" Clean up the database """
if self._db:
self._db.close()
- def review_modify(self, review):
+ def analytics_inc_fetch(self, app_id, when=None):
+ """ Increments the fetch count on one specific application """
+ try:
+ cur = self._db.cursor()
+ if not when:
+ when = datetime.date.today()
+ datestr = _get_datestr_from_dt(when)
+ cur.execute("INSERT INTO analytics (datestr,app_id) VALUES (%s, %s) "
+ "ON DUPLICATE KEY UPDATE fetch_cnt=fetch_cnt+1;",
+ (datestr, app_id,))
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+
+ def get_stats_distro(self, limit=10):
+ """ Returns distro stats for reviews """
+ try:
+ cur = self._db.cursor()
+ cur.execute("SELECT DISTINCT(distro), COUNT(distro) AS total "
+ "FROM reviews GROUP BY distro ORDER BY total DESC "
+ "LIMIT %s;",
+ (limit,))
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchall()
+ data = []
+ for en in res:
+ data.append((en[0], en[1]))
+ return data
+
+ def get_stats_fetch(self, msg, limit=50):
+ """ Returns interesting statistics for the webapp """
+ try:
+ cur = self._db.cursor()
+ cur.execute("SELECT DISTINCT app_id, COUNT(app_id) as total "
+ "FROM eventlog WHERE app_id IS NOT NULL "
+ "AND message=%s GROUP BY app_id "
+ "ORDER BY total DESC LIMIT %s;",
+ (msg, limit,))
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchall()
+ data = []
+ for en in res:
+ data.append((en[0], en[1]))
+ return data
+
+ def get_stats(self):
+ """ Returns interesting statistics for the webapp """
+ item = {}
+
+ # get the total number of reviews
+ try:
+ cur = self._db.cursor()
+ cur.execute("SELECT COUNT(*) FROM reviews;")
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchall()
+ item['Active reviews'] = int(res[0][0])
+
+ # unique reviewers
+ try:
+ cur.execute("SELECT COUNT(DISTINCT(user_hash)) FROM reviews;")
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchall()
+ item['Unique reviewers'] = int(res[0][0])
+
+ # total votes
+ try:
+ cur.execute("SELECT COUNT(*) FROM votes WHERE val = 1;")
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchall()
+ item['User upvotes'] = int(res[0][0])
+ try:
+ cur.execute("SELECT COUNT(*) FROM votes WHERE val = -1;")
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchall()
+ item['User downvotes'] = int(res[0][0])
+
+ # unique voters
+ try:
+ cur.execute("SELECT COUNT(DISTINCT(user_hash)) FROM votes;")
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchall()
+ item['Unique voters'] = int(res[0][0])
+
+ # unique languages
+ try:
+ cur.execute("SELECT COUNT(DISTINCT(locale)) FROM reviews;")
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchall()
+ item['Unique languages'] = int(res[0][0])
+
+ # unique distros
+ try:
+ cur.execute("SELECT COUNT(DISTINCT(distro)) FROM reviews;")
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchall()
+ item['Unique distros'] = int(res[0][0])
+
+ # unique apps
+ try:
+ cur.execute("SELECT COUNT(DISTINCT(app_id)) FROM reviews;")
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchall()
+ item['Unique apps reviewed'] = int(res[0][0])
+
+ # unique distros
+ try:
+ cur.execute("SELECT COUNT(*) FROM reviews WHERE reported > 0;")
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchall()
+ item['Reported reviews'] = int(res[0][0])
+
+ # star reviews
+ for star in range(1, 6):
+ try:
+ cur.execute("SELECT COUNT(*) FROM reviews WHERE rating = %s;",
+ (star * 20,))
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchall()
+ item['%i star reviews' % star] = int(res[0][0])
+
+ # done
+ return item
+
+ def get_stats_by_interval(self, size, interval, msg):
+ """ Gets stats data """
+ cnt = []
+ now = datetime.date.today()
+
+ # yes, there's probably a way to do this in one query
+ cur = self._db.cursor()
+ for i in range(size):
+ start = now - datetime.timedelta((i * interval) + interval - 1)
+ end = now - datetime.timedelta((i * interval) - 1)
+ try:
+ cur.execute("SELECT COUNT(*) FROM eventlog "
+ "WHERE message = %s AND date_created BETWEEN %s "
+ "AND %s", (msg, start, end,))
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchone()
+ cnt.append(int(res[0]))
+ return cnt
+
+ def get_analytics_by_interval(self, size, interval):
+ """ Gets analytics data """
+ array = []
+ now = datetime.date.today()
+
+ # yes, there's probably a way to do this in one query
+ cur = self._db.cursor()
+ for i in range(size):
+ start = _get_datestr_from_dt(now - datetime.timedelta((i * interval) + interval - 1))
+ end = _get_datestr_from_dt(now - datetime.timedelta((i * interval) - 1))
+ try:
+ cur.execute("SELECT fetch_cnt FROM analytics WHERE "
+ "datestr BETWEEN %s "
+ "AND %s", (start, end,))
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchall()
+
+ # add all these up
+ tmp = 0
+ for r in res:
+ tmp = tmp + int(r[0])
+ array.append(tmp)
+ return array
+
+ def get_analytics_fetch(self, limit=50):
+ """ Returns interesting statistics for the webapp """
+ try:
+ cur = self._db.cursor()
+ cur.execute("SELECT DISTINCT app_id, SUM(fetch_cnt) AS total "
+ "FROM analytics WHERE app_id IS NOT NULL "
+ "GROUP BY app_id ORDER BY total DESC LIMIT %s;",
+ (limit,))
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchall()
+ data = []
+ for en in res:
+ data.append((en[0], en[1]))
+ return data
+
+class DatabaseEventlog(object):
+
+ def __init__(self, db):
+ """ Constructor for object """
+ self._db = db
+
+ def warn(self,
+ user_addr=None,
+ user_hash=None,
+ app_id=None,
+ message=None,
+ important=True):
+ """ Adds a warning to the event log """
+ try:
+ cur = self._db.cursor()
+ cur.execute("INSERT INTO eventlog (user_addr, user_hash, app_id, "
+ "message, important) "
+ "VALUES (%s, %s, %s, %s, %s);",
+ (user_addr, user_hash, app_id, message, important,))
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+
+ def info(self, user_addr=None, user_hash=None, app_id=None, message=None):
+ """ Adds an info item to the event log """
+ self.warn(user_addr, user_hash, app_id, message, False)
+
+class DatabaseReviews(object):
+
+ def __init__(self, db):
+ """ Constructor for object """
+ self._db = db
+
+ def modify(self, review):
""" Modifies a review """
try:
cur = self._db.cursor()
@@ -121,7 +351,7 @@ class ReviewsDatabase(object):
raise CursorError(cur, e)
return True
- def review_add(self, review, user_addr):
+ def add(self, review, user_addr):
""" Add a review to the database """
try:
cur = self._db.cursor()
@@ -142,7 +372,7 @@ class ReviewsDatabase(object):
except mdb.Error as e:
raise CursorError(cur, e)
- def review_delete(self, review):
+ def delete(self, review):
""" Deletes a review """
try:
cur = self._db.cursor()
@@ -152,7 +382,7 @@ class ReviewsDatabase(object):
raise CursorError(cur, e)
return True
- def review_remove(self, review_id, user_hash):
+ def remove(self, review_id, user_hash):
""" Marks a review as removed """
try:
cur = self._db.cursor()
@@ -163,7 +393,7 @@ class ReviewsDatabase(object):
raise CursorError(cur, e)
return True
- def review_get_for_app_id(self, app_id):
+ def get_for_app_id(self, app_id):
""" Returns all the reviews for an application (for client-side) """
try:
cur = self._db.cursor()
@@ -183,7 +413,7 @@ class ReviewsDatabase(object):
reviews.append(_create_review(e))
return reviews
- def review_get_for_id(self, review_id):
+ def get_for_id(self, review_id):
""" Returns a specific review """
try:
cur = self._db.cursor()
@@ -199,7 +429,7 @@ class ReviewsDatabase(object):
return None
return _create_review(res[0])
- def review_exists(self, app_id, user_hash):
+ def exists(self, app_id, user_hash):
""" Checks to see if a review exists for the application+user """
try:
cur = self._db.cursor()
@@ -213,19 +443,6 @@ class ReviewsDatabase(object):
return True
return False
- def vote_exists(self, review_id, user_hash):
- """ Checks to see if a vote exists for the review+user """
- try:
- cur = self._db.cursor()
- cur.execute("SELECT date_created "
- "FROM votes WHERE review_id=%s AND user_hash=%s;",
- (review_id, user_hash,))
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchone()
- if res is not None:
- return True
- return False
def vote_add(self, review_id, val, user_hash):
""" Votes on a specific review and add to the votes database """
@@ -246,7 +463,21 @@ class ReviewsDatabase(object):
except mdb.Error as e:
raise CursorError(cur, e)
- def review_get_all(self):
+ def vote_exists(self, review_id, user_hash):
+ """ Checks to see if a vote exists for the review+user """
+ try:
+ cur = self._db.cursor()
+ cur.execute("SELECT date_created "
+ "FROM votes WHERE review_id=%s AND user_hash=%s;",
+ (review_id, user_hash,))
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchone()
+ if res is not None:
+ return True
+ return False
+
+ def get_all(self):
""" Gets all non-removed reviews from the server for all applications """
try:
cur = self._db.cursor()
@@ -264,40 +495,55 @@ class ReviewsDatabase(object):
reviews.append(_create_review(e))
return reviews
- def event_warn(self,
- user_addr=None,
- user_hash=None,
- app_id=None,
- message=None,
- important=True):
- """ Adds a warning to the event log """
+ def get_rating_for_app_id(self, app_id, min_total=1):
+ """ Gets the ratings information for the application """
try:
cur = self._db.cursor()
- cur.execute("INSERT INTO eventlog (user_addr, user_hash, app_id, "
- "message, important) "
- "VALUES (%s, %s, %s, %s, %s);",
- (user_addr, user_hash, app_id, message, important,))
+ cur.execute("SELECT COUNT(*) total,"
+ " SUM(rating = 0) star0,"
+ " SUM(rating = 20) star1,"
+ " SUM(rating = 40) star2,"
+ " SUM(rating = 60) star3,"
+ " SUM(rating = 80) star4,"
+ " SUM(rating = 100) star5 "
+ "FROM reviews WHERE app_id = %s "
+ "AND date_deleted=0;", (app_id,))
except mdb.Error as e:
raise CursorError(cur, e)
+ res = cur.fetchone()
+ if not res:
+ return []
+ item = {}
+ item['total'] = int(res[0])
+ if item['total'] < min_total:
+ return []
+ for i in range(6):
+ if res[i + 1]:
+ item['star%i' % i] = int(res[i + 1])
+ else:
+ item['star%i' % i] = 0
+ return item
- def event_info(self, user_addr=None, user_hash=None, app_id=None, message=None):
- """ Adds an info item to the event log """
- self.event_warn(user_addr, user_hash, app_id, message, False)
-
- def analytics_inc_fetch(self, app_id, when=None):
- """ Increments the fetch count on one specific application """
+ def get_all_apps(self):
+ """ Returns interesting statistics for the webapp """
try:
cur = self._db.cursor()
- if not when:
- when = datetime.date.today()
- datestr = _get_datestr_from_dt(when)
- cur.execute("INSERT INTO analytics (datestr,app_id) VALUES (%s, %s) "
- "ON DUPLICATE KEY UPDATE fetch_cnt=fetch_cnt+1;",
- (datestr, app_id,))
+ cur.execute("SELECT DISTINCT(app_id) FROM reviews ORDER BY app_id;")
except mdb.Error as e:
raise CursorError(cur, e)
+ res = cur.fetchall()
+ data = []
+ for en in res:
+ data.append(en[0])
+ return data
- def user_add(self, user_hash):
+class DatabaseUsers(object):
+
+ def __init__(self, db):
+ """ Constructor for object """
+ self._db = db
+
+ def add(self, user_hash):
""" Add a user to the database """
try:
cur = self._db.cursor()
@@ -306,7 +552,7 @@ class ReviewsDatabase(object):
except mdb.Error as e:
raise CursorError(cur, e)
- def user_get_all(self):
+ def get_all(self):
""" Get all the users on the system """
try:
cur = self._db.cursor()
@@ -323,7 +569,7 @@ class ReviewsDatabase(object):
users.append(_create_user(e))
return users
- def user_get_with_login(self, username, password):
+ def get_with_login(self, username, password):
""" Get information about a specific login """
try:
cur = self._db.cursor()
@@ -339,7 +585,7 @@ class ReviewsDatabase(object):
return None
return _create_user(res)
- def user_get_by_id(self, user_hash):
+ def get_by_id(self, user_hash):
""" Get information about a specific user """
try:
cur = self._db.cursor()
@@ -354,7 +600,7 @@ class ReviewsDatabase(object):
return None
return _create_user(res)
- def user_get_by_hash(self, user_hash):
+ def get_by_hash(self, user_hash):
""" Get information about a specific user """
try:
cur = self._db.cursor()
@@ -369,7 +615,7 @@ class ReviewsDatabase(object):
return None
return _create_user(res)
- def get_users_by_karma(self, best=True):
+ def get_by_karma(self, best=True):
""" Returns interesting statistics for the webapp """
try:
cur = self._db.cursor()
@@ -389,13 +635,13 @@ class ReviewsDatabase(object):
data.append(_create_user(res))
return data
- def user_update_karma(self, user_hash, val):
+ def update_karma(self, user_hash, val):
""" Update the request time for a specific user ID """
# if not existing, create it
- user = self.user_get_by_hash(user_hash)
+ user = self.get_by_hash(user_hash)
if not user:
- self.user_add(user_hash)
+ self.add(user_hash)
return
# update the karma value
@@ -406,11 +652,11 @@ class ReviewsDatabase(object):
except mdb.Error as e:
raise CursorError(cur, e)
- def user_ban(self, user_hash):
+ def ban(self, user_hash):
""" Ban a user """
# check it exists
- user = self.user_get_by_hash(user_hash)
+ user = self.get_by_hash(user_hash)
if not user:
return
@@ -421,227 +667,3 @@ class ReviewsDatabase(object):
"WHERE user_hash = %s;", (user_hash,))
except mdb.Error as e:
raise CursorError(cur, e)
-
- def reviews_get_rating_for_app_id(self, app_id, min_total=1):
- """ Gets the ratings information for the application """
- try:
- cur = self._db.cursor()
- cur.execute("SELECT COUNT(*) total,"
- " SUM(rating = 0) star0,"
- " SUM(rating = 20) star1,"
- " SUM(rating = 40) star2,"
- " SUM(rating = 60) star3,"
- " SUM(rating = 80) star4,"
- " SUM(rating = 100) star5 "
- "FROM reviews WHERE app_id = %s "
- "AND date_deleted=0;", (app_id,))
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchone()
- if not res:
- return []
- item = {}
- item['total'] = int(res[0])
- if item['total'] < min_total:
- return []
- for i in range(6):
- if res[i + 1]:
- item['star%i' % i] = int(res[i + 1])
- else:
- item['star%i' % i] = 0
- return item
-
- def get_stats_distro(self, limit=10):
- """ Returns distro stats for reviews """
- try:
- cur = self._db.cursor()
- cur.execute("SELECT DISTINCT(distro), COUNT(distro) AS total "
- "FROM reviews GROUP BY distro ORDER BY total DESC "
- "LIMIT %s;",
- (limit,))
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchall()
- data = []
- for en in res:
- data.append((en[0], en[1]))
- return data
-
- def get_stats_fetch(self, msg, limit=50):
- """ Returns interesting statistics for the webapp """
- try:
- cur = self._db.cursor()
- cur.execute("SELECT DISTINCT app_id, COUNT(app_id) as total "
- "FROM eventlog WHERE app_id IS NOT NULL "
- "AND message=%s GROUP BY app_id "
- "ORDER BY total DESC LIMIT %s;",
- (msg, limit,))
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchall()
- data = []
- for en in res:
- data.append((en[0], en[1]))
- return data
-
- def get_all_apps(self):
- """ Returns interesting statistics for the webapp """
- try:
- cur = self._db.cursor()
- cur.execute("SELECT DISTINCT(app_id) FROM reviews ORDER BY app_id;")
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchall()
- data = []
- for en in res:
- data.append(en[0])
- return data
-
- def get_stats(self):
- """ Returns interesting statistics for the webapp """
- item = {}
-
- # get the total number of reviews
- try:
- cur = self._db.cursor()
- cur.execute("SELECT COUNT(*) FROM reviews;")
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchall()
- item['Active reviews'] = int(res[0][0])
-
- # unique reviewers
- try:
- cur.execute("SELECT COUNT(DISTINCT(user_hash)) FROM reviews;")
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchall()
- item['Unique reviewers'] = int(res[0][0])
-
- # total votes
- try:
- cur.execute("SELECT COUNT(*) FROM votes WHERE val = 1;")
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchall()
- item['User upvotes'] = int(res[0][0])
- try:
- cur.execute("SELECT COUNT(*) FROM votes WHERE val = -1;")
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchall()
- item['User downvotes'] = int(res[0][0])
-
- # unique voters
- try:
- cur.execute("SELECT COUNT(DISTINCT(user_hash)) FROM votes;")
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchall()
- item['Unique voters'] = int(res[0][0])
-
- # unique languages
- try:
- cur.execute("SELECT COUNT(DISTINCT(locale)) FROM reviews;")
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchall()
- item['Unique languages'] = int(res[0][0])
-
- # unique distros
- try:
- cur.execute("SELECT COUNT(DISTINCT(distro)) FROM reviews;")
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchall()
- item['Unique distros'] = int(res[0][0])
-
- # unique apps
- try:
- cur.execute("SELECT COUNT(DISTINCT(app_id)) FROM reviews;")
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchall()
- item['Unique apps reviewed'] = int(res[0][0])
-
- # unique distros
- try:
- cur.execute("SELECT COUNT(*) FROM reviews WHERE reported > 0;")
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchall()
- item['Reported reviews'] = int(res[0][0])
-
- # star reviews
- for star in range(1, 6):
- try:
- cur.execute("SELECT COUNT(*) FROM reviews WHERE rating = %s;",
- (star * 20,))
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchall()
- item['%i star reviews' % star] = int(res[0][0])
-
- # done
- return item
-
- def get_stats_by_interval(self, size, interval, msg):
- """ Gets stats data """
- cnt = []
- now = datetime.date.today()
-
- # yes, there's probably a way to do this in one query
- cur = self._db.cursor()
- for i in range(size):
- start = now - datetime.timedelta((i * interval) + interval - 1)
- end = now - datetime.timedelta((i * interval) - 1)
- try:
- cur.execute("SELECT COUNT(*) FROM eventlog "
- "WHERE message = %s AND date_created BETWEEN %s "
- "AND %s", (msg, start, end,))
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchone()
- cnt.append(int(res[0]))
- return cnt
-
- def get_analytics_by_interval(self, size, interval):
- """ Gets analytics data """
- array = []
- now = datetime.date.today()
-
- # yes, there's probably a way to do this in one query
- cur = self._db.cursor()
- for i in range(size):
- start = _get_datestr_from_dt(now - datetime.timedelta((i * interval) + interval - 1))
- end = _get_datestr_from_dt(now - datetime.timedelta((i * interval) - 1))
- try:
- cur.execute("SELECT fetch_cnt FROM analytics WHERE "
- "datestr BETWEEN %s "
- "AND %s", (start, end,))
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchall()
-
- # add all these up
- tmp = 0
- for r in res:
- tmp = tmp + int(r[0])
- array.append(tmp)
- return array
-
- def get_analytics_fetch(self, limit=50):
- """ Returns interesting statistics for the webapp """
- try:
- cur = self._db.cursor()
- cur.execute("SELECT DISTINCT app_id, SUM(fetch_cnt) AS total "
- "FROM analytics WHERE app_id IS NOT NULL "
- "GROUP BY app_id ORDER BY total DESC LIMIT %s;",
- (limit,))
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchall()
- data = []
- for en in res:
- data.append((en[0], en[1]))
- return data
diff --git a/app/models.py b/app/models.py
new file mode 100644
index 0000000..67a6cb6
--- /dev/null
+++ b/app/models.py
@@ -0,0 +1,61 @@
+#!/usr/bin/python2
+# -*- coding: utf-8 -*-
+#
+# pylint: disable=invalid-name,missing-docstring,too-few-public-methods
+#
+# Copyright (C) 2015-2017 Richard Hughes <richard hughsie com>
+# Licensed under the GNU General Public License Version 2
+
+class User(object):
+ def __init__(self):
+ self.id = None
+ self.karma = 0
+ self.date_created = 0
+ self.user_hash = 0
+ self.is_banned = 0
+
+ @property
+ def is_authenticated(self):
+ return True
+
+ @property
+ def is_active(self):
+ return True
+
+ @property
+ def is_anonymous(self):
+ return False
+
+ def get_id(self):
+ return str(self.id)
+
+ def __repr__(self):
+ return '<User %r>' % (self.id)
+
+class Review(object):
+ def __init__(self):
+ self.review_id = 0
+ self.date_created = 0
+ self.app_id = None
+ self.locale = None
+ self.summary = None
+ self.description = None
+ self.version = None
+ self.distro = None
+ self.karma_up = 0
+ self.karma_down = 0
+ self.user_hash = None
+ self.user_display = None
+ self.rating = 0
+ self.date_deleted = None
+ self.reported = None
+
+class Event(object):
+ def __init__(self):
+ self.eventlog_id = 0
+ self.date_created = 0
+ self.user_addr = None
+ self.user_hash = None
+ self.message = None
+ self.app_id = None
+ self.important = False
diff --git a/static/Chart.js b/app/static/Chart.js
similarity index 100%
rename from static/Chart.js
rename to app/static/Chart.js
diff --git a/static/layout.css b/app/static/layout.css
similarity index 100%
rename from static/layout.css
rename to app/static/layout.css
diff --git a/static/style.css b/app/static/style.css
similarity index 100%
rename from static/style.css
rename to app/static/style.css
diff --git a/templates/default.html b/app/templates/default.html
similarity index 99%
rename from templates/default.html
rename to app/templates/default.html
index 964ac16..383725f 100644
--- a/templates/default.html
+++ b/app/templates/default.html
@@ -6,7 +6,6 @@
<head>
<title>{% block title %}{% endblock %}</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
- <base href="https://odrs.gnome.org/" />
<link href="layout.css" rel="stylesheet" type="text/css" media="screen" />
<link href="style.css" rel="stylesheet" type="text/css" media="all" />
<link rel="icon" type="image/png" href="https://www.gnome.org/img/logo/foot-16.png" />
diff --git a/templates/delete.html b/app/templates/delete.html
similarity index 100%
rename from templates/delete.html
rename to app/templates/delete.html
diff --git a/templates/distros.html b/app/templates/distros.html
similarity index 100%
rename from templates/distros.html
rename to app/templates/distros.html
diff --git a/templates/error.html b/app/templates/error.html
similarity index 100%
rename from templates/error.html
rename to app/templates/error.html
diff --git a/templates/graph-month.html b/app/templates/graph-month.html
similarity index 100%
rename from templates/graph-month.html
rename to app/templates/graph-month.html
diff --git a/templates/graph-year.html b/app/templates/graph-year.html
similarity index 100%
rename from templates/graph-year.html
rename to app/templates/graph-year.html
diff --git a/templates/index.html b/app/templates/index.html
similarity index 100%
rename from templates/index.html
rename to app/templates/index.html
diff --git a/templates/login.html b/app/templates/login.html
similarity index 100%
rename from templates/login.html
rename to app/templates/login.html
diff --git a/templates/oars.html b/app/templates/oars.html
similarity index 100%
rename from templates/oars.html
rename to app/templates/oars.html
diff --git a/templates/show-all.html b/app/templates/show-all.html
similarity index 100%
rename from templates/show-all.html
rename to app/templates/show-all.html
diff --git a/templates/show.html b/app/templates/show.html
similarity index 100%
rename from templates/show.html
rename to app/templates/show.html
diff --git a/templates/stats.html b/app/templates/stats.html
similarity index 100%
rename from templates/stats.html
rename to app/templates/stats.html
diff --git a/templates/users.html b/app/templates/users.html
similarity index 100%
rename from templates/users.html
rename to app/templates/users.html
diff --git a/api10.py b/app/views.py
similarity index 72%
rename from api10.py
rename to app/views.py
index f81d047..003e9f7 100644
--- a/api10.py
+++ b/app/views.py
@@ -1,22 +1,23 @@
#!/usr/bin/python2
# -*- coding: utf-8 -*-
#
-# pylint: disable=invalid-name
+# pylint: disable=invalid-name,missing-docstring
#
-# Copyright (C) 2016 Richard Hughes <richard hughsie com>
-# Licensed under the GNU General Public License Version 3
+# Copyright (C) 2015-2017 Richard Hughes <richard hughsie com>
+# Licensed under the GNU General Public License Version 2
import json
import os
import hashlib
import math
-from flask import Blueprint, Response, request
+from flask import request, url_for, redirect, flash, render_template, send_from_directory, Response
+from flask_login import login_user, logout_user
-from database import ReviewsDatabase, CursorError
-from review import OdrsReview
+from app import app, get_db
-api = Blueprint('api10', __name__, url_prefix='/')
+from .db import CursorError
+from .models import Review
def _get_user_key(user_hash, app_id):
salt = os.environ['ODRS_REVIEWS_SECRET']
@@ -114,7 +115,54 @@ def _sanitised_version(val):
return val
-@api.errorhandler(400)
+@app.route('/login', methods=['GET', 'POST'])
+def login():
+ if request.method != 'POST':
+ return render_template('login.html')
+ username = request.form['username']
+ password = request.form['password']
+ try:
+ db = get_db()
+ user = db.users.get_with_login(request.form['username'],
+ request.form['password'])
+ except CursorError as e:
+ flash(str(e))
+ return render_template('error.html'), 503
+ if not user:
+ flash('Credentials are not valid.')
+ return redirect(url_for('.login'))
+ login_user(user, remember=False)
+ flash('Logged in successfully.')
+ return redirect(url_for('.index'))
+
+@app.route("/logout")
+def logout():
+ logout_user()
+ flash('Logged out successfully.')
+ return redirect(url_for('.index'))
+
+@app.errorhandler(404)
+def error_page_not_found(msg=None):
+ """ Error handler: File not found """
+ flash(msg)
+ return render_template('error.html'), 404
+
+@app.route('/')
+def index():
+ """ start page """
+ return render_template('index.html')
+
+@app.route('/oars')
+def oars_index():
+ """ OARS page """
+ return render_template('oars.html')
+
+@app.route('/<path:resource>')
+def static_resource(resource):
+ """ Return a static image or resource """
+ return send_from_directory('static/', os.path.basename(resource))
+
+@app.errorhandler(400)
def json_error(msg=None, errcode=400):
""" Error handler: JSON output """
item = {}
@@ -126,7 +174,7 @@ def json_error(msg=None, errcode=400):
status=errcode, \
mimetype="application/json")
-@api.errorhandler(401)
+@app.errorhandler(401)
def error_permission_denied(msg=None):
""" Error handler: Permission Denied """
return json_error(msg, 401)
@@ -150,7 +198,7 @@ def _check_str(val):
return False
return True
-@api.route('/api/submit', methods=['POST'])
+@app.route('/1.0/reviews/api/submit', methods=['POST'])
def submit():
"""
Submits a new review.
@@ -183,23 +231,23 @@ def submit():
if not _check_str(item[key]):
return json_error('%s is not a valid string' % key)
try:
- db = ReviewsDatabase(os.environ)
# user has already reviewed
- if db.review_exists(item['app_id'], item['user_hash']):
- db.event_warn(_get_client_address(),
- item['user_hash'],
- item['app_id'],
- "already reviewed")
+ db = get_db()
+ if db.reviews.exists(item['app_id'], item['user_hash']):
+ db.eventlog.warn(_get_client_address(),
+ item['user_hash'],
+ item['app_id'],
+ "already reviewed")
return json_error('already reviewed this app')
# check user has not been banned
- user = db.user_get_by_hash(item['user_hash'])
+ user = db.users.get_by_hash(item['user_hash'])
if user and user.is_banned:
return json_error('account has been disabled due to abuse')
# create new
- review = OdrsReview()
+ review = Review()
review.app_id = item['app_id']
review.locale = item['locale']
review.summary = _sanitised_summary(item['summary'])
@@ -219,25 +267,25 @@ def submit():
review.user_display = item['user_display']
# log and add
- db.event_info(_get_client_address(),
- review.user_hash,
- review.app_id,
- "reviewed")
- db.review_add(review, _get_client_address())
+ db.eventlog.info(_get_client_address(),
+ review.user_hash,
+ review.app_id,
+ "reviewed")
+ db.reviews.add(review, _get_client_address())
except CursorError as e:
return json_error(str(e))
return json_success()
-@api.route('/api/app/<app_id>/<user_hash>')
-@api.route('/api/app/<app_id>')
-def app(app_id, user_hash=None):
+@app.route('/1.0/reviews/api/app/<app_id>/<user_hash>')
+@app.route('/1.0/reviews/api/app/<app_id>')
+def show_app(app_id, user_hash=None):
"""
Return details about an application.
"""
try:
- db = ReviewsDatabase(os.environ)
- db.event_info(_get_client_address(), user_hash, app_id, "getting")
- reviews = db.review_get_for_app_id(app_id)
+ db = get_db()
+ db.eventlog.info(_get_client_address(), user_hash, app_id, "getting")
+ reviews = db.reviews.get_for_app_id(app_id)
except CursorError as e:
return json_error(str(e))
@@ -256,7 +304,7 @@ def app(app_id, user_hash=None):
status=200, \
mimetype="application/json")
-@api.route('/api/fetch', methods=['POST'])
+@app.route('/1.0/reviews/api/fetch', methods=['POST'])
def fetch():
"""
Return details about an application.
@@ -276,9 +324,9 @@ def fetch():
return json_error('the user_hash is invalid')
try:
- db = ReviewsDatabase(os.environ)
+ db = get_db()
db.analytics_inc_fetch(item['app_id'])
- reviews = db.review_get_for_app_id(item['app_id'])
+ reviews = db.reviews.get_for_app_id(item['app_id'])
except CursorError as e:
return json_error(str(e))
@@ -286,15 +334,15 @@ def fetch():
if 'compat_ids' in item:
for app_id in item['compat_ids']:
try:
- reviews_tmp = db.review_get_for_app_id(app_id)
+ reviews_tmp = db.reviews.get_for_app_id(app_id)
except CursorError as e:
return json_error(str(e))
reviews.extend(reviews_tmp)
# if user does not exist then create
- user = db.user_get_by_hash(item['user_hash'])
+ user = db.users.get_by_hash(item['user_hash'])
if not user:
- db.user_add(item['user_hash'])
+ db.users.add(item['user_hash'])
# add score for review using secret sauce
items_new = []
@@ -312,7 +360,7 @@ def fetch():
item_new['score'] = _get_review_score(review, item)
# the UI can hide the vote buttons on reviews already voted on
- if db.vote_exists(review.review_id, item['user_hash']):
+ if db.reviews.vote_exists(review.review_id, item['user_hash']):
item_new['vote_id'] = 1
items_new.append(item_new)
@@ -336,16 +384,16 @@ def fetch():
status=200, \
mimetype="application/json")
-@api.route('/api/all/<user_hash>')
-@api.route('/api/all')
+@app.route('/1.0/reviews/api/all/<user_hash>')
+@app.route('/1.0/reviews/api/all')
def all(user_hash=None):
"""
Return all the reviews on the server as a JSON object.
"""
try:
- db = ReviewsDatabase(os.environ)
- db.event_info(_get_client_address(), user_hash, None, "getting all reviews")
- reviews = db.review_get_all()
+ db = get_db()
+ db.eventlog.info(_get_client_address(), user_hash, None, "getting all reviews")
+ reviews = db.reviews.get_all()
except CursorError as e:
return json_error(str(e))
@@ -362,16 +410,16 @@ def all(user_hash=None):
status=200, \
mimetype="application/json")
-@api.route('/api/moderate/<user_hash>')
-@api.route('/api/moderate/<user_hash>/<locale>')
+@app.route('/1.0/reviews/api/moderate/<user_hash>')
+@app.route('/1.0/reviews/api/moderate/<user_hash>/<locale>')
def moderate(user_hash, locale=None):
"""
Return all the reviews on the server the user can moderate.
"""
try:
- db = ReviewsDatabase(os.environ)
- db.event_info(_get_client_address(), user_hash, None, "getting moderatable reviews")
- reviews = db.review_get_all()
+ db = get_db()
+ db.eventlog.info(_get_client_address(), user_hash, None, "getting moderatable reviews")
+ reviews = db.reviews.get_all()
except CursorError as e:
return json_error(str(e))
@@ -380,7 +428,7 @@ def moderate(user_hash, locale=None):
for review in reviews:
if locale and not _locale_is_compatible(review.locale, locale):
continue
- if not db.vote_exists(review.review_id, user_hash):
+ if not db.reviews.vote_exists(review.review_id, user_hash):
item = review.__dict__
item['user_skey'] = _get_user_key(user_hash, review.app_id)
items_new.append(item)
@@ -412,29 +460,25 @@ def vote(val):
if not len(item['user_skey']) == 40:
return json_error('the user_skey is invalid')
- # connect to database early
- try:
- db = ReviewsDatabase(os.environ)
- except CursorError as e:
- return json_error(str(e))
-
if item['user_skey'] != _get_user_key(item['user_hash'], item['app_id']):
- db.event_warn(_get_client_address(), item['user_hash'], None,
- "invalid user_skey of %s" % item['user_skey'])
+ db = get_db()
+ db.eventlog.warn(_get_client_address(), item['user_hash'], None,
+ "invalid user_skey of %s" % item['user_skey'])
#print("expected user_skey of %s" % _get_user_key(item['user_hash'], item['app_id']))
return json_error('invalid user_skey')
try:
# the user already has a review
- if db.vote_exists(item['review_id'], item['user_hash']):
- db.event_warn(_get_client_address(), item['user_hash'], item['app_id'],
- "duplicate vote")
+ db = get_db()
+ if db.reviews.vote_exists(item['review_id'], item['user_hash']):
+ db.eventlog.warn(_get_client_address(), item['user_hash'], item['app_id'],
+ "duplicate vote")
return json_error('already voted on this app')
# update the per-user karma
- user = db.user_get_by_hash(item['user_hash'])
+ user = db.users.get_by_hash(item['user_hash'])
if not user:
- db.user_add(item['user_hash'])
+ db.users.add(item['user_hash'])
else:
# user is naughty
@@ -444,46 +488,46 @@ def vote(val):
# the user is too harsh
if val < 0 and user.karma < -50:
return json_error('all negative karma used up')
- db.user_update_karma(item['user_hash'], val)
+ db.users.update_karma(item['user_hash'], val)
# add the vote to the database
- db.vote_add(item['review_id'], val, item['user_hash'])
- db.event_info(_get_client_address(), item['user_hash'], item['app_id'],
- "voted %i on review" % val)
+ db.reviews.vote_add(item['review_id'], val, item['user_hash'])
+ db.eventlog.info(_get_client_address(), item['user_hash'], item['app_id'],
+ "voted %i on review" % val)
except CursorError as e:
return json_error(str(e))
return json_success('voted #%i %i' % (item['review_id'], val))
-@api.route('/api/upvote', methods=['POST'])
+@app.route('/1.0/reviews/api/upvote', methods=['POST'])
def upvote():
"""
Upvote an existing review by one karma point.
"""
return vote(1)
-@api.route('/api/downvote', methods=['POST'])
+@app.route('/1.0/reviews/api/downvote', methods=['POST'])
def downvote():
"""
Downvote an existing review by one karma point.
"""
return vote(-1)
-@api.route('/api/dismiss', methods=['POST'])
+@app.route('/1.0/reviews/api/dismiss', methods=['POST'])
def dismiss():
"""
Dismiss a review without rating it up or down.
"""
return vote(0)
-@api.route('/api/report', methods=['POST'])
+@app.route('/1.0/reviews/api/report', methods=['POST'])
def report():
"""
Report a review for abuse.
"""
return vote(-5)
-@api.route('/api/remove', methods=['POST'])
+@app.route('/1.0/reviews/api/remove', methods=['POST'])
def remove():
"""
Remove a review.
@@ -504,34 +548,31 @@ def remove():
if not len(item['user_skey']) == 40:
return json_error('the user_skey is invalid')
- # connect to database early
- try:
- db = ReviewsDatabase(os.environ)
- except CursorError as e:
- return json_error(str(e))
if item['user_skey'] != _get_user_key(item['user_hash'], item['app_id']):
- db.event_warn(_get_client_address(), item['user_hash'], None,
- "invalid user_skey of %s" % item['user_skey'])
+ db = get_db()
+ db.eventlog.warn(_get_client_address(), item['user_hash'], None,
+ "invalid user_skey of %s" % item['user_skey'])
return json_error('invalid user_skey')
try:
# the user already has a review
- db.review_remove(item['review_id'], item['user_hash'])
- db.event_info(_get_client_address(),
- item['user_hash'],
- item['app_id'],
- "removed review")
+ db = get_db()
+ db.reviews.remove(item['review_id'], item['user_hash'])
+ db.eventlog.info(_get_client_address(),
+ item['user_hash'],
+ item['app_id'],
+ "removed review")
except CursorError as e:
return json_error(str(e))
return json_success('removed review #%i' % item['review_id'])
-@api.route('/api/ratings/<app_id>')
+@app.route('/1.0/reviews/api/ratings/<app_id>')
def rating_for_id(app_id):
"""
Get the star ratings for a specific application.
"""
try:
- db = ReviewsDatabase(os.environ)
- ratings = db.reviews_get_rating_for_app_id(app_id)
+ db = get_db()
+ ratings = db.reviews.get_rating_for_app_id(app_id)
except CursorError as e:
return json_error(str(e))
@@ -540,17 +581,17 @@ def rating_for_id(app_id):
status=200, \
mimetype="application/json")
-@api.route('/api/ratings')
+@app.route('/1.0/reviews/api/ratings')
def ratings():
"""
Get the star ratings for a specific application.
"""
item = {}
try:
- db = ReviewsDatabase(os.environ)
- app_ids = db.get_all_apps()
+ db = get_db()
+ app_ids = db.reviews.get_all_apps()
for app_id in app_ids:
- ratings = db.reviews_get_rating_for_app_id(app_id, 2)
+ ratings = db.reviews.get_rating_for_app_id(app_id, 2)
if len(ratings) == 0:
continue
item[app_id] = ratings
diff --git a/admin.py b/app/views_admin.py
similarity index 75%
rename from admin.py
rename to app/views_admin.py
index fc58983..96aa784 100644
--- a/admin.py
+++ b/app/views_admin.py
@@ -1,20 +1,20 @@
#!/usr/bin/python2
# -*- coding: utf-8 -*-
#
-# Copyright (C) 2016 Richard Hughes <richard hughsie com>
-# Licensed under the GNU General Public License Version 3
+# pylint: disable=invalid-name,missing-docstring
+#
+# Copyright (C) 2015-2017 Richard Hughes <richard hughsie com>
+# Licensed under the GNU General Public License Version 2
-import os
import datetime
import calendar
from math import ceil
-from flask import Blueprint, abort, request, flash, render_template, redirect, url_for
+from flask import abort, request, flash, render_template, redirect, url_for
from flask_login import login_required
-from database import ReviewsDatabase, CursorError
-
-admin = Blueprint('admin', __name__, url_prefix='/admin')
+from app import app, get_db
+from .db import CursorError
def _get_chart_labels_months():
""" Gets the chart labels """
@@ -68,29 +68,26 @@ class Pagination(object):
yield num
last = num
-@admin.errorhandler(400)
+@app.errorhandler(400)
def error_internal(msg=None, errcode=400):
""" Error handler: Internal """
flash("Internal error: %s" % msg)
return render_template('error.html'), errcode
-@admin.errorhandler(401)
+@app.errorhandler(401)
def error_permission_denied(msg=None):
""" Error handler: Permission Denied """
flash("Permission denied: %s" % msg)
return render_template('error.html'), 401
-@admin.route('/graph_month')
+@app.route('/admin/graph_month')
@login_required
def graph_month():
"""
Show nice graph graphs.
"""
- try:
- db = ReviewsDatabase(os.environ)
- except CursorError as e:
- return error_internal(str(e))
+ db = get_db()
data_fetch = db.get_analytics_by_interval(30, 1)
data_review = db.get_stats_by_interval(30, 1, 'reviewed')
return render_template('graph-month.html',
@@ -98,16 +95,13 @@ def graph_month():
data_requests=data_fetch[::-1],
data_submitted=data_review[::-1])
-@admin.route('/graph_year')
+@app.route('/admin/graph_year')
@login_required
def graph_year():
"""
Show nice graph graphs.
"""
- try:
- db = ReviewsDatabase(os.environ)
- except CursorError as e:
- return error_internal(str(e))
+ db = get_db()
data_fetch = db.get_analytics_by_interval(12, 30)
data_review = db.get_stats_by_interval(12, 30, 'reviewed')
return render_template('graph-year.html',
@@ -115,14 +109,14 @@ def graph_year():
data_requests=data_fetch[::-1],
data_submitted=data_review[::-1])
-@admin.route('/stats')
+@app.route('/admin/stats')
@login_required
-def stats():
+def show_stats():
"""
Return the statistics page as HTML.
"""
try:
- db = ReviewsDatabase(os.environ)
+ db = get_db()
stats = db.get_stats()
except CursorError as e:
return error_internal(str(e))
@@ -146,14 +140,14 @@ def stats():
results_viewed=results_viewed,
results_submitted=results_submitted)
-@admin.route('/distros')
+@app.route('/admin/distros')
@login_required
def distros():
"""
Return the statistics page as HTML.
"""
try:
- db = ReviewsDatabase(os.environ)
+ db = get_db()
stats = db.get_stats_distro(8)
except CursorError as e:
return error_internal(str(e))
@@ -168,7 +162,7 @@ def distros():
data.append(s[1])
return render_template('distros.html', labels=labels, data=data)
-@admin.context_processor
+@app.context_processor
def utility_processor():
def format_rating(rating):
nr_stars = int(rating / 20)
@@ -199,27 +193,27 @@ def utility_processor():
format_timestamp=format_timestamp,
url_for_other_page=url_for_other_page)
-@admin.route('/review/<review_id>')
-def review(review_id):
+@app.route('/admin/review/<review_id>')
+def admin_show_review(review_id):
"""
Show a specific review as HTML.
"""
try:
- db = ReviewsDatabase(os.environ)
- review = db.review_get_for_id(review_id)
+ db = get_db()
+ review = db.reviews.get_for_id(review_id)
except CursorError as e:
return error_internal(str(e))
if not review:
return error_internal('no review with that ID')
return render_template('show.html', r=review)
-@admin.route('/modify/<review_id>', methods=['POST'])
+@app.route('/admin/modify/<review_id>', methods=['POST'])
@login_required
-def modify(review_id):
+def admin_modify(review_id):
""" Change details about a review """
try:
- db = ReviewsDatabase(os.environ)
- review = db.review_get_for_id(review_id)
+ db = get_db()
+ review = db.reviews.get_for_id(review_id)
except CursorError as e:
return error_internal(str(e))
if not review:
@@ -234,57 +228,57 @@ def modify(review_id):
review.description = request.form['description']
review.summary = request.form['summary']
review.version = request.form['version']
- db.review_modify(review)
+ db.reviews.modify(review)
return redirect(url_for('.review', review_id=review_id))
-@admin.route('/user_ban/<user_hash>')
+@app.route('/admin/users.ban/<user_hash>')
@login_required
-def user_ban(user_hash):
+def admin_user_ban(user_hash):
""" Change details about a review """
try:
- db = ReviewsDatabase(os.environ)
- db.user_ban(user_hash)
+ db = get_db()
+ db.users.ban(user_hash)
except CursorError as e:
return error_internal(str(e))
return redirect(url_for('.show_reported'))
-@admin.route('/unreport/<review_id>')
+@app.route('/admin/unreport/<review_id>')
@login_required
-def unreport(review_id):
+def admin_unreport(review_id):
""" Unreport a perfectly valid review """
try:
- db = ReviewsDatabase(os.environ)
- review = db.review_get_for_id(review_id)
+ db = get_db()
+ review = db.reviews.get_for_id(review_id)
except CursorError as e:
return error_internal(str(e))
if not review:
return error_internal('no review with that ID')
review.reported = 0
- db.review_modify(review)
+ db.reviews.modify(review)
return redirect(url_for('.review', review_id=review_id))
-@admin.route('/unremove/<review_id>')
+@app.route('/admin/unremove/<review_id>')
@login_required
-def unremove(review_id):
+def admin_unremove(review_id):
""" Unreport a perfectly valid review """
try:
- db = ReviewsDatabase(os.environ)
- review = db.review_get_for_id(review_id)
+ db = get_db()
+ review = db.reviews.get_for_id(review_id)
except CursorError as e:
return error_internal(str(e))
if not review:
return error_internal('no review with that ID')
review.date_deleted = 0
- db.review_modify(review)
+ db.reviews.modify(review)
return redirect(url_for('.review', review_id=review_id))
-@admin.route('/englishify/<review_id>')
+@app.route('/admin/englishify/<review_id>')
@login_required
-def englishify(review_id):
+def admin_englishify(review_id):
""" Marks a review as writen in English """
try:
- db = ReviewsDatabase(os.environ)
- review = db.review_get_for_id(review_id)
+ db = get_db()
+ review = db.reviews.get_for_id(review_id)
except CursorError as e:
return error_internal(str(e))
if not review:
@@ -294,53 +288,53 @@ def englishify(review_id):
review.locale = 'en'
else:
review.locale = 'en_' + parts[1]
- db.review_modify(review)
+ db.reviews.modify(review)
return redirect(url_for('.review', review_id=review_id))
-@admin.route('/anonify/<review_id>')
+@app.route('/admin/anonify/<review_id>')
@login_required
-def anonify(review_id):
+def admin_anonify(review_id):
""" Removes the username from the review """
try:
- db = ReviewsDatabase(os.environ)
- review = db.review_get_for_id(review_id)
+ db = get_db()
+ review = db.reviews.get_for_id(review_id)
except CursorError as e:
return error_internal(str(e))
if not review:
return error_internal('no review with that ID')
review.user_display = None
- db.review_modify(review)
+ db.reviews.modify(review)
return redirect(url_for('.review', review_id=review_id))
-@admin.route('/delete/<review_id>/force')
+@app.route('/admin/delete/<review_id>/force')
@login_required
-def delete_force(review_id):
+def admin_delete_force(review_id):
""" Delete a review """
try:
- db = ReviewsDatabase(os.environ)
- review = db.review_get_for_id(review_id)
+ db = get_db()
+ review = db.reviews.get_for_id(review_id)
except CursorError as e:
return error_internal(str(e))
if not review:
return error_internal('no review with that ID')
- db.review_delete(review)
+ db.reviews.delete(review)
return redirect(url_for('.show_all'))
-@admin.route('/delete/<review_id>')
+@app.route('/admin/delete/<review_id>')
@login_required
-def delete(review_id):
+def admin_delete(review_id):
""" Ask for confirmation to delete a review """
return render_template('delete.html', review_id=review_id)
-@admin.route('/show/all', defaults={'page': 1})
-@admin.route('/show/all/page/<int:page>')
-def show_all(page):
+@app.route('/admin/show/all', defaults={'page': 1})
+@app.route('/admin/show/all/page/<int:page>')
+def admin_show_all(page):
"""
Return all the reviews on the server as HTML.
"""
try:
- db = ReviewsDatabase(os.environ)
- reviews = db.review_get_all()
+ db = get_db()
+ reviews = db.reviews.get_all()
except CursorError as e:
return error_internal(str(e))
if not reviews and page != 1:
@@ -353,15 +347,15 @@ def show_all(page):
pagination=pagination,
reviews=reviews)
-@admin.route('/show/reported')
-def show_reported():
+@app.route('/admin/show/reported')
+def admin_show_reported():
"""
Return all the reported reviews on the server as HTML.
"""
reviews_filtered = []
try:
- db = ReviewsDatabase(os.environ)
- reviews = db.review_get_all()
+ db = get_db()
+ reviews = db.reviews.get_all()
for review in reviews:
if review.reported > 0:
reviews_filtered.append(review)
@@ -369,15 +363,15 @@ def show_reported():
return error_internal(str(e))
return render_template('show-all.html', reviews=reviews_filtered)
-@admin.route('/show/user/<user_hash>')
-def show_user(user_hash):
+@app.route('/admin/show/user/<user_hash>')
+def admin_show_user(user_hash):
"""
Return all the reviews from a user on the server as HTML.
"""
reviews_filtered = []
try:
- db = ReviewsDatabase(os.environ)
- reviews = db.review_get_all()
+ db = get_db()
+ reviews = db.reviews.get_all()
for review in reviews:
if review.user_hash == user_hash:
reviews_filtered.append(review)
@@ -385,15 +379,15 @@ def show_user(user_hash):
return error_internal(str(e))
return render_template('show-all.html', reviews=reviews_filtered)
-@admin.route('/show/app/<app_id>')
-def show_app(app_id):
+@app.route('/admin/show/app/<app_id>')
+def admin_show_app(app_id):
"""
Return all the reviews from a user on the server as HTML.
"""
reviews_filtered = []
try:
- db = ReviewsDatabase(os.environ)
- reviews = db.review_get_all()
+ db = get_db()
+ reviews = db.reviews.get_all()
for review in reviews:
if review.app_id == app_id:
reviews_filtered.append(review)
@@ -401,15 +395,15 @@ def show_app(app_id):
return error_internal(str(e))
return render_template('show-all.html', reviews=reviews_filtered)
-@admin.route('/show/lang/<locale>')
-def show_lang(locale):
+@app.route('/admin/show/lang/<locale>')
+def admin_show_lang(locale):
"""
Return all the reviews from a user on the server as HTML.
"""
reviews_filtered = []
try:
- db = ReviewsDatabase(os.environ)
- reviews = db.review_get_all()
+ db = get_db()
+ reviews = db.reviews.get_all()
for review in reviews:
if review.locale == locale:
reviews_filtered.append(review)
@@ -417,16 +411,16 @@ def show_lang(locale):
return error_internal(str(e))
return render_template('show-all.html', reviews=reviews_filtered)
-@admin.route('/users/all')
+@app.route('/admin/users/all')
@login_required
-def users_all():
+def admin_users_all():
"""
Return all the users as HTML.
"""
try:
- db = ReviewsDatabase(os.environ)
- users_awesome = db.get_users_by_karma(best=True)
- users_haters = db.get_users_by_karma(best=False)
+ db = get_db()
+ users_awesome = db.users.get_by_karma(best=True)
+ users_haters = db.users.get_by_karma(best=False)
except CursorError as e:
return error_internal(str(e))
return render_template('users.html', users_awesome=users_awesome, users_haters=users_haters)
diff --git a/flaskapp.py b/flaskapp.py
index ab2edc3..f9c087b 100755
--- a/flaskapp.py
+++ b/flaskapp.py
@@ -1,80 +1,10 @@
#!/usr/bin/python2
# -*- coding: utf-8 -*-
#
-# Copyright (C) 2016 Richard Hughes <richard hughsie com>
+# Copyright (C) 2016-2017 Richard Hughes <richard hughsie com>
# Licensed under the GNU General Public License Version 3
-import os
-from flask import Flask, request, url_for, redirect, flash, render_template, send_from_directory, abort
-from flask_login import LoginManager
-from flask_login import login_required, login_user, logout_user
-
-from api10 import api as api10
-from admin import admin
-from database import ReviewsDatabase, CursorError
-from user import OdrsUser
-
-app = Flask(__name__)
-app.config.from_object(__name__)
-app.secret_key = os.environ['ODRS_REVIEWS_SECRET']
-app.register_blueprint(api10, url_prefix='/1.0/reviews')
-app.register_blueprint(admin, url_prefix='/admin')
-
-login_manager = LoginManager()
-login_manager.init_app(app)
-
-@login_manager.user_loader
-def load_user(user_id):
- db = ReviewsDatabase(os.environ)
- user = db.user_get_by_id(user_id)
- return user
-
-@app.route('/login', methods=['GET', 'POST'])
-def login():
- if request.method != 'POST':
- return render_template('login.html')
- username = request.form['username']
- password = request.form['password']
- try:
- db = ReviewsDatabase(os.environ)
- user = db.user_get_with_login(request.form['username'],
- request.form['password'])
- except CursorError as e:
- flash(str(e))
- return render_template('error.html'), 503
- if not user:
- flash('Credentials are not valid.')
- return redirect(url_for('.login'))
- login_user(user, remember=False)
- flash('Logged in successfully.')
- return redirect(url_for('.index'))
-
-@app.route("/logout")
-def logout():
- logout_user()
- flash('Logged out successfully.')
- return redirect(url_for('.index'))
-
-@app.errorhandler(404)
-def error_page_not_found(msg=None):
- """ Error handler: File not found """
- flash(msg)
- return render_template('error.html'), 404
-
-@app.route('/')
-def index():
- """ start page """
- return render_template('index.html')
-
-@app.route('/oars')
-def oars_index():
- """ OARS page """
- return render_template('oars.html')
-
-@app.route('/<path:resource>')
-def static_resource(resource):
- """ Return a static image or resource """
- return send_from_directory('static/', resource)
+from app import app
if __name__ == '__main__':
app.debug = True
diff --git a/odrs.wsgi b/odrs.wsgi
index c9de12a..855747a 100644
--- a/odrs.wsgi
+++ b/odrs.wsgi
@@ -15,5 +15,5 @@ def application(environ, start_response):
'MYSQL_DB_PASSWORD',
'ODRS_REVIEWS_SECRET']:
os.environ[key] = environ[key]
- from flaskapp import app as _application
+ from app import app as _application
return _application(environ, start_response)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]