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

1import json 

2from io import BytesIO 

3 

4from flask import Blueprint, make_response, request, abort, render_template, send_file, url_for 

5from flask_login import current_user, login_required 

6 

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 

16 

17blueprint = Blueprint('doajservices', __name__) 

18 

19 

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" 

29 

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) 

39 

40 # try to unlock 

41 unlocked = lock.unlock(object_type, object_id, current_user.id) 

42 

43 # if we couldn't unlock, this is a bad request 

44 if not unlocked: 

45 abort(400) 

46 

47 # otherwise, return success 

48 resp = make_response(json.dumps({"result": "success"})) 

49 resp.mimetype = "application/json" 

50 return resp 

51 

52 

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) 

64 

65 

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() 

74 

75 try: 

76 short_url_record = urlshort.get_short_url(url) 

77 except UrlShortenerLimitExceeded: 

78 abort(429) 

79 except InvalidURL: 

80 abort(400) 

81 

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 

86 

87 

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)) 

103 

104 

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) 

113 

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 

119 

120 

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) 

132 

133 if not current_user.has_role("admin"): 

134 abort(403) 

135 

136 if action not in ['in_progress', 'closed']: 

137 abort(400) 

138 

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) 

144 

145 return make_response(json.dumps({"status": "success"}))