1 | """ |
||
2 | byceps.blueprints.api.tourney.match.views |
||
3 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||
4 | |||
5 | :Copyright: 2006-2019 Jochen Kupperschmidt |
||
6 | :License: Modified BSD, see LICENSE for details. |
||
7 | """ |
||
8 | |||
9 | from typing import Any, Dict |
||
10 | |||
11 | from flask import abort, jsonify, request, url_for |
||
12 | from marshmallow import ValidationError |
||
13 | from marshmallow.schema import SchemaMeta |
||
14 | |||
15 | from ......services.tourney import match_comment_service, match_service |
||
16 | from ......services.user import service as user_service |
||
17 | from ......util.framework.blueprint import create_blueprint |
||
18 | from ......util.framework.templating import templated |
||
19 | from ......util.views import respond_created, respond_no_content |
||
20 | |||
21 | from ....decorators import api_token_required |
||
22 | |||
23 | from ... import signals |
||
24 | |||
25 | from .schemas import ( |
||
26 | CreateMatchCommentRequest, |
||
27 | ModerateMatchCommentRequest, |
||
28 | UpdateMatchCommentRequest, |
||
29 | ) |
||
30 | |||
31 | |||
32 | blueprint = create_blueprint('api_tourney_match_comments', __name__) |
||
33 | |||
34 | |||
35 | @blueprint.route('/matches/<uuid:match_id>/comments') |
||
36 | @api_token_required |
||
37 | @templated |
||
38 | def view_for_match(match_id): |
||
39 | """Render the comments on a match.""" |
||
40 | match = _get_match_or_404(match_id) |
||
41 | |||
42 | party_id = request.args.get('party_id') |
||
43 | |||
44 | comments = match_comment_service.get_comments( |
||
45 | match.id, party_id=party_id, include_hidden=False |
||
46 | ) |
||
47 | |||
48 | return { |
||
49 | 'comments': comments, |
||
50 | } |
||
51 | |||
52 | |||
53 | @blueprint.route('/matches/<uuid:match_id>/comments.json') |
||
54 | @api_token_required |
||
55 | def view_for_match_as_json(match_id): |
||
56 | """Render the comments on a match as JSON.""" |
||
57 | match = _get_match_or_404(match_id) |
||
58 | |||
59 | party_id = request.args.get('party_id') |
||
60 | |||
61 | comments = match_comment_service.get_comments( |
||
62 | match.id, party_id=party_id, include_hidden=True |
||
63 | ) |
||
64 | |||
65 | comment_dtos = list(map(_comment_to_json, comments)) |
||
66 | |||
67 | return jsonify({ |
||
68 | 'comments': comment_dtos, |
||
69 | }) |
||
70 | |||
71 | |||
72 | def _comment_to_json(comment): |
||
73 | creator = comment.creator |
||
74 | |||
75 | return { |
||
76 | 'comment_id': str(comment.id), |
||
77 | 'match_id': str(comment.match_id), |
||
78 | 'created_at': comment.created_at.isoformat(), |
||
79 | 'creator': { |
||
80 | 'user_id': str(creator.id), |
||
81 | 'screen_name': creator.screen_name, |
||
82 | 'suspended': creator.suspended, |
||
83 | 'deleted': creator.deleted, |
||
84 | 'avatar_url': creator.avatar_url, |
||
85 | 'is_orga': creator.is_orga, |
||
86 | }, |
||
87 | 'body': comment.body_rendered, |
||
88 | 'hidden': comment.hidden, |
||
89 | 'hidden_at': comment.hidden_at.isoformat() \ |
||
90 | if (comment.hidden_at is not None) else None, |
||
91 | 'hidden_by_id': comment.hidden_by_id, |
||
92 | } |
||
93 | |||
94 | |||
95 | blueprint.add_url_rule( |
||
96 | '/match_comments/<uuid:comment_id>', |
||
97 | endpoint='view', |
||
98 | build_only=True, |
||
99 | ) |
||
100 | |||
101 | |||
102 | @blueprint.route('/match_comments', methods=['POST']) |
||
103 | @api_token_required |
||
104 | @respond_created |
||
105 | def create(): |
||
106 | """Create a comment on a match.""" |
||
107 | req = _parse_request(CreateMatchCommentRequest) |
||
108 | |||
109 | match = match_service.find_match(req['match_id']) |
||
110 | if not match: |
||
111 | abort(400, 'Unknown match ID') |
||
112 | |||
113 | creator = user_service.find_active_user(req['creator_id']) |
||
114 | if not creator: |
||
115 | abort(400, 'Creator ID does not reference an active user.') |
||
116 | |||
117 | body = req['body'].strip() |
||
118 | |||
119 | comment = match_comment_service.create_comment(match.id, creator.id, body) |
||
120 | |||
121 | signals.match_comment_created.send(None, comment_id=comment.id) |
||
122 | |||
123 | return url_for('.view', comment_id=comment.id) |
||
124 | |||
125 | |||
126 | @blueprint.route('/match_comments/<uuid:comment_id>', methods=['PATCH']) |
||
127 | @api_token_required |
||
128 | @respond_no_content |
||
129 | def update(comment_id): |
||
130 | """Update a comment on a match.""" |
||
131 | comment = _get_comment_or_404(comment_id) |
||
132 | |||
133 | req = _parse_request(UpdateMatchCommentRequest) |
||
134 | |||
135 | editor = user_service.find_active_user(req['editor_id']) |
||
136 | if not editor: |
||
137 | abort(400, 'Editor ID does not reference an active user.') |
||
138 | |||
139 | body = req['body'].strip() |
||
140 | |||
141 | match_comment_service.update_comment(comment.id, editor.id, body) |
||
142 | |||
143 | |||
144 | @blueprint.route( |
||
1 ignored issue
–
show
Duplication
introduced
by
Loading history...
|
|||
145 | '/match_comments/<uuid:comment_id>/flags/hidden', methods=['POST'] |
||
146 | ) |
||
147 | @api_token_required |
||
148 | @respond_no_content |
||
149 | def hide(comment_id): |
||
150 | """Hide the match comment.""" |
||
151 | comment = _get_comment_or_404(comment_id) |
||
152 | |||
153 | req = _parse_request(ModerateMatchCommentRequest) |
||
154 | |||
155 | initiator = user_service.find_active_user(req['initiator_id']) |
||
156 | if not initiator: |
||
157 | abort(400, 'Initiator ID does not reference an active user.') |
||
158 | |||
159 | match_comment_service.hide_comment(comment.id, initiator.id) |
||
160 | |||
161 | |||
162 | @blueprint.route( |
||
163 | '/match_comments/<uuid:comment_id>/flags/hidden', |
||
164 | methods=['DELETE'], |
||
165 | ) |
||
166 | @api_token_required |
||
167 | @respond_no_content |
||
168 | def unhide(comment_id): |
||
169 | """Un-hide the match comment.""" |
||
170 | comment = _get_comment_or_404(comment_id) |
||
171 | |||
172 | req = _parse_request(ModerateMatchCommentRequest) |
||
173 | |||
174 | initiator = user_service.find_active_user(req['initiator_id']) |
||
175 | if not initiator: |
||
176 | abort(400, 'Initiator ID does not reference an active user.') |
||
177 | |||
178 | match_comment_service.unhide_comment(comment.id, initiator.id) |
||
179 | |||
180 | |||
181 | def _get_match_or_404(match_id): |
||
182 | match = match_service.find_match(match_id) |
||
183 | |||
184 | if match is None: |
||
185 | abort(404) |
||
186 | |||
187 | return match |
||
188 | |||
189 | |||
190 | def _get_comment_or_404(comment_id): |
||
191 | comment = match_comment_service.find_comment(comment_id) |
||
192 | |||
193 | if comment is None: |
||
194 | abort(404) |
||
195 | |||
196 | return comment |
||
197 | |||
198 | |||
199 | def _parse_request(schema_class: SchemaMeta) -> Dict[str, Any]: |
||
200 | schema = schema_class() |
||
201 | request_data = request.get_json() |
||
202 | |||
203 | try: |
||
204 | req = schema.load(request_data) |
||
205 | except ValidationError as e: |
||
206 | abort(400, str(e.normalized_messages())) |
||
207 | |||
208 | return req |
||
209 |