From 47cbd5695c1ae1a7b08d16d304748a9df7f31e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Forr=C3=B3?= Date: Thu, 28 Jun 2018 16:09:26 +0200 Subject: [PATCH] Add search endpoint to Twitch Logs API --- twitch-logs-api/app.py | 66 ++++++++++++++++++++++++++++++++++++++++-- twitch-logs-api/db.py | 2 +- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/twitch-logs-api/app.py b/twitch-logs-api/app.py index 31cf899..28b12b0 100644 --- a/twitch-logs-api/app.py +++ b/twitch-logs-api/app.py @@ -61,6 +61,7 @@ video_fields = { comment_fields = { 'id': flask_restful.fields.String(attribute='comment.id'), 'video_id': flask_restful.fields.Integer(), + 'video_recorded_at': flask_restful.fields.DateTime(dt_format='iso8601', attribute='video.recorded_at'), 'offset': flask_restful.fields.Float(), 'commenter_id': flask_restful.fields.Integer(attribute='comment.commenter_id'), 'commenter_name': flask_restful.fields.String(attribute='comment.commenter_name'), @@ -74,6 +75,16 @@ comment_fields = { 'updated_at': flask_restful.fields.DateTime(dt_format='iso8601', attribute='comment.updated_at'), } +search_fields = { + **comment_fields, + 'video_title': flask_restful.fields.String(attribute='video.title'), + 'video_game': flask_restful.fields.String(attribute='video.game'), + 'video_length': flask_restful.fields.Integer(attribute='video.length'), + 'video_thumbnail_small': flask_restful.fields.String(attribute='video.thumbnail_small'), + 'video_thumbnail_medium': flask_restful.fields.String(attribute='video.thumbnail_medium'), + 'video_thumbnail_large': flask_restful.fields.String(attribute='video.thumbnail_large'), +} + emote_fields = { 'id': flask_restful.fields.Integer(), 'code': flask_restful.fields.String(), @@ -87,6 +98,14 @@ filter_parser.add_argument('sort_order', type=str) filter_parser.add_argument('page_number', type=int) filter_parser.add_argument('page_size', type=int) +search_parser = flask_restful.reqparse.RequestParser() +search_parser.add_argument('commenter', type=str) +search_parser.add_argument('term', type=str) +search_parser.add_argument('sort_by', type=str) +search_parser.add_argument('sort_order', type=str) +search_parser.add_argument('page_number', type=int) +search_parser.add_argument('page_size', type=int) + class VideoResource(flask_restful.Resource): @flask_restful.marshal_with(video_fields) @@ -132,8 +151,9 @@ class CommentResource(flask_restful.Resource): video = q.first() if not video: flask_restful.abort(404, message='Video {0} does not exist'.format(video_id)) - q = db.session.query(Association).join(Comment).filter( - Association.video_id == video_id, Association.comment_id == comment_id) + q = db.session.query(Association).join(Comment) + q = q.filter((Association.video_id == video_id) &\ + (Association.comment_id == comment_id)) assoc = q.first() if not assoc: flask_restful.abort(404, @@ -149,7 +169,8 @@ class CommentsResource(flask_restful.Resource): video = q.first() if not video: flask_restful.abort(404, message='Video {0} does not exist'.format(id)) - q = db.session.query(Association).join(Comment).filter(Association.video_id == id) + q = db.session.query(Association).join(Comment).join(Video) + q = q.filter(Association.video_id == id) if args['filter']: q = q.filter(Comment.message_body.ilike('%{}%'.format(args['filter']))) count = q.count() @@ -159,6 +180,44 @@ class CommentsResource(flask_restful.Resource): col = getattr(Association, args['sort_by'], None) if not col: col = getattr(Comment, args['sort_by'], None) + if not col: + col = getattr(Video, args['sort_by'].split('video_', 1).pop(), None) + if col: + if args['sort_order']: + order_by = getattr(col, args['sort_order'], None) + if order_by: + q = q.order_by(order_by()) + else: + q = q.order_by(col) + if args['page_size']: + q = q.limit(args['page_size']) + if args['page_number'] and args['page_size']: + q = q.offset(args['page_number'] * args['page_size']) + assocs = q.all() + return assocs, 200, {'X-Total-Count': count} + + +class SearchResource(flask_restful.Resource): + @flask_restful.marshal_with(search_fields) + def get(self): + args = search_parser.parse_args() + if not args['commenter'] and not args['term']: + return [], 200, {'X-Total-Count': 0} + q = db.session.query(Association).join(Comment).join(Video) + if args['commenter']: + q = q.filter(Comment.commenter_name.ilike('%{}%'.format(args['commenter'])) |\ + Comment.commenter_display_name.ilike('%{}%'.format(args['commenter']))) + if args['term']: + q = q.filter(Comment.message_body.ilike('%{}%'.format(args['term']))) + count = q.count() + if args['sort_order'] == 'random': + q = q.order_by(sqlalchemy.func.random()) + elif args['sort_by']: + col = getattr(Association, args['sort_by'], None) + if not col: + col = getattr(Comment, args['sort_by'], None) + if not col: + col = getattr(Video, args['sort_by'].split('video_', 1).pop(), None) if col: if args['sort_order']: order_by = getattr(col, args['sort_order'], None) @@ -196,6 +255,7 @@ api.add_resource(VideoResource, '/videos/') api.add_resource(VideosResource, '/videos') api.add_resource(CommentResource, '/videos//comments/') api.add_resource(CommentsResource, '/videos//comments') +api.add_resource(SearchResource, '/search') api.add_resource(EmoteResource, '/emotes/') api.add_resource(EmotesResource, '/emotes') diff --git a/twitch-logs-api/db.py b/twitch-logs-api/db.py index 06f583a..d5978eb 100644 --- a/twitch-logs-api/db.py +++ b/twitch-logs-api/db.py @@ -20,7 +20,7 @@ class Video(db.Model): updated_at = db.Column(db.DateTime) recorded_at = db.Column(db.DateTime) published_at = db.Column(db.DateTime) - associations = db.relationship('Association') + associations = db.relationship('Association', backref='video') class Comment(db.Model):