from modules.posts import Comment, Post, Posts
from modules.users import User, Users, User_lite
from modules.utils.exceptions import *
from modules.utils.generals import check_password, logify, is_valid_username, is_valid_email, is_valid_password, is_valid_image
from modules.utils.log import Log
from modules.utils.token import Token
from typing import Any, Dict, List
[docs]
class App:
"""Manages al the app functions."""
Log.info("Application initialized.")
#Access
[docs]
@staticmethod
def login(username_or_email: str, password: str) -> Dict[str, Any]:
"""
Log an user into the app.
Args:
username_or_email (str): User's username or email.
password (str): User's password.
Raises:
InvalidCredential: If username or email aren't valid.
InvalidCredential: If password isn't valid.
IncorrectCredential: If password doesn't match.
NotFound: If the user no exist.
GeneralError: General errors.
Returns:
Dict[str, Any]: The username and the token in a dict: {"username": username, "token": token}.
"""
try:
if not username_or_email or (not is_valid_username(username_or_email) and not is_valid_email(username_or_email)):
raise InvalidCredential("Username or email")
if not password or not is_valid_password(password):
raise InvalidCredential("Password")
user: User = Users.get_user(username_or_email.lower(), "email") if "@" in username_or_email else Users.get_user(username_or_email.capitalize(), "username")
if user:
if check_password(password, user.password):
Log.info(f"User {user.ID} logged in")
return {"username": user.username, "token": user.token()}
raise IncorrectCredential(user.ID)
else:
raise NotFound("user")
except Exception as e:
raise GeneralError(
data="Error logging the user",
error=e,
log=logify(["password"])
) from e
[docs]
@staticmethod
def register(username: str, email: str, password: str) -> Dict[str, Any]:
"""
Register a user into the app.
Args:
username (str): The new's user username.
email (str): The new's user email.
password (str): The new's user password.
Raises:
InvalidCredential: If username isn't valid.
InvalidCredential: If email isn't valid.
InvalidCredential: If password isn't valid.
AlreadyInUse: If username is already in use by another user.
AlreadyInUse: If email is already in use by another user.
GeneralError: General errors.
Returns:
Dict[str, Any]: The username and the token in a dict: {"username": username, "token": token}.
"""
try:
if not username or not is_valid_username(username):
raise InvalidCredential("username")
if not email or not is_valid_email(email):
raise InvalidCredential("email")
if not password or not is_valid_password(password):
raise InvalidCredential("password")
username = username.capitalize()
email = email.lower()
if Users.get_user(username, "username"):
raise AlreadyInUse("username")
if Users.get_user(email, "email"):
raise AlreadyInUse("email")
user: User = Users.create_user(username, email, password)
if user:
Log.info(f"User {user.ID} created and logged in")
return {"username": user.username, "token": user.token()}
raise GeneralError("Error creating the user")
except Exception as e:
raise GeneralError(
data=f"Error creating the user",
error=e,
log=logify(["password"])
) from e
#Users
[docs]
@staticmethod
def get_user(username: str, request_user: Dict[str, Any]) -> Dict[str, Any]:
"""
Fetch an user.
Args:
username (str): The user's username to fetch.
request_user (Dict[str, Any]): The user that makes the request.
Raises:
NotFound: If the user isn't found.
GeneralError: General errors.
Returns:
Dict[str, Any]: The fetched user data.
"""
try:
request_User: User_lite = User_lite(request_user)
user: User = Users.get_user(username.capitalize(), "username")
if user:
Log.info(f"User {user.ID} fetched by User {request_User.ID}")
return user.filter(request_User)
raise NotFound("user")
except Exception as e:
raise GeneralError(
data=f"Error fetching the user {username}",
error=e
) from e
#Follows
[docs]
@staticmethod
def follownt(following_username: str, request_user: Dict[str, Any]) -> bool:
"""
Follows or unfollows an user.
Args:
following_username (str): User's username that will be followed or unfollowed.
request_user (Dict[str, Any]): User that makes the request.
Raises:
GeneralError: General errors.
NotFound: If user isn't found.
Returns:
bool: True if the operation was sucessfull.
"""
try:
follower: User_lite = User_lite(request_user)
following: User = Users.get_user(following_username, "username")
if follower == following:
raise GeneralError("No puedes seguirte a ti mismo")
if following:
if follower.ID not in following.followers:
if Users.follow(follower, following):
Log.info(f"User {following.ID} followed by {follower.ID}")
return True
raise GeneralError(
data="Error al seguir al usuario",
log=f"{follower.ID} to {following.ID}"
)
else:
if Users.unfollow(follower, following):
Log.info(f"User {following.ID} unfollowed by {follower.ID}")
return True
raise GeneralError(
data="Error al dejar de seguir al usuario",
log=f"{follower.ID} to {following.ID}"
)
raise NotFound("user")
except Exception as e:
raise GeneralError(
data="Error al seguir o no",
error=e,
log=f"{following.ID} follownt by {follower.ID}"
) from e
#Posts
[docs]
@staticmethod
def get_posts(request_user: Dict[str, Any]) -> List[Dict[str, Any]]:
"""
Fetch all posts.
Args:
request_user (Dict[str, Any]): User that makes the request.
Raises:
GeneralError: General errors.
Returns:
List[Dict[str, Any]]: All posts.
"""
try:
request_User: User_lite = User_lite(request_user)
Log.info(f"All posts fetched by {request_User.ID if request_User else 'invited'}")
return Posts.get_posts()
except Exception as e:
raise GeneralError(
data="Error al obtener los posts",
error=e
) from e
[docs]
@staticmethod
def get_post(post_id: str, request_user: Dict[str, Any]) -> Dict[str, Any]:
"""
Fetch an specific post,
Args:
post_id (str): The post's ID.
request_user (Dict[str, Any]): The user that makes the request.
Raises:
NotFound: If post isn´t found.
GeneralError: General errors.
Returns:
Dict[str, Any]: The post data.
"""
try:
request_User: User_lite = User_lite(request_user)
try:
object_id_post = ObjectId(post_id)
except:
raise NotFound("post")
post: Post = Posts.get_post(object_id_post)
if post:
Log.info(f"Post {post.ID} fetched by {request_User.ID}")
return post.filter(request_User)
raise NotFound("post")
except Exception as e:
raise GeneralError(
data=f"Error al obtener post {post_id}",
error=e
) from e
[docs]
@staticmethod
def create_post(name: str, location: str, review: str, rating: float, imageUrl: str, creator: Dict[str, Any]) -> str:
"""
Creates a new post.
Args:
name (str): The new post's name.
location (str): The new post's location.
review (str): The new post's review.
rating (float): The new post's rating.
imageUrl (str): The new post's imageUrl.
creator (Dict[str, Any]): The new post's creator.
Raises:
ValueError: If the rating isn't valid.
ValueError: If the imange isn't valid.
GeneralError: General errors.
Returns:
str: The created post's ID.
"""
try:
if rating < 0 or rating > 10:
raise ValueError("Rating inválido")
if not is_valid_image(imageUrl):
raise ValueError("Imagen inválida")
request_User: User_lite = User_lite(creator)
post: Post = Posts.create_post(name.capitalize(), location.title(), review.capitalize(), rating, imageUrl, request_User)
if post:
Log.info(f"Post {post.ID} created")
return str(post.ID)
Log.error(f"Error creating the post {post.json()}")
raise GeneralError("Error al crear el post")
except Exception as e:
raise GeneralError(
data="Error al crear el post",
error=e,
log=f"name: {name}, location: {location}, review: {review}, rating: {rating}, imageUrl: {imageUrl}, creator: {creator}"
) from e
[docs]
@staticmethod
def edit_post(post_id: str, name: str, location: str, review: str, rating: float, imageUrl: str, request_user: Dict[str, Any]) -> bool:
"""
Edit a post.
Args:
post_id (str): Edited post's ID.
name (str): Edited post's name.
location (str): Edited post's location.
review (str): Edited post's review.
rating (float): Edited post's rating.
imageUrl (str): Edited post's imageUrl.
request_user (Dict[str, Any]): User that makes the request..
Raises:
ValueError: If the rating isn't valid.
ValueError: If the imange isn't valid.
Unauthorized: If the request user isn't is authorized to edit.
NotFound: If post isn't founded.
GeneralError: General error.
Returns:
bool: True id was edited.
"""
try:
request_User: User_lite = User_lite(request_user)
post: Post = Posts.get_post(post_id)
new_data: Dict[str, Any] = {}
if name:
new_data["name"] = name
if location:
new_data["location"] = location
if review:
new_data["review"] = review
if rating and rating != 0:
rating = int(rating)
if rating < 0 or rating > 10:
raise ValueError("Rating inválido")
new_data["rating"] = rating
if imageUrl:
if not is_valid_image(imageUrl):
raise ValueError("Imagen inválida")
new_data["imageUrl"] = imageUrl
if not new_data:
return True
if post:
if request_User != post.creator:
raise Unauthorized()
edited: Post = Posts.edit_post(post, new_data)
if edited:
Log.info(f"Post {post.ID} edited {{original: {post.filter()}, edit: {edited.filter()}}}")
return True
Log.error(f"Error editing the post {post_id}, new_data: {new_data}")
raise GeneralError("Error al editar el Post")
raise NotFound("post")
except Exception as e:
raise GeneralError(
data="Error al editar el post",
error=e
) from e
[docs]
@staticmethod
def delete_post(post_id: str, request_user: Dict[str, Any]) -> bool:
"""
Delete a post.
Args:
post_id (str): Post's ID.
request_user (Dict[str, Any]): User that makes the request.
Raises:
Unauthorized: If the request user isn't is authorized to delete.
GeneralError: General errors.
NotFound: If post isn't founded.
Returns:
bool: True if was deleted.
"""
try:
request_User: User_lite = User_lite(request_user)
post: Post = Posts.get_post(post_id)
if post:
if request_User != post.creator:
raise Unauthorized()
deleted: Post = Posts.delete_post(post)
if deleted:
Log.info(f"Post {post.ID} deleted {{original: {post.json()}}}")
return True
Log.error(f"Error deleting post {post.filter()}")
raise GeneralError("Error al borrar el post")
raise NotFound("post")
except Exception as e:
raise GeneralError(
data="Error al borrar el post",
error=e
) from e
#Comments
#Token
[docs]
@staticmethod
def verify_token(request_token: str) -> Dict[str, Any]:
"""
Verify a JWT.
Args:
request_token (str): The token to verify
Raises:
GeneralError: General error
Returns:
Dict[str, Any]: {"verify": True, "user": (token info)} or {"verify": False, "error": (error)}
"""
try:
valid: Dict[str, Any] = Token.is_valid(request_token)
return valid
except Exception as e:
raise GeneralError(
data="Error al verificar el token",
error=e
) from e