Coverage for portality / view / doajservices.py: 53%
93 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-04 09:41 +0100
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-04 09:41 +0100
1import json
2from io import BytesIO
4from flask import Blueprint, make_response, request, abort, render_template, send_file, url_for
5from flask_login import current_user, login_required
7from portality import lock, models
8from portality.bll import DOAJ
9from portality.crosswalks.article_ris import ArticleRisXWalk
10from portality.core import app
11from portality.decorators import ssl_required, write_required
12from portality.lib import plausible
13from portality.util import jsonp
14from portality.ui import templates
15from portality.bll.services.shorturl import InvalidURL, UrlShortenerLimitExceeded
17blueprint = Blueprint('doajservices', __name__)
20@blueprint.route("/unlock/<object_type>/<object_id>", methods=["POST"])
21@login_required
22@ssl_required
23@write_required()
24def unlock(object_type, object_id):
25 # change from suggestion to application after redesign, that needs refactoring to make the code consistent
26 # that if is only for quick hotfix to make sure the functionality works
27 if object_type == "application":
28 object_type = "suggestion"
30 # first figure out if we are allowed to even contemplate this action
31 if object_type not in ["journal", "suggestion"]:
32 abort(404)
33 if object_type == "journal":
34 if not current_user.has_role("edit_journal"):
35 abort(401)
36 if object_type == "suggestion":
37 if not current_user.has_role("edit_suggestion"):
38 abort(401)
40 # try to unlock
41 unlocked = lock.unlock(object_type, object_id, current_user.id)
43 # if we couldn't unlock, this is a bad request
44 if not unlocked:
45 abort(400)
47 # otherwise, return success
48 resp = make_response(json.dumps({"result": "success"}))
49 resp.mimetype = "application/json"
50 return resp
53@blueprint.route("/unlocked")
54@login_required
55def unlocked():
56 """
57 Redirect to this route on completion of an unlock
58 :return:
59 """
60 if current_user.is_super:
61 return render_template(templates.ADMIN_UNLOCKED)
62 else:
63 return render_template(templates.EDITOR_UNLOCKED)
66@blueprint.route("/shorten", methods=["POST"])
67@plausible.pa_event(app.config.get('ANALYTICS_CATEGORY_URLSHORT', 'Urlshort'),
68 action=app.config.get('ANALYTICS_ACTION_URLSHORT_ADD', 'Find or create shortener url'))
69@write_required()
70def shorten():
71 """ create shortener url """
72 url = json.loads(request.data)['url']
73 urlshort = DOAJ.shortUrlService()
75 try:
76 short_url_record = urlshort.get_short_url(url)
77 except UrlShortenerLimitExceeded:
78 abort(429)
79 except InvalidURL:
80 abort(400)
82 short_url = app.config.get("BASE_URL") + url_for('doaj.shortened_url', alias=short_url_record.alias)
83 resp = make_response(json.dumps({"short_url": short_url}))
84 resp.mimetype = "application/json"
85 return resp
88@blueprint.route("/groupstatus/<group_id>", methods=["GET"])
89@jsonp
90@login_required
91def group_status(group_id):
92 """
93 ~~GroupStatus:Feature -> Todo:Service~~
94 :param group_id:
95 :return:
96 """
97 if (not (current_user.has_role("editor") and models.EditorGroup.pull(group_id).editor == current_user.id)) and (
98 not current_user.has_role("admin")):
99 abort(404)
100 svc = DOAJ.todoService()
101 stats = svc.group_stats(group_id)
102 return make_response(json.dumps(stats))
105@blueprint.route('/export/article/<article_id>/<fmt>')
106@plausible.pa_event(app.config.get('ANALYTICS_CATEGORY_RIS', 'RIS'),
107 action=app.config.get('ANALYTICS_ACTION_RISEXPORT', 'Export'), record_value_of_which_arg='article_id')
108@write_required()
109def export_article_ris(article_id, fmt):
110 article = models.Article.pull(article_id)
111 if not article:
112 abort(404)
114 exportSvc = DOAJ.exportService()
115 ris = exportSvc.ris(article)
116 filename = f'article-{article_id[:10]}.ris'
117 resp = make_response(send_file(ris.byte_stream, as_attachment=True, download_name=filename))
118 return resp
121@blueprint.route("/alerts/<alert_id>/<action>", methods=["POST"])
122@write_required()
123def manage_alert(alert_id, action):
124 """
125 Manage user alerts
126 :param alert_id: the id of the alert to manage
127 :param action: the action to perform on the alert, either 'delete' or 'mark_read'
128 :return: JSON response indicating success or failure
129 """
130 if not current_user.is_authenticated:
131 abort(401)
133 if not current_user.has_role("admin"):
134 abort(403)
136 if action not in ['in_progress', 'closed']:
137 abort(400)
139 svc = DOAJ.adminAlertsService()
140 if action == 'in_progress':
141 svc.set_in_progress(alert_id, current_user)
142 elif action == 'closed':
143 svc.set_closed(alert_id, current_user)
145 return make_response(json.dumps({"status": "success"}))