From 440063bbd005944eb30906d4e76920526223c369 Mon Sep 17 00:00:00 2001 From: ThisBirchWood Date: Tue, 28 Jan 2025 20:36:13 +0000 Subject: [PATCH] UPDATE: Revamped database class, and implemented some routes in streams.py --- web_server/blueprints/streams.py | 114 +++++++++++++++------------ web_server/database/app.db | Bin 73728 -> 73728 bytes web_server/database/database.py | 55 +++++++++---- web_server/database/testing_data.sql | 4 +- web_server/utils/stream_utils.py | 40 +++++++--- web_server/utils/user_utils.py | 2 +- 6 files changed, 130 insertions(+), 85 deletions(-) diff --git a/web_server/blueprints/streams.py b/web_server/blueprints/streams.py index 93fce7a..e5cff8d 100644 --- a/web_server/blueprints/streams.py +++ b/web_server/blueprints/streams.py @@ -1,8 +1,8 @@ from flask import Blueprint, session, jsonify, g -from utils.stream_utils import streamer_live_status, streamer_most_recent_stream, user_stream, followed_live_streams +from utils.stream_utils import streamer_live_status, streamer_most_recent_stream, user_stream, followed_live_streams, followed_streamers from utils.user_utils import get_user_id -from utils import login_required -from database.database import Database, fetch_data_as_list +from blueprints.utils import login_required +from database.database import Database stream_bp = Blueprint("stream", __name__) @@ -11,24 +11,12 @@ def get_sample_streams() -> list[dict]: """ Returns a list of (sample) streams live right now """ - - # top 25, if not logged in - # if logged in, show streams that match user's tags - # user attains tags from the tags of the streamers they follow, and streamers they've watched - - # TODO Add a category field to the stream object - db = Database() - cursor = db.create_connection() - - # fetch top 25 most viewed live streams if not logged in # TODO Add a check to see if user is logged in, if they are, find streams that match categories they follow - query = """SELECT * FROM streams - ORDER BY num_viewers DESC - LIMIT 25; """ - - streams = fetch_data_as_list(cursor, query) - - + db = Database() + db.create_connection() + streams = db.fetchall("""SELECT * FROM streams + ORDER BY num_viewers DESC + LIMIT 25; """) return jsonify({ "streams": streams }) @@ -69,30 +57,34 @@ def get_categories() -> list[dict]: """ db = Database() - cursor = db.create_connection() - - # fetch top categories by number of viewers - query = """SELECT categories.category_id, category_name, SUM(num_viewers) as num_viewers FROM categories, streams + db.create_connection() + categories = db.fetchall("""SELECT categories.category_id, category_name, SUM(num_viewers) as num_viewers FROM categories, streams WHERE categories.category_id = streams.category_id GROUP BY category_name ORDER BY SUM(num_viewers) DESC - LIMIT 25; """ + LIMIT 25; """) - categories = fetch_data_as_list(cursor, query) return jsonify({'categories': categories}) @login_required -@stream_bp.route('/get_followed_categories') -def get_followed_categories() -> list | list[dict]: +@stream_bp.route('/get_recommended_categories') +def get_recommended_categories() -> list | list[dict]: """ - Queries DB to get a list of followed categories + Queries DB to get a list of recommended categories for the user """ + username = session.get('username') + user_id = get_user_id(username) + db = Database() - cursor = db.create_connection() + db.create_connection() + categories = db.fetchall("""SELECT categories.category_id, categories.category_name, favourability + FROM categories, user_preferences + WHERE user_id = ? AND categories.category_id = user_preferences.category_id, + ORDER BY favourability DESC""", (user_id,)) + + return jsonify({'categories': categories}) - # fetch categories that the user follows - @stream_bp.route('/get_streamer_data/') def get_streamer_data(streamer_username): @@ -107,48 +99,65 @@ def get_streamer_status(streamer_username): """ Returns a streamer's status, if they are live or not and their most recent stream """ - return {"status": "live", "streamId": 1} - streamers_id = streamer_id(streamer_username) - if not streamers_id: - return # whatever - streamer_status = streamer_live_status(streamers_id) - stream_id = streamer_most_recent_stream(streamers_id) - return {"live": streamer_status, "streamerId": streamers_id, "streamId": stream_id} + user_id = get_user_id(streamer_username) + if not user_id: + return jsonify({ + "error": "User not found" + }) + + is_live = streamer_live_status(user_id) + most_recent_stream = streamer_most_recent_stream(user_id) -@stream_bp.route('/get_stream_data/', methods=['GET']) + if not most_recent_stream: + most_recent_stream = {'stream_id': None} + + return jsonify({ + "is_live": is_live, + "most_recent_stream": most_recent_stream['stream_id'] + }) + + +@stream_bp.route('/get_stream_data/', methods=['GET']) def get_stream(streamer_username): """ Returns a streamer's most recent stream data """ - return # Whatever + user_id = get_user_id(streamer_username) + if not user_id: + return jsonify({ + "error": "User not found" + }) + + return jsonify(streamer_most_recent_stream(user_id)) -@stream_bp.route('/get_stream_data//', methods=['GET']) +@stream_bp.route('/get_stream_data//', methods=['GET']) def get_specific_stream(streamer_username, stream_id): """ Returns a streamer's stream data given stream_id """ - stream = user_stream(streamer_username, stream_id) + user_id = get_user_id(streamer_username) + stream = user_stream(user_id, stream_id) if stream: - return stream + return jsonify(stream) - return # whatever + return jsonify({ + "error": "Stream not found" + }) # @login_required -# need to add in a lock to this route for only logged in users since we will be taking from the session - -@stream_bp.route('/get_followed_streams', methods=['GET']) +@login_required +@stream_bp.route('/get_followed_streamers', methods=['GET']) def get_followed_streamers(): """ Queries DB to get a list of followed streamers """ username = session.get('username') user_id = get_user_id(username) - live_following_streams = followed_live_streams(user_id) - if not followed_live_streams: - return # whatever + + live_following_streams = followed_streamers(user_id) return live_following_streams @@ -156,7 +165,8 @@ def get_followed_streamers(): def stream_thumbnail_snapshot(streamer_id): """ Function to be called periodically which saves the current live stream as an img to be used for the thumbnail to be displayed - will be asking streamer guy how to get the picture + will be asking streamer guy how to get the picture + will also be asking myself how to do this - Dylan will be saved as a png stream_id.streamer_id.png or similar to create a unique image """ return diff --git a/web_server/database/app.db b/web_server/database/app.db index 2a24de60b1993646c7453310742dc37dd2ae9c33..7b7a56e8095353f977d06e378859b391339c215c 100644 GIT binary patch delta 303 zcmZoTz|wGlWrDQeItB&?At2@fVipDl#*~RV#=7em^mGDvS(Y;JGJRy=U&j~4^MPd@ zcP&dYt0wb%rjHyF?5EfbI2W=ya9-oj->fL`o{@!RDPzgxdZr?l#;V3tAma~X%H$g6 zLT3BMq{-oI_Pi|j7&w7eFfiygCa_KZ$gImVjlml%(3II2J2{8Njs?g_1~RHAr?I{R zI)!1@!MvcM&6F z{N^t_nT(8ao8$P-@(UU}q~|7P=IABo=khd|TUZ)Qekd=$Ia+R|ARj9~I|Kh&{<-`) n{8s$zn*|lV@@;0fzbnAVy7{TUQ35B(c6Ja3NzFH25+DZ3=C|Iv20+5Ze!HsjV!vnoa_u8V1cI0M(@eGtko<)M(k!? z)=Wlb`^K2bX)N|Ejbe>qlV`C_WA<(goqU$vk1=Gk3`Z*?(7g<^CNJfj#?l None: self._db = os.path.join(os.path.abspath(os.path.dirname(__file__)), "app.db") + self.cursor = None def create_connection(self) -> sqlite3.Cursor: conn = sqlite3.connect(self._db) conn.row_factory = sqlite3.Row self._conn = conn - cursor = conn.cursor() - return cursor + self.cursor = conn.cursor() + return self.cursor + + def fetchall(self, query: str, parameters=None) -> list[dict]: + if parameters: + self.cursor.execute(query, parameters) + else: + self.cursor.execute(query) + + result = self.cursor.fetchall() + return self.convert_to_list_dict(result) + + def fetchone(self, query: str, parameters=None) -> list[dict]: + if parameters: + self.cursor.execute(query, parameters) + else: + self.cursor.execute(query) + + result = self.cursor.fetchone() + return self.convert_to_list_dict(result) + + def convert_to_list_dict(self, result): + """ + Converts a query result to a list of dictionaries + """ + # Get the column names from the cursor + columns = [description[0] for description in self.cursor.description] + + if not result: + # for empty result + return [] + elif isinstance(result, sqlite3.Row): + # for fetchone + return dict(zip(columns, result)) + else: + # for fetchall or fetchmany + return [dict(zip(columns, row)) for row in result] def commit_data(self): try: @@ -19,17 +55,4 @@ class Database: print(e) def close_connection(self) -> None: - self._conn.close() - -def fetch_data_as_list(cursor, query, params=None): - # Execute the query with parameters (if any) - cursor.execute(query, params or []) - - # Get the column names from the cursor - columns = [description[0] for description in cursor.description] - - # Convert rows to dictionaries - rows = cursor.fetchall() - result = [dict(zip(columns, row)) for row in rows] - - return result \ No newline at end of file + self._conn.close() \ No newline at end of file diff --git a/web_server/database/testing_data.sql b/web_server/database/testing_data.sql index 529f0e6..dfba1fa 100644 --- a/web_server/database/testing_data.sql +++ b/web_server/database/testing_data.sql @@ -53,7 +53,5 @@ SELECT * FROM user_preferences; SELECT * FROM subscribes; SELECT * FROM categories; -INSERT INTO streams (user_id, title, start_time, num_viewers, isLive, vod_id, category_id) VALUES -(6, 'Epic Gaming Session 2', '2025-01-26 18:00:00', 800, 1, NULL, 1); INSERT INTO users (username, password, email, num_followers, stream_key, is_partnered, bio) VALUES -('GamerDude2', 'password123', 'gamerdude2@gmail.com', 3200, '6789', 0, 'Streaming my gaming adventures!'); +('GamerDude2', 'password123', 'gamerdude3@gmail.com', 3200, '7890', 0, 'Streaming my gaming adventures!'); diff --git a/web_server/utils/stream_utils.py b/web_server/utils/stream_utils.py index d10005b..51578bc 100644 --- a/web_server/utils/stream_utils.py +++ b/web_server/utils/stream_utils.py @@ -1,52 +1,66 @@ from database.database import Database from typing import Optional +import sqlite3 def streamer_live_status(user_id: int) -> bool: """ - Returns whether the given streamer is live + Returns boolean on whether the given streamer is live """ db = Database() cursor = db.create_connection() return bool(cursor.execute("SELECT 1 FROM streams WHERE user_id = ? AND isLive = 1 ORDER BY stream_id DESC", (user_id,)).fetchone()) -def followed_live_streams(user_id: int): +def followed_live_streams(user_id: int) -> list[dict]: """ Searches for streamers who the user followed which are currently live """ db = Database() cursor = db.create_connection() - live_streams = cursor.execute(""" + live_streams = db.fetchall(""" SELECT user_id, stream_id, title, num_viewers FROM streams WHERE user_id IN (SELECT followed_id FROM follows WHERE user_id = ?) AND stream_id = (SELECT MAX(stream_id) FROM streams WHERE user_id = streams.user_id) AND isLive = 1; - """, (user_id,)).fetchall() + """, (user_id,)) return live_streams -def streamer_most_recent_stream(user_id: int): +def followed_streamers(user_id: int) -> list[dict]: + """ + Returns a list of streamers who the user follows + """ + db = Database() + db.create_connection() + + followed_streamers = db.fetchall(""" + SELECT user_id, username + FROM users + WHERE user_id IN (SELECT followed_id FROM follows WHERE user_id = ?); + """, (user_id,)) + + return followed_streamers + +def streamer_most_recent_stream(user_id: int) -> dict: """ Returns data of the most recent stream by a streamer """ db = Database() - cursor = db.create_connection() - - most_recent_stream = cursor.execute("""SELECT * FROM streams WHERE + db.create_connection() + most_recent_stream = db.fetchone("""SELECT * FROM streams WHERE user_id = ? AND stream_id = (SELECT MAX(stream_id) FROM - streams WHERE user_id = ?)""", (user_id, user_id)).fetchone() + streams WHERE user_id = ?)""", (user_id, user_id)) return most_recent_stream -def user_stream(user_id: int, stream_id: int): +def user_stream(user_id: int, stream_id: int) -> dict: """ Returns data of a streamers selected stream """ db = Database() cursor = db.create_connection() - - stream = cursor.execute("SELECT * FROM streams WHERE user_id = ? AND stream_id = ?", (user_id,stream_id)).fetchone() + stream = db.fetchone("SELECT * FROM streams WHERE user_id = ? AND stream_id = ?", (user_id,stream_id)) - return stream + return stream \ No newline at end of file diff --git a/web_server/utils/user_utils.py b/web_server/utils/user_utils.py index 00afede..5c8ff4d 100644 --- a/web_server/utils/user_utils.py +++ b/web_server/utils/user_utils.py @@ -18,7 +18,7 @@ def get_user_id(username: str) -> Optional[int]: try: data = cursor.execute( - "SELECT user_id FROM user WHERE username = ?", + "SELECT user_id FROM users WHERE username = ?", (username,) ).fetchone() return data[0] if data else None