* Fix pylint warnings across all 24 Python files in web_server - Add module, class, and function docstrings (C0114, C0115, C0116) - Fix import ordering: stdlib before third-party before local (C0411) - Replace wildcard imports with explicit named imports (W0401) - Remove trailing whitespace and add missing final newlines (C0303, C0304) - Replace dict() with dict literals (R1735) - Remove unused imports and variables (W0611, W0612) - Narrow broad Exception catches to specific exceptions (W0718) - Replace f-string logging with lazy % formatting (W1203) - Fix variable naming: UPPER_CASE for constants, snake_case for locals (C0103) - Add pylint disable comments for necessary global statements (W0603) - Fix no-else-return, simplifiable-if-expression, singleton-comparison - Fix bad indentation in stripe.py (W0311) - Add encoding="utf-8" to open() calls (W1514) - Add check=True to subprocess.run() calls (W1510) - Register Celery task modules via conf.include * Update `package-lock.json` add peer dependencies
299 lines
8.8 KiB
Python
299 lines
8.8 KiB
Python
"""User profile management, following, and subscription utilities."""
|
|
|
|
from typing import Optional, List
|
|
from datetime import datetime, timedelta
|
|
|
|
from database.database import Database
|
|
from dateutil import parser
|
|
|
|
|
|
def get_user_id(username: str) -> Optional[int]:
|
|
"""
|
|
Returns user_id associated with given username
|
|
"""
|
|
with Database() as db:
|
|
data = db.fetchone("""
|
|
SELECT user_id
|
|
FROM users
|
|
WHERE username = ?
|
|
""", (username,))
|
|
return data['user_id'] if data else None
|
|
|
|
def get_username(user_id: str) -> Optional[str]:
|
|
"""
|
|
Returns username associated with given user_id
|
|
"""
|
|
with Database() as db:
|
|
data = db.fetchone("""
|
|
SELECT username
|
|
FROM users
|
|
WHERE user_id = ?
|
|
""", (user_id,))
|
|
return data['username'] if data else None
|
|
|
|
def update_bio(user_id: int, bio: str):
|
|
"""
|
|
Updates users bio given their user_id
|
|
"""
|
|
with Database() as db:
|
|
db.execute("""
|
|
UPDATE users
|
|
SET bio = ?
|
|
WHERE user_id = ?
|
|
""", (bio, user_id))
|
|
|
|
def has_password(email: str):
|
|
"""
|
|
Returns if account associated with this email has password,
|
|
i.e is account from Google OAuth
|
|
"""
|
|
with Database() as db:
|
|
data = db.fetchone("""
|
|
SELECT password
|
|
FROM users
|
|
WHERE email = ?
|
|
""", (email,))
|
|
return data["password"] is not None
|
|
|
|
def get_session_info_email(email: str) -> dict:
|
|
"""
|
|
Returns username and user_id given email
|
|
"""
|
|
with Database() as db:
|
|
session_info = db.fetchone("""
|
|
SELECT user_id, username
|
|
FROM users
|
|
WHERE email = ?
|
|
""", (email,))
|
|
return session_info
|
|
|
|
def is_user_partner(user_id: int) -> bool:
|
|
"""
|
|
Returns True if user is a partner, else False
|
|
"""
|
|
with Database() as db:
|
|
data = db.fetchone("""
|
|
SELECT is_partnered
|
|
FROM users
|
|
WHERE user_id = ?
|
|
""", (user_id,))
|
|
return bool(data)
|
|
|
|
def is_subscribed(user_id: int, subscribed_to_id: int) -> bool:
|
|
"""Returns True if user is subscribed to a streamer, else False"""
|
|
with Database() as db:
|
|
return bool(db.fetchone(
|
|
"""
|
|
SELECT 1
|
|
FROM subscribes
|
|
WHERE user_id = ?
|
|
AND subscribed_id = ?
|
|
AND expires > ?;
|
|
""",
|
|
(user_id, subscribed_to_id, datetime.now())
|
|
))
|
|
|
|
def is_following(user_id: int, followed_id: int) -> bool:
|
|
"""
|
|
Returns where a user is following another
|
|
"""
|
|
with Database() as db:
|
|
result = db.fetchone("""
|
|
SELECT 1
|
|
FROM follows
|
|
WHERE user_id = ?
|
|
AND followed_id = ?
|
|
""", (user_id, followed_id))
|
|
return bool(result)
|
|
|
|
def follow(user_id: int, followed_id: int):
|
|
"""
|
|
Follows followed_id user from user_id user
|
|
"""
|
|
if is_following(user_id, followed_id):
|
|
return {"success": False, "error": "Already following user"}, 400
|
|
|
|
with Database() as db:
|
|
db.execute("""
|
|
INSERT INTO follows (user_id, followed_id)
|
|
VALUES(?,?);
|
|
""", (user_id, followed_id))
|
|
return {"success": True}
|
|
|
|
def unfollow(user_id: int, followed_id: int):
|
|
"""
|
|
Unfollows followed_id user from user_id user
|
|
"""
|
|
if not is_following(user_id, followed_id):
|
|
return {"success": False, "error": "Not following user"}, 400
|
|
with Database() as db:
|
|
db.execute("""
|
|
DELETE FROM follows
|
|
WHERE user_id = ?
|
|
AND followed_id = ?
|
|
""", (user_id, followed_id))
|
|
return {"success": True}
|
|
|
|
def is_following_category(user_id: int, category_id: str):
|
|
"""
|
|
Checks if user is following category
|
|
"""
|
|
with Database() as db:
|
|
result = db.fetchone("""
|
|
SELECT 1
|
|
FROM followed_categories
|
|
WHERE user_id = ?
|
|
AND category_id = ?
|
|
""", (user_id, category_id))
|
|
return bool(result)
|
|
|
|
def follow_category(user_id: int, category_id: str):
|
|
"""
|
|
Follows category given user_id
|
|
"""
|
|
if is_following_category(user_id, category_id):
|
|
return {"success": False, "error": "Already following category"}, 400
|
|
|
|
with Database() as db:
|
|
db.execute("""
|
|
INSERT INTO followed_categories (user_id, category_id)
|
|
VALUES(?,?);
|
|
""", (user_id, category_id))
|
|
return {"success": True}
|
|
|
|
|
|
def unfollow_category(user_id: int, category_id: str):
|
|
"""
|
|
Unfollows category given user_id
|
|
"""
|
|
if not is_following_category(user_id, category_id):
|
|
return {"success": False, "error": "Not following category"}, 400
|
|
|
|
with Database() as db:
|
|
db.execute("""
|
|
DELETE FROM followed_categories
|
|
WHERE user_id = ?
|
|
AND category_id = ?
|
|
""", (user_id, category_id))
|
|
return {"success": True}
|
|
|
|
def subscribe(user_id: int, streamer_id: int):
|
|
"""
|
|
Subscribes user_id to streamer_id
|
|
"""
|
|
# If user is already subscribed then extend the expiration date else create a new entry
|
|
with Database() as db:
|
|
existing = db.fetchone("""
|
|
SELECT expires
|
|
FROM subscribes
|
|
WHERE user_id = ? AND subscribed_id = ?
|
|
""", (user_id, streamer_id))
|
|
if existing:
|
|
db.execute("""
|
|
UPDATE subscribes SET expires = expires + ?
|
|
WHERE user_id = ? AND subscribed_id = ?
|
|
""", (timedelta(days=30), user_id, streamer_id))
|
|
else:
|
|
db.execute("""
|
|
INSERT INTO subscribes
|
|
(user_id, subscribed_id, since, expires)
|
|
VALUES (?,?,?,?)
|
|
""", (user_id, streamer_id, datetime.now(), datetime.now() + timedelta(days=30)))
|
|
|
|
def delete_subscription(user_id: int, subscribed_id: int):
|
|
"""
|
|
Deletes a subscription entry given user_id and streamer_id
|
|
"""
|
|
with Database() as db:
|
|
db.execute("""
|
|
DELETE FROM subscribes
|
|
WHERE user_id = ? AND subscribed_id = ?
|
|
""", (user_id, subscribed_id))
|
|
|
|
|
|
def subscription_expiration(user_id: int, subscribed_id: int) -> int:
|
|
"""
|
|
Returns the amount of time left until user subscription to a streamer ends
|
|
"""
|
|
with Database() as db:
|
|
data = db.fetchone("""
|
|
SELECT expires
|
|
FROM subscribes
|
|
WHERE user_id = ?
|
|
AND subscribed_id = ?
|
|
AND expires > ?
|
|
""", (user_id, subscribed_id, datetime.now()))
|
|
|
|
if data:
|
|
expiration_date = data["expires"]
|
|
remaining_time = (parser.parse(expiration_date) - datetime.now()).seconds
|
|
return remaining_time
|
|
|
|
return 0
|
|
|
|
def get_email(user_id: int) -> Optional[str]:
|
|
"""Returns the email address for a given user_id."""
|
|
with Database() as db:
|
|
email = db.fetchone("""
|
|
SELECT email
|
|
FROM users
|
|
WHERE user_id = ?
|
|
""", (user_id,))
|
|
|
|
return email["email"] if email else None
|
|
|
|
def get_followed_streamers(user_id: int) -> Optional[List[dict]]:
|
|
"""
|
|
Returns a list of streamers who the user follows
|
|
|
|
Returns:
|
|
List of dictionaries with the following structure:
|
|
[
|
|
{
|
|
"user_id": int,
|
|
"username": str
|
|
},
|
|
...
|
|
]
|
|
"""
|
|
with Database() as db:
|
|
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 get_followed_categories(user_id: int) -> Optional[List[dict]]:
|
|
"""
|
|
Returns a list of categories that the user follows
|
|
|
|
Returns:
|
|
List of dictionaries with the following structure:
|
|
[
|
|
{
|
|
"category_id": int,
|
|
"category_name": str
|
|
},
|
|
...
|
|
]
|
|
"""
|
|
with Database() as db:
|
|
followed_categories = db.fetchall("""
|
|
SELECT category_id, category_name
|
|
FROM categories
|
|
WHERE category_id IN (SELECT category_id FROM followed_categories WHERE user_id = ?);
|
|
""", (user_id,))
|
|
return followed_categories
|
|
|
|
def get_user(user_id: int) -> Optional[dict]:
|
|
"""
|
|
Returns information about a user from user_id
|
|
"""
|
|
with Database() as db:
|
|
data = db.fetchone("""
|
|
SELECT user_id, username, bio, num_followers, is_partnered, is_live FROM users
|
|
WHERE user_id = ?;
|
|
""", (user_id,))
|
|
return data
|