Skip to content

Commit 975f321

Browse files
Bekka592catInOrbitmariobehlingnorbusan
authored
Added video translation backend (#9018)
* added files for video translation backend * resolving python errors * adding docstrings and re-formatting * marking unused function parameters * add static method decorators * modifying static methods --------- Co-authored-by: Mao Nguyen Minh Tam <[email protected]> Co-authored-by: Mario Behling <[email protected]> Co-authored-by: Norbert Preining <[email protected]>
1 parent 89f31b4 commit 975f321

File tree

6 files changed

+275
-0
lines changed

6 files changed

+275
-0
lines changed

app/api/routes.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,12 @@
265265
TrackRelationshipOptional,
266266
TrackRelationshipRequired,
267267
)
268+
from app.api.translation_channels import (
269+
TranslationChannelsDetail,
270+
TranslationChannelsList,
271+
TranslationChannelsListPost,
272+
TranslationChannelsRelationship,
273+
)
268274
from app.api.user_check_in import (
269275
UserCheckInDetail,
270276
UserCheckInList,
@@ -1907,6 +1913,35 @@
19071913
api.route(ImportJobDetail, 'import_job_detail', '/import-jobs/<int:id>')
19081914

19091915
# Video Streams
1916+
api.route(
1917+
TranslationChannelsList,
1918+
'translation_channels_list',
1919+
"/translation_channels",
1920+
'/video-streams/<int:video_stream_id>/translation_channels',
1921+
)
1922+
1923+
api.route(
1924+
TranslationChannelsListPost, 'translation_channels_list_post', '/translation_channels'
1925+
)
1926+
1927+
api.route(
1928+
TranslationChannelsDetail,
1929+
'translation_channels_detail',
1930+
'/translation_channels/<int:id>',
1931+
)
1932+
1933+
api.route(
1934+
TranslationChannelsRelationship,
1935+
'translation_channels_video_stream',
1936+
'/translation_channel/<int:id>/relationships/video-stream',
1937+
)
1938+
1939+
api.route(
1940+
TranslationChannelsRelationship,
1941+
'translation_channels_channel',
1942+
'/translation_channel/<int:id>/relationships/channels',
1943+
)
1944+
19101945
api.route(VideoStreamList, 'video_stream_list', '/video-streams')
19111946
api.route(
19121947
VideoStreamDetail,
@@ -1943,6 +1978,8 @@
19431978
'video_stream_moderators',
19441979
'/video-streams/<int:id>/relationships/video-stream-moderators',
19451980
)
1981+
1982+
19461983
api.route(
19471984
VideoStreamRelationship,
19481985
'video_stream_recordings',
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from marshmallow_jsonapi import fields
2+
from marshmallow_jsonapi.flask import Relationship, Schema
3+
4+
from app.api.helpers.utilities import dasherize
5+
6+
7+
class TranslationChannelSchema(Schema):
8+
"""Schema of Translation Channel objects"""
9+
10+
id = fields.Integer(dump_only=True)
11+
name = fields.String(required=True)
12+
url = fields.String(required=True)
13+
14+
video_stream = Relationship(
15+
self_view="v1.translation_channels_video_stream",
16+
self_view_kwargs={"id": "<id>"},
17+
related_view="v1.video_stream_detail",
18+
related_view_kwargs={"id": "<video_stream_id>"},
19+
schema="VideoStreamSchema",
20+
type_="video_stream",
21+
)
22+
23+
channel = Relationship(
24+
self_view="v1.translation_channels_channel",
25+
self_view_kwargs={"id": "<id>"},
26+
related_view="v1.video_channel_detail",
27+
related_view_kwargs={"id": "<channel_id>"},
28+
schema="VideoChannelSchema",
29+
type_="video_channel",
30+
)
31+
32+
class Meta:
33+
type_ = "translation_channel"
34+
self_view = 'v1.translation_channels_detail'
35+
self_view_kwargs = {'id': '<id>'}
36+
self_view_many = 'v1.translation_channels_list'
37+
inflect = dasherize
38+
strict = True

app/api/schema/video_stream.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class Meta:
5656
schema='MicrolocationSchema',
5757
type_='microlocation',
5858
)
59+
5960
event = Relationship(
6061
self_view='v1.video_stream_event',
6162
self_view_kwargs={'id': '<id>'},

app/api/translation_channels.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
from flask_rest_jsonapi import ResourceDetail, ResourceList, ResourceRelationship
2+
from flask_rest_jsonapi.exceptions import ObjectNotFound
3+
4+
from app.api.bootstrap import api
5+
from app.api.helpers.db import safe_query_by_id
6+
from app.api.helpers.permissions import jwt_required
7+
from app.api.helpers.utilities import require_relationship
8+
from app.api.schema.translation_channels import TranslationChannelSchema
9+
from app.models import db
10+
from app.models.translation_channels import TranslationChannel
11+
from app.models.video_channel import VideoChannel
12+
from app.models.video_stream import VideoStream
13+
14+
15+
class TranslationChannelsList(ResourceList):
16+
"""Get list of Translation Channels"""
17+
18+
@staticmethod
19+
def before_get(kwargs):
20+
"""Function called when requesting Translation Channels List"""
21+
stream_id = kwargs.get("video_stream_id")
22+
if stream_id:
23+
vid_stream = safe_query_by_id(VideoStream, stream_id)
24+
if not vid_stream:
25+
26+
raise ObjectNotFound(
27+
{'parameter': f'{stream_id}'},
28+
f"video stream not found for id {stream_id}",
29+
)
30+
31+
def query(self, view_kwargs):
32+
"""Query related channels (translations) for specific video stream"""
33+
if view_kwargs.get("video_stream_id"):
34+
stream_id = view_kwargs.get("video_stream_id")
35+
36+
# Do not use all() as it returns a list of object, needs BaseQuery object
37+
records = self.session.query(TranslationChannel).filter_by(
38+
video_stream_id=stream_id
39+
)
40+
return records
41+
else:
42+
return None
43+
44+
methods = ["GET"]
45+
schema = TranslationChannelSchema
46+
decorators = (jwt_required,)
47+
data_layer = {
48+
'session': db.session,
49+
'model': TranslationChannel,
50+
'methods': {"query": query, "before_get": before_get},
51+
}
52+
53+
54+
class TranslationChannelsListPost(ResourceList):
55+
"""Post a list of Translation Channels"""
56+
57+
@staticmethod
58+
def before_post(data):
59+
"""Function called when posting to the Translation Channel List"""
60+
require_relationship(['video_stream', 'channel'], data)
61+
video_stream = db.session.query(
62+
VideoStream.query.filter_by(id=data['video_stream']).exists()
63+
).scalar()
64+
65+
channel = db.session.query(
66+
VideoChannel.query.filter_by(id=data['channel']).exists()
67+
).scalar()
68+
if not video_stream and not channel:
69+
raise ObjectNotFound(
70+
{'parameter': 'id'},
71+
"Incorrect video_stream and channel data in request body",
72+
)
73+
74+
schema = TranslationChannelSchema
75+
decorators = (
76+
jwt_required,
77+
api.has_permission('auth_required', methods="POST", model=TranslationChannel),
78+
)
79+
methods = ['POST']
80+
data_layer = {
81+
'session': db.session,
82+
'model': TranslationChannel,
83+
'methods': {"before_post": before_post},
84+
}
85+
86+
87+
class TranslationChannelsDetail(ResourceDetail):
88+
"""Plain Pattern for the Translation Channel Detail class"""
89+
90+
schema = TranslationChannelSchema
91+
decorators = (jwt_required,)
92+
methods = ['GET', 'PATCH', 'DELETE']
93+
data_layer = {
94+
'session': db.session,
95+
'model': TranslationChannel,
96+
'methods': {},
97+
}
98+
99+
100+
class TranslationChannelsRelationship(ResourceRelationship):
101+
"""Plain pattern for the Translation Channel Relationship class"""
102+
103+
schema = TranslationChannelSchema
104+
data_layer = {
105+
'session': db.session,
106+
'model': TranslationChannel,
107+
'methods': {},
108+
}

app/api/video_channel_stream.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
from flask_rest_jsonapi import ResourceDetail, ResourceList
2+
3+
from app.api.bootstrap import api
4+
from app.api.helpers.db import safe_query_kwargs
5+
from app.api.helpers.permission_manager import has_access, is_logged_in
6+
from app.api.schema.video_channel import VideoChannelSchema, VideoChannelSchemaPublic
7+
from app.models import db
8+
from app.models.video_channel import VideoChannel
9+
from app.models.video_stream import VideoStream
10+
11+
12+
class VideoChannelListPost(ResourceList):
13+
"""Post to Video Channel List"""
14+
15+
methods = ['POST']
16+
decorators = (api.has_permission('is_admin', methods="POST"),)
17+
schema = VideoChannelSchema
18+
data_layer = {
19+
'session': db.session,
20+
'model': VideoChannel,
21+
}
22+
23+
24+
class VideoChannelList(ResourceList):
25+
"""List of Video Channels"""
26+
27+
def before_get(self, unused_args, unused_kwargs):
28+
"""Providing the requester with the (public) video channel schema"""
29+
if is_logged_in() and has_access('is_admin'):
30+
self.schema = VideoChannelSchema
31+
else:
32+
self.schema = VideoChannelSchemaPublic
33+
34+
methods = ['GET']
35+
schema = VideoChannelSchemaPublic
36+
data_layer = {
37+
'session': db.session,
38+
'model': VideoChannel,
39+
}
40+
41+
42+
class VideoChannelDetail(ResourceDetail):
43+
"""Details of Video Channel"""
44+
45+
def before_get(self, unused_args, kwargs):
46+
"""
47+
Providing the requester with the (public) video channel schema,
48+
as well as further details.
49+
"""
50+
if is_logged_in() and has_access('is_admin'):
51+
self.schema = VideoChannelSchema
52+
else:
53+
self.schema = VideoChannelSchemaPublic
54+
55+
if kwargs.get('video_stream_id'):
56+
stream = safe_query_kwargs(VideoStream, kwargs, 'video_stream_id')
57+
kwargs['id'] = stream.channel_id
58+
59+
schema = VideoChannelSchema
60+
decorators = (
61+
api.has_permission(
62+
'is_admin',
63+
methods="PATCH,DELETE",
64+
),
65+
)
66+
data_layer = {
67+
'session': db.session,
68+
'model': VideoChannel,
69+
}

app/models/translation_channels.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from sqlalchemy.orm import backref
2+
3+
from app.models import db
4+
from app.models.video_channel import VideoChannel
5+
6+
7+
class TranslationChannel(db.Model):
8+
"""Translation Channel"""
9+
10+
__tablename__ = "translation_channels"
11+
id = db.Column(db.Integer, primary_key=True)
12+
video_stream_id = db.Column(
13+
db.Integer, db.ForeignKey('video_streams.id', ondelete='CASCADE')
14+
)
15+
video_stream = db.relationship('VideoStream', backref=backref('video_stream'))
16+
17+
channel_id = db.Column(
18+
db.Integer, db.ForeignKey('video_channels.id', ondelete='CASCADE')
19+
)
20+
channel = db.relationship(VideoChannel, backref='stream_channels')
21+
name = db.Column(db.String)
22+
url = db.Column(db.String)

0 commit comments

Comments
 (0)