# This file is part of HEPData.
# Copyright (C) 2016 CERN.
#
# HEPData is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# HEPData is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with HEPData; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307, USA.
#
# In applying this license, CERN does not
# waive the privileges and immunities granted to it by virtue of its status
# as an Intergovernmental Organization or submit itself to any jurisdiction.
"""HEPData Dashboard Views."""
from flask import Blueprint, jsonify, request, render_template, abort, \
current_app, make_response, url_for
from flask_login import login_required, current_user
from invenio_accounts.models import User, Role
from hepdata.ext.opensearch.admin_view.api import AdminIndexer
from hepdata.ext.opensearch.api import reindex_all
from hepdata.ext.opensearch.api import push_data_keywords
from hepdata.modules.dashboard.api import prepare_submissions, get_pending_invitations_for_user, get_submission_count, \
list_submission_titles, get_dashboard_current_user, set_dashboard_current_user, get_submissions_summary, \
get_submissions_csv
from hepdata.modules.permissions.api import get_pending_request, get_pending_coordinator_requests
from hepdata.modules.permissions.views import check_is_sandbox_record
from hepdata.modules.records.utils.submission import unload_submission, do_finalise
from hepdata.modules.submission.api import get_latest_hepsubmission
from hepdata.modules.records.ext import user_is_admin_or_coordinator
from hepdata.modules.records.utils.users import has_role
from hepdata.modules.records.utils.common import get_record_by_id
from hepdata.modules.records.utils.workflow import update_record
from hepdata.modules.inspire_api.views import get_inspire_record_information
from hepdata.utils.url import modify_query
import collections
import json
import math
from invenio_userprofiles import current_userprofile, UserProfile
blueprint = Blueprint('hep_dashboard', __name__, url_prefix="/dashboard",
template_folder='templates',
static_folder='static')
[docs]
@blueprint.route('/')
@login_required
def dashboard():
"""
Depending on the user that is logged in, they will get a
dashboard that reflects the
current status of all submissions of which they are a participant.
An admin user can view the dashboard as if they were a different user,
using the view_as parameter
"""
view_as_user_id = request.args.get('view_as_user', type=int)
if view_as_user_id:
try:
user_to_display = set_dashboard_current_user(current_user, view_as_user_id)
except ValueError:
abort(404)
else:
user_to_display = get_dashboard_current_user(current_user)
if user_to_display == current_user:
user_profile = current_userprofile
else:
user_profile = UserProfile.get_by_userid(user_to_display.get_id())
ctx = {'user_is_admin': has_role(user_to_display, 'admin'),
'user_profile': user_profile,
'user_to_display': user_to_display,
'view_as_mode': user_to_display != current_user,
'user_is_coordinator_or_admin': user_is_admin_or_coordinator(user_to_display),
'user_has_coordinator_request': get_pending_request(),
'pending_coordinator_requests': get_pending_coordinator_requests(),
'pending_invites': get_pending_invitations_for_user(user_to_display)}
return render_template('hepdata_dashboard/dashboard.html', ctx=ctx)
[docs]
@blueprint.route('/dashboard-submissions')
@login_required
def dashboard_submissions():
user = get_dashboard_current_user(current_user)
view_as_mode = user != current_user
filter_record_id = request.args.get('record_id')
current_page = request.args.get('page', default=1, type=int)
size = request.args.get('size', 25)
submissions = prepare_submissions(
user,
items_per_page=size,
current_page=current_page,
record_id=filter_record_id
)
submission_meta = []
submission_stats = []
for record_id in submissions:
stats = []
for key in submissions[record_id]["stats"].keys():
stats.append(
{"name": key, "count": submissions[record_id]["stats"][key]})
submission_stats.append({"recid": record_id, "stats": stats})
review_flag = "todo"
if submissions[record_id]["stats"]["attention"] == 0 and \
submissions[record_id]["stats"]["todo"] == 0 and \
submissions[record_id]["stats"]["passed"] == 0:
review_flag = "todo"
elif submissions[record_id]["stats"]["attention"] > 0 or \
submissions[record_id]["stats"]["todo"] > 0:
review_flag = "attention"
elif submissions[record_id]["stats"]["attention"] == 0 and \
submissions[record_id]["stats"]["todo"] == 0:
review_flag = "passed"
if submissions[record_id]["status"] == 'finished':
review_flag = "finished"
submissions[record_id]["metadata"]["submission_status"] = \
submissions[record_id]["status"]
submissions[record_id]["metadata"]["review_flag"] = review_flag
submission_meta.append(submissions[record_id]["metadata"])
total_records = get_submission_count(user)
total_pages = int(math.ceil(total_records / size))
ctx = {
'user_is_admin': has_role(user, 'admin'),
'view_as_mode': view_as_mode,
'modify_query': modify_query,
'submissions': submission_meta,
'submission_stats': json.dumps(submission_stats)
}
if filter_record_id is None:
ctx['pages'] = {
'total': total_pages,
'current': current_page,
'endpoint': '.dashboard'
}
return render_template('hepdata_dashboard/dashboard-submissions.html', ctx=ctx)
[docs]
@blueprint.route('/dashboard-submission-titles')
@login_required
def dashboard_submission_titles(user=current_user):
if user != current_user and not has_role(current_user, 'admin'):
abort(403)
return jsonify(list_submission_titles(user))
[docs]
@blueprint.route('/delete/<int:recid>')
@login_required
def delete_submission(recid):
"""
Submissions can only be removed if they are not finalised,
meaning they should never be in the index.
Only delete the latest version of a submission.
Delete indexed information only if version = 1.
:param recid:
:return:
"""
if has_role(current_user, 'admin') or has_role(current_user, 'coordinator') or check_is_sandbox_record(recid):
submission = get_latest_hepsubmission(publication_recid=recid)
if submission:
unload_submission.delay(recid, submission.version)
admin_idx = AdminIndexer()
admin_idx.delete_by_id(submission.id)
return json.dumps({"success": True})
else:
return json.dumps({"success": False, "message": "This submission has already been deleted."})
else:
return json.dumps(
{"success": False,
"message":
"You do not have permission to delete this submission. "
"Only coordinators can do that."})
[docs]
@blueprint.route('/manage/reindex/', methods=['POST'])
@login_required
def reindex():
if has_role(current_user, 'admin'):
reindex_all(recreate=True)
push_data_keywords()
admin_idx = AdminIndexer()
admin_idx.reindex(recreate=True)
return jsonify({"success": True})
else:
return jsonify({"success": False,
'message': "You don't have sufficient privileges to "
"perform this action."})
[docs]
@blueprint.route('/finalise/<int:recid>', methods=['POST'])
@login_required
def finalise(recid, publication_record=None, force_finalise=False):
commit_message = request.form.get('message')
# Update publication information from INSPIRE record before finalising.
if not publication_record:
record = get_record_by_id(recid)
content, status = get_inspire_record_information(record['inspire_id'])
if status == 'success':
publication_record = update_record(recid, content)
return do_finalise(recid, publication_record=publication_record, force_finalise=force_finalise,
commit_message=commit_message, send_tweet=True)
[docs]
@blueprint.route('/submissions/', methods=['GET'])
@login_required
def submissions():
view_as_user_id = request.args.get('view_as_user', type=int)
if view_as_user_id:
try:
user = set_dashboard_current_user(current_user, view_as_user_id)
except ValueError:
abort(404)
else:
user = get_dashboard_current_user(current_user)
if has_role(user, 'admin') or has_role(user, 'coordinator'):
user_profile = UserProfile.get_by_userid(user.get_id())
url_for_params = { 'view_as_user': user.id } if user != current_user else {}
dashboard_url = url_for('hep_dashboard.dashboard', **url_for_params)
ctx = {'user_is_admin': has_role(user, 'admin'),
'user_is_coordinator_or_admin': user_is_admin_or_coordinator(user),
'user_profile': user_profile,
'user_to_display': user,
'view_as_mode': user != current_user,
'dashboard_url': dashboard_url }
return render_template('hepdata_dashboard/submissions.html', ctx=ctx)
else:
abort(403)
[docs]
@blueprint.route('/submissions/list', methods=['GET'])
@login_required
def submissions_list():
user = get_dashboard_current_user(current_user)
if not (has_role(user, 'admin') or has_role(user, 'coordinator')):
return {"success": False,
'message': "You don't have sufficient privileges to "
"perform this action."}
summary = get_submissions_summary(
user,
include_imported=current_app.config.get('TESTING', False)
)
return jsonify(summary)
[docs]
@blueprint.route('/submissions/csv', methods=['GET'])
@login_required
def submissions_csv():
user = get_dashboard_current_user(current_user)
if not (has_role(user, 'admin') or has_role(user, 'coordinator')):
abort(403)
csv_data = get_submissions_csv(
user,
include_imported=current_app.config.get('TESTING', False)
)
output = make_response(csv_data)
output.headers["Content-Disposition"] = "attachment; filename=export.csv"
output.headers["Content-type"] = "text/csv"
return output
[docs]
@blueprint.route('/list-all-users')
@login_required
def get_all_users():
"""
Gets a list of all active users in the system.
:return:
"""
if has_role(current_user, 'admin'):
coordinators_only = request.args.get(
'coordinators_only', default=False,
type=lambda v: v.lower() == 'true'
)
users_query = User.query.with_entities(User.id, User.email) \
.filter_by(active=True)
if coordinators_only:
users_query = users_query.filter(User.roles.any(Role.name == 'coordinator'))
users = users_query.all()
return jsonify([{'id': u[0], 'email': u[1]} for u in users])
else:
abort(403)