Passed
Push — master ( 6fa740...307301 )
by Jochen
02:39
created

byceps.blueprints.api.tourney.match.comments.views   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 205
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 127
dl 0
loc 205
rs 10
c 0
b 0
f 0
wmc 21

10 Functions

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