From 0887dde6fca44d6669a0bcce9423c003a14bd20c Mon Sep 17 00:00:00 2001 From: Kfir Hadas Date: Sat, 8 Jul 2017 00:50:55 +0300 Subject: [PATCH] Move /user/ route into a blueprint Move supporting functions and variables into other files * nyaa.utils: - cached_function - chain_get * nyaa.search: - DEFAULT_MAX_SEARCH_RESULT - DEFAULT_PER_PAGE - SERACH_PAGINATE_DISPLAY_MSG - _generate_query_string --- nyaa/routes.py | 186 ++--------------------------- nyaa/search.py | 19 +++ nyaa/templates/adminlog.html | 2 +- nyaa/templates/edit.html | 2 +- nyaa/templates/layout.html | 6 +- nyaa/templates/reports.html | 4 +- nyaa/templates/search_results.html | 2 +- nyaa/templates/view.html | 4 +- nyaa/utils.py | 12 ++ nyaa/views/__init__.py | 2 + nyaa/views/admin.py | 6 +- nyaa/views/users.py | 156 ++++++++++++++++++++++++ 12 files changed, 209 insertions(+), 192 deletions(-) create mode 100644 nyaa/views/users.py diff --git a/nyaa/routes.py b/nyaa/routes.py index 41699e7..aff0a14 100644 --- a/nyaa/routes.py +++ b/nyaa/routes.py @@ -14,15 +14,12 @@ from werkzeug.datastructures import CombinedMultiDict from itsdangerous import BadSignature, URLSafeSerializer from sqlalchemy.orm import joinedload -from nyaa import api_handler, app, backend, db, forms, models, torrents, utils, views -from nyaa.search import search_db, search_elastic +from nyaa import api_handler, app, backend, db, forms, models, torrents, views +from nyaa.search import (DEFAULT_MAX_SEARCH_RESULT, DEFAULT_PER_PAGE, SERACH_PAGINATE_DISPLAY_MSG, + _generate_query_string, search_db, search_elastic) +from nyaa.utils import cached_function, chain_get DEBUG_API = False -DEFAULT_MAX_SEARCH_RESULT = 1000 -DEFAULT_PER_PAGE = 75 -SERACH_PAGINATE_DISPLAY_MSG = ('Displaying results {start}-{end} out of {total} results.
\n' - 'Please refine your search results if you can\'t find ' - 'what you were looking for.') # For static_cachebuster @@ -100,19 +97,6 @@ def before_request(): return 'You are banned.', 403 -def _generate_query_string(term, category, filter, user): - params = {} - if term: - params['q'] = str(term) - if category: - params['c'] = str(category) - if filter: - params['f'] = str(filter) - if user: - params['u'] = str(user) - return params - - @app.template_filter('utc_time') def get_utc_timestamp(datetime_str): ''' Returns a UTC POSIX timestamp, as seconds ''' @@ -125,7 +109,7 @@ def get_display_time(datetime_str): return datetime.strptime(datetime_str, '%Y-%m-%dT%H:%M:%S').strftime('%Y-%m-%d %H:%M') -@utils.cached_function +@cached_function def get_category_id_map(): ''' Reads database for categories and turns them into a dict with ids as keys and name list as the value, ala @@ -141,18 +125,6 @@ def get_category_id_map(): # Routes start here # -def chain_get(source, *args): - ''' Tries to return values from source by the given keys. - Returns None if none match. - Note: can return a None from the source. ''' - sentinel = object() - for key in args: - value = source.get(key, sentinel) - if value is not sentinel: - return value - return None - - @app.route('/rss', defaults={'rss': True}) @app.route('/', defaults={'rss': False}) def home(rss): @@ -290,130 +262,6 @@ def home(rss): special_results=special_results) -@app.route('/user/', methods=['GET', 'POST']) -def view_user(user_name): - user = models.User.by_username(user_name) - - if not user: - flask.abort(404) - - admin_form = None - if flask.g.user and flask.g.user.is_moderator and flask.g.user.level > user.level: - admin_form = forms.UserForm() - default, admin_form.user_class.choices = _create_user_class_choices(user) - if flask.request.method == 'GET': - admin_form.user_class.data = default - - url = flask.url_for('view_user', user_name=user.username) - if flask.request.method == 'POST' and admin_form and admin_form.validate(): - selection = admin_form.user_class.data - log = None - if selection == 'regular': - user.level = models.UserLevelType.REGULAR - log = "[{}]({}) changed to regular user".format(user_name, url) - elif selection == 'trusted': - user.level = models.UserLevelType.TRUSTED - log = "[{}]({}) changed to trusted user".format(user_name, url) - elif selection == 'moderator': - user.level = models.UserLevelType.MODERATOR - log = "[{}]({}) changed to moderator user".format(user_name, url) - elif selection == 'banned': - user.status = models.UserStatusType.BANNED - log = "[{}]({}) changed to banned user".format(user_name, url) - - adminlog = models.AdminLog(log=log, admin_id=flask.g.user.id) - db.session.add(user) - db.session.add(adminlog) - db.session.commit() - - return flask.redirect(url) - - user_level = ['Regular', 'Trusted', 'Moderator', 'Administrator'][user.level] - - req_args = flask.request.args - - search_term = chain_get(req_args, 'q', 'term') - - sort_key = req_args.get('s') - sort_order = req_args.get('o') - - category = chain_get(req_args, 'c', 'cats') - quality_filter = chain_get(req_args, 'f', 'filter') - - page_number = chain_get(req_args, 'p', 'page', 'offset') - try: - page_number = max(1, int(page_number)) - except (ValueError, TypeError): - page_number = 1 - - results_per_page = app.config.get('RESULTS_PER_PAGE', DEFAULT_PER_PAGE) - - query_args = { - 'term': search_term or '', - 'user': user.id, - 'sort': sort_key or 'id', - 'order': sort_order or 'desc', - 'category': category or '0_0', - 'quality_filter': quality_filter or '0', - 'page': page_number, - 'rss': False, - 'per_page': results_per_page - } - - if flask.g.user: - query_args['logged_in_user'] = flask.g.user - if flask.g.user.is_moderator: # God mode - query_args['admin'] = True - - # Use elastic search for term searching - rss_query_string = _generate_query_string(search_term, category, quality_filter, user_name) - use_elastic = app.config.get('USE_ELASTIC_SEARCH') - if use_elastic and search_term: - query_args['term'] = search_term - - max_search_results = app.config.get('ES_MAX_SEARCH_RESULT', DEFAULT_MAX_SEARCH_RESULT) - - # Only allow up to (max_search_results / page) pages - max_page = min(query_args['page'], int(math.ceil(max_search_results / results_per_page))) - - query_args['page'] = max_page - query_args['max_search_results'] = max_search_results - - query_results = search_elastic(**query_args) - - max_results = min(max_search_results, query_results['hits']['total']) - # change p= argument to whatever you change page_parameter to or pagination breaks - pagination = Pagination(p=query_args['page'], per_page=results_per_page, - total=max_results, bs_version=3, page_parameter='p', - display_msg=SERACH_PAGINATE_DISPLAY_MSG) - return flask.render_template('user.html', - use_elastic=True, - pagination=pagination, - torrent_query=query_results, - search=query_args, - user=user, - user_page=True, - rss_filter=rss_query_string, - level=user_level, - admin_form=admin_form) - # Similar logic as home page - else: - if use_elastic: - query_args['term'] = '' - else: - query_args['term'] = search_term or '' - query = search_db(**query_args) - return flask.render_template('user.html', - use_elastic=False, - torrent_query=query, - search=query_args, - user=user, - user_page=True, - rss_filter=rss_query_string, - level=user_level, - admin_form=admin_form) - - @app.template_filter('rfc822') def _jinja2_filter_rfc822(date, fmt=None): return formatdate(date.timestamp()) @@ -459,7 +307,7 @@ def activate_user(payload): return flask.redirect('/login') -@utils.cached_function +@cached_function def _create_upload_category_choices(): ''' Turns categories in the database into a list of (id, name)s ''' choices = [('', '[Select a category]')] @@ -730,27 +578,6 @@ def get_activation_link(user): return flask.url_for('activate_user', payload=payload, _external=True) -def _create_user_class_choices(user): - choices = [('regular', 'Regular')] - default = 'regular' - if flask.g.user: - if flask.g.user.is_moderator: - choices.append(('trusted', 'Trusted')) - if flask.g.user.is_superadmin: - choices.append(('moderator', 'Moderator')) - choices.append(('banned', 'Banned')) - - if user: - if user.is_moderator: - default = 'moderator' - elif user.is_trusted: - default = 'trusted' - elif user.is_banned: - default = 'banned' - - return default, choices - - @app.template_filter() def timesince(dt, default='just now'): """ @@ -791,6 +618,7 @@ def register_blueprints(flask_app): flask_app.register_blueprint(views.account_bp) flask_app.register_blueprint(views.admin_bp) flask_app.register_blueprint(views.site_bp) + flask_app.register_blueprint(views.users_bp) # When done, this can be moved to nyaa/__init__.py instead of importing this file diff --git a/nyaa/search.py b/nyaa/search.py index 2614e24..9bc3dbd 100644 --- a/nyaa/search.py +++ b/nyaa/search.py @@ -13,6 +13,25 @@ from sqlalchemy_fulltext import FullTextSearch from elasticsearch import Elasticsearch from elasticsearch_dsl import Search, Q +DEFAULT_MAX_SEARCH_RESULT = 1000 +DEFAULT_PER_PAGE = 75 +SERACH_PAGINATE_DISPLAY_MSG = ('Displaying results {start}-{end} out of {total} results.
\n' + 'Please refine your search results if you can\'t find ' + 'what you were looking for.') + + +def _generate_query_string(term, category, filter, user): + params = {} + if term: + params['q'] = str(term) + if category: + params['c'] = str(category) + if filter: + params['f'] = str(filter) + if user: + params['u'] = str(user) + return params + def search_elastic(term='', user=None, sort='id', order='desc', category='0_0', quality_filter='0', page=1, diff --git a/nyaa/templates/adminlog.html b/nyaa/templates/adminlog.html index 8b3e5bf..24c5a30 100644 --- a/nyaa/templates/adminlog.html +++ b/nyaa/templates/adminlog.html @@ -17,7 +17,7 @@ {{ log.id }} - {{ log.admin.username }} + {{ log.admin.username }}
{{ log.log }}
{{ log.created_time }} diff --git a/nyaa/templates/edit.html b/nyaa/templates/edit.html index afc3df1..a7ebf68 100644 --- a/nyaa/templates/edit.html +++ b/nyaa/templates/edit.html @@ -8,7 +8,7 @@

Edit Torrent #{{torrent.id}} {% if (torrent.user != None) and (torrent.user != g.user) %} - (by {{ torrent.user.username }}) + (by {{ torrent.user.username }}) {% endif %}

diff --git a/nyaa/templates/layout.html b/nyaa/templates/layout.html index 76ae459..07bffdf 100644 --- a/nyaa/templates/layout.html +++ b/nyaa/templates/layout.html @@ -122,7 +122,7 @@
  • - + Torrents @@ -214,7 +214,7 @@
    {# The mobile menu #} {% if user_page %} -
    {% if user_page %} - + {% else %} {% endif %} diff --git a/nyaa/templates/reports.html b/nyaa/templates/reports.html index 943abb7..968aab2 100644 --- a/nyaa/templates/reports.html +++ b/nyaa/templates/reports.html @@ -19,14 +19,14 @@ {{ report.id }} - {{ report.user.username }} + {{ report.user.username }} {% if report.user.is_trusted %} Trusted {% endif %} {{ report.torrent.display_name }} - by + by {{ report.torrent.user.username }} {% if g.user.is_superadmin and report.torrent.uploader_ip %} ({{ report.torrent.uploader_ip_string }}) diff --git a/nyaa/templates/search_results.html b/nyaa/templates/search_results.html index c606592..ddc372b 100644 --- a/nyaa/templates/search_results.html +++ b/nyaa/templates/search_results.html @@ -12,7 +12,7 @@ {% if special_results is defined and not search.user %} {% if special_results.first_word_user %} {% endif %} {% endif %} diff --git a/nyaa/templates/view.html b/nyaa/templates/view.html index dc0f199..4e3525d 100644 --- a/nyaa/templates/view.html +++ b/nyaa/templates/view.html @@ -29,7 +29,7 @@
    Submitter:
    - {% set user_url = torrent.user and url_for('view_user', user_name=torrent.user.username) %} + {% set user_url = torrent.user and url_for('users.view_user', user_name=torrent.user.username) %} {%- if not torrent.anonymous and torrent.user -%} {{ torrent.user.username }} {%- else -%} @@ -145,7 +145,7 @@

    - {{ comment.user.username }} + {{ comment.user.username }} {% if comment.user.id == torrent.uploader_id and not torrent.anonymous %} (uploader) {% endif %} diff --git a/nyaa/utils.py b/nyaa/utils.py index 6596621..3b28df1 100644 --- a/nyaa/utils.py +++ b/nyaa/utils.py @@ -59,3 +59,15 @@ def flatten_dict(d, result=None): else: result[key] = value return result + + +def chain_get(source, *args): + ''' Tries to return values from source by the given keys. + Returns None if none match. + Note: can return a None from the source. ''' + sentinel = object() + for key in args: + value = source.get(key, sentinel) + if value is not sentinel: + return value + return None diff --git a/nyaa/views/__init__.py b/nyaa/views/__init__.py index d7922cc..7eb4491 100644 --- a/nyaa/views/__init__.py +++ b/nyaa/views/__init__.py @@ -2,8 +2,10 @@ from nyaa.views import ( account, admin, site, + users, ) account_bp = account.bp admin_bp = admin.bp site_bp = site.bp +users_bp = users.bp diff --git a/nyaa/views/admin.py b/nyaa/views/admin.py index 2c3416a..b48b1d3 100644 --- a/nyaa/views/admin.py +++ b/nyaa/views/admin.py @@ -46,19 +46,19 @@ def view_reports(): log = log.format(report_id, 'Deleted', torrent_id, flask.url_for('view_torrent', torrent_id=torrent_id), report_user.username, - flask.url_for('view_user', user_name=report_user.username)) + flask.url_for('users.view_user', user_name=report_user.username)) elif action == 'hide': log = log.format(report_id, 'Hid', torrent_id, flask.url_for('view_torrent', torrent_id=torrent_id), report_user.username, - flask.url_for('view_user', user_name=report_user.username)) + flask.url_for('users.view_user', user_name=report_user.username)) torrent.hidden = True report.status = 1 else: log = log.format(report_id, 'Closed', torrent_id, flask.url_for('view_torrent', torrent_id=torrent_id), report_user.username, - flask.url_for('view_user', user_name=report_user.username)) + flask.url_for('users.view_user', user_name=report_user.username)) report.status = 2 adminlog = models.AdminLog(log=log, admin_id=flask.g.user.id) diff --git a/nyaa/views/users.py b/nyaa/views/users.py new file mode 100644 index 0000000..e854d57 --- /dev/null +++ b/nyaa/views/users.py @@ -0,0 +1,156 @@ +import math + +import flask +from flask_paginate import Pagination + +from nyaa import app, db, forms, models +from nyaa.search import (DEFAULT_MAX_SEARCH_RESULT, DEFAULT_PER_PAGE, SERACH_PAGINATE_DISPLAY_MSG, + _generate_query_string, search_db, search_elastic) +from nyaa.utils import chain_get + +bp = flask.Blueprint('users', __name__) + + +@bp.route('/user/', methods=['GET', 'POST']) +def view_user(user_name): + user = models.User.by_username(user_name) + + if not user: + flask.abort(404) + + admin_form = None + if flask.g.user and flask.g.user.is_moderator and flask.g.user.level > user.level: + admin_form = forms.UserForm() + default, admin_form.user_class.choices = _create_user_class_choices(user) + if flask.request.method == 'GET': + admin_form.user_class.data = default + + url = flask.url_for('users.view_user', user_name=user.username) + if flask.request.method == 'POST' and admin_form and admin_form.validate(): + selection = admin_form.user_class.data + log = None + if selection == 'regular': + user.level = models.UserLevelType.REGULAR + log = "[{}]({}) changed to regular user".format(user_name, url) + elif selection == 'trusted': + user.level = models.UserLevelType.TRUSTED + log = "[{}]({}) changed to trusted user".format(user_name, url) + elif selection == 'moderator': + user.level = models.UserLevelType.MODERATOR + log = "[{}]({}) changed to moderator user".format(user_name, url) + elif selection == 'banned': + user.status = models.UserStatusType.BANNED + log = "[{}]({}) changed to banned user".format(user_name, url) + + adminlog = models.AdminLog(log=log, admin_id=flask.g.user.id) + db.session.add(user) + db.session.add(adminlog) + db.session.commit() + + return flask.redirect(url) + + user_level = ['Regular', 'Trusted', 'Moderator', 'Administrator'][user.level] + + req_args = flask.request.args + + search_term = chain_get(req_args, 'q', 'term') + + sort_key = req_args.get('s') + sort_order = req_args.get('o') + + category = chain_get(req_args, 'c', 'cats') + quality_filter = chain_get(req_args, 'f', 'filter') + + page_number = chain_get(req_args, 'p', 'page', 'offset') + try: + page_number = max(1, int(page_number)) + except (ValueError, TypeError): + page_number = 1 + + results_per_page = app.config.get('RESULTS_PER_PAGE', DEFAULT_PER_PAGE) + + query_args = { + 'term': search_term or '', + 'user': user.id, + 'sort': sort_key or 'id', + 'order': sort_order or 'desc', + 'category': category or '0_0', + 'quality_filter': quality_filter or '0', + 'page': page_number, + 'rss': False, + 'per_page': results_per_page + } + + if flask.g.user: + query_args['logged_in_user'] = flask.g.user + if flask.g.user.is_moderator: # God mode + query_args['admin'] = True + + # Use elastic search for term searching + rss_query_string = _generate_query_string(search_term, category, quality_filter, user_name) + use_elastic = app.config.get('USE_ELASTIC_SEARCH') + if use_elastic and search_term: + query_args['term'] = search_term + + max_search_results = app.config.get('ES_MAX_SEARCH_RESULT', DEFAULT_MAX_SEARCH_RESULT) + + # Only allow up to (max_search_results / page) pages + max_page = min(query_args['page'], int(math.ceil(max_search_results / results_per_page))) + + query_args['page'] = max_page + query_args['max_search_results'] = max_search_results + + query_results = search_elastic(**query_args) + + max_results = min(max_search_results, query_results['hits']['total']) + # change p= argument to whatever you change page_parameter to or pagination breaks + pagination = Pagination(p=query_args['page'], per_page=results_per_page, + total=max_results, bs_version=3, page_parameter='p', + display_msg=SERACH_PAGINATE_DISPLAY_MSG) + return flask.render_template('user.html', + use_elastic=True, + pagination=pagination, + torrent_query=query_results, + search=query_args, + user=user, + user_page=True, + rss_filter=rss_query_string, + level=user_level, + admin_form=admin_form) + # Similar logic as home page + else: + if use_elastic: + query_args['term'] = '' + else: + query_args['term'] = search_term or '' + query = search_db(**query_args) + return flask.render_template('user.html', + use_elastic=False, + torrent_query=query, + search=query_args, + user=user, + user_page=True, + rss_filter=rss_query_string, + level=user_level, + admin_form=admin_form) + + +def _create_user_class_choices(user): + choices = [('regular', 'Regular')] + default = 'regular' + if flask.g.user: + if flask.g.user.is_moderator: + choices.append(('trusted', 'Trusted')) + if flask.g.user.is_superadmin: + choices.append(('moderator', 'Moderator')) + choices.append(('banned', 'Banned')) + + if user: + if user.is_moderator: + default = 'moderator' + elif user.is_trusted: + default = 'trusted' + elif user.is_banned: + default = 'banned' + + return default, choices