Files
gander/web_server/utils/user_utils.py
Christopher Ahern 2758be8680
Some checks are pending
CI / build (3.10) (push) Waiting to run
CI / build (3.8) (push) Waiting to run
CI / build (3.9) (push) Waiting to run
Fix/pylint cleanup (#8)
* 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
2026-02-07 20:57:28 +00:00

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