diff --git a/.gitignore b/.gitignore index c100deb..d7472ee 100644 --- a/.gitignore +++ b/.gitignore @@ -157,5 +157,5 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +.idea/ .DS_Store \ No newline at end of file diff --git a/app/docker/tablas.sql b/app/docker/tablas.sql index 1322ba2..f5f45eb 100644 --- a/app/docker/tablas.sql +++ b/app/docker/tablas.sql @@ -5,12 +5,16 @@ CREATE TABLE IF NOT EXISTS users_service.users ( name VARCHAR(255), email VARCHAR(255) UNIQUE NOT NULL, gender VARCHAR(20), - photo VARCHAR(255) + photo VARCHAR(255), + birthdate DATE, + location JSONB, + nickname VARCHAR(30), + biography VARCHAR(255) ); INSERT INTO - users_service.users (name, email) -VALUES ('Agus', 'agus@fi.uba.ar'), - ('Pach', 'pach@fi.uba.ar'), - ('Sofi', 'sofi@fi.uba.ar'), - ('Violeta', 'violeta@fi.uba.ar'); \ No newline at end of file + users_service.users (name, email, birthdate, location) +VALUES ('Agus', 'agus@fi.uba.ar', TO_DATE('1999-01-29', 'YYYY-MM-DD'), '{"lat": 20, "long": 100}'), + ('Pach', 'pach@fi.uba.ar', TO_DATE('1999-08-06', 'YYYY-MM-DD'), '{"lat": 10, "long": 200}'), + ('Sofi', 'sofi@fi.uba.ar', TO_DATE('1998-04-26', 'YYYY-MM-DD'), '{"lat": 1190, "long": 500}'), + ('Violeta', 'violeta@fi.uba.ar', TO_DATE('1998-05-12', 'YYYY-MM-DD'), '{"lat": 330, "long": 2333}'); \ No newline at end of file diff --git a/app/exceptions/UserException.py b/app/exceptions/UserException.py index 29a4ac0..022828e 100644 --- a/app/exceptions/UserException.py +++ b/app/exceptions/UserException.py @@ -8,6 +8,15 @@ def __init__(self, id: int): super().__init__(status_code=status_code, detail=detail) +class InvalidData(HTTPException): + def __init__(self): + status_code = status.HTTP_400_BAD_REQUEST + super().__init__( + status_code=status_code, + detail="Invalid user data was provided" + ) + + class InvalidURL(HTTPException): def __init__(self, id: int): status_code = status.HTTP_400_BAD_REQUEST diff --git a/app/models/users.py b/app/models/users.py index a32ed6b..ab73efe 100644 --- a/app/models/users.py +++ b/app/models/users.py @@ -1,4 +1,4 @@ -from sqlalchemy import Column, Integer, String +from sqlalchemy import Column, Integer, String, JSON, Date from models.database import Base from os import environ @@ -13,3 +13,7 @@ class User(Base): email = Column(String, nullable=False) gender = Column(String, nullable=True) photo = Column(String, nullable=True) + birthdate = Column(Date, nullable=True) + location = Column(JSON, nullable=True) + nickname = Column(String, nullable=True) + biography = Column(String, nullable=True) diff --git a/app/repository/Users.py b/app/repository/Users.py index e633e95..54cfdaa 100644 --- a/app/repository/Users.py +++ b/app/repository/Users.py @@ -4,6 +4,7 @@ from typing import Optional from models.database import Base from models.users import User +from datetime import date class UsersRepository: @@ -48,6 +49,10 @@ def create_user( name: Optional[str] = None, gender: Optional[str] = None, photo: Optional[str] = None, + nickname: Optional[str] = None, + biography: Optional[str] = None, + location: Optional[dict] = None, + birthdate: Optional[date] = None ) -> User: user_data = {"email": email} @@ -57,6 +62,14 @@ def create_user( user_data["gender"] = gender if photo is not None: user_data["photo"] = photo + if location is not None: + user_data["location"] = location + if birthdate is not None: + user_data["birthdate"] = birthdate + if nickname is not None: + user_data["nickname"] = nickname + if biography is not None: + user_data["biography"] = biography new_user = User(**user_data) self.session.add(new_user) diff --git a/app/schemas/Schemas.py b/app/schemas/Schemas.py index 0997343..d4d0b58 100644 --- a/app/schemas/Schemas.py +++ b/app/schemas/Schemas.py @@ -1,5 +1,6 @@ +from datetime import date from pydantic import BaseModel -from typing import Optional +from typing import Optional, Dict class UserSchema(BaseModel): @@ -8,10 +9,21 @@ class UserSchema(BaseModel): email: str gender: str photo: str + birthdate: date + location: Dict + nickname: str + biography: str class CreateUserSchema(BaseModel): email: str + name: Optional[str] = None + gender: Optional[str] = None + photo: Optional[str] = None + birthdate: Optional[date] = None + location: Optional[Dict] = None + nickname: Optional[str] = None + biography: Optional[str] = None class LoginRequest(BaseModel): @@ -21,4 +33,8 @@ class LoginRequest(BaseModel): class UpdateUserSchema(BaseModel): name: Optional[str] = None gender: Optional[str] = None - photo: str + photo: Optional[str] = None + birthdate: Optional[date] = None + location: Optional[Dict] = None + nickname: Optional[str] = None + biography: Optional[str] = None diff --git a/app/service/Users.py b/app/service/Users.py index 74260ee..514799e 100644 --- a/app/service/Users.py +++ b/app/service/Users.py @@ -1,4 +1,4 @@ -from exceptions.UserException import UserNotFound, InvalidURL +from exceptions.UserException import UserNotFound, InvalidData, InvalidURL from exceptions.LoginException import AuthenticationError from models.users import User from repository.Users import UsersRepository @@ -21,8 +21,23 @@ def get_all_users(self): return self.user_repository.get_all_users() def create_user(self, user_data: dict): - email = user_data.get("email") - return self.user_repository.create_user(email) + if not self._validate_location(user_data.get("location")): + raise InvalidData() + return self.user_repository.create_user(**user_data) + + def update_user(self, user_id: int, update_data: dict): + # TODO: aca habria que chequear a partir del token, session o algo que + # es el propio usuario editando sus datos y no permitir + # que un usuario edite los de un tercero + self.get_user(user_id) + filtered_update_data = {k: v for k, v in update_data.items() + if v is not None} + if 'photo' in filtered_update_data: + photo_url = filtered_update_data['photo'] + if not re.match(r'^https?://(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,6}' + r'(?:/[^/#?]+)+(?:\?.*)?$', photo_url): + raise InvalidURL("Invalid photo URL") + self.user_repository.edit_user(user_id, filtered_update_data) def login(self, auth_code: str): access_token = self._get_access_token(auth_code) @@ -30,7 +45,6 @@ def login(self, auth_code: str): raise AuthenticationError("Authentication code is invalid") user_info = self._get_user_info(access_token) - print(user_info) user = self.user_repository.get_user_by_email(user_info["email"]) if user is None: @@ -72,17 +86,9 @@ def _get_user_info(self, access_token): user_data['photo'] = response.json().get("picture") return user_data - def update_user(self, user_id: int, update_data: dict): - # TODO: aca habria que chequear a partir del token, session o algo que - # es el propio usuario editando sus datos y no permitir - # que un usuario edite los de un tercero - self.get_user(user_id) - filtered_update_data = {k: v for k, v in update_data.items() - if v is not None} - if 'photo' in filtered_update_data: - photo_url = filtered_update_data['photo'] - if not re.match(r'^https?://(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,6}' - r'(?:/[^/#?]+)+\.(?:jpg|jpeg|png|gif)$', - photo_url): - raise InvalidURL("Invalid photo URL") - self.user_repository.edit_user(user_id, filtered_update_data) + def _validate_location(self, location): + if "lat" in location and "long" in location: + if -90 <= location["lat"] <= 90 and \ + -180 <= location["long"] <= 180: + return True + return False