From 7933404b3a9569576793aea1715115237e965f7e Mon Sep 17 00:00:00 2001 From: feijooso Date: Mon, 1 Apr 2024 02:38:12 -0300 Subject: [PATCH 1/8] add location field --- app/docker/tablas.sql | 13 +++++++------ app/models/users.py | 3 ++- app/schemas/Schemas.py | 3 ++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/docker/tablas.sql b/app/docker/tablas.sql index 1322ba2..697f94f 100644 --- a/app/docker/tablas.sql +++ b/app/docker/tablas.sql @@ -5,12 +5,13 @@ 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), + location JSONB ); 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, location) +VALUES ('Agus', 'agus@fi.uba.ar', '{"lat": 20, "long": 100}'), + ('Pach', 'pach@fi.uba.ar','{"lat": 10, "long": 200}'), + ('Sofi', 'sofi@fi.uba.ar', '{"lat": 1190, "long": 500}'), + ('Violeta', 'violeta@fi.uba.ar', '{"lat": 330, "long": 2333}'); \ No newline at end of file diff --git a/app/models/users.py b/app/models/users.py index a32ed6b..443a9da 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 from models.database import Base from os import environ @@ -13,3 +13,4 @@ class User(Base): email = Column(String, nullable=False) gender = Column(String, nullable=True) photo = Column(String, nullable=True) + location = Column(JSON, nullable=True) diff --git a/app/schemas/Schemas.py b/app/schemas/Schemas.py index 025c68a..46239d3 100644 --- a/app/schemas/Schemas.py +++ b/app/schemas/Schemas.py @@ -1,5 +1,5 @@ from pydantic import BaseModel - +from typing import Dict class UserSchema(BaseModel): id: int @@ -7,6 +7,7 @@ class UserSchema(BaseModel): email: str gender: str photo: str + location: Dict class CreateUserSchema(BaseModel): From 266fbb1cdc05711642c5a5c4f57b50980810d077 Mon Sep 17 00:00:00 2001 From: feijooso Date: Mon, 1 Apr 2024 02:43:11 -0300 Subject: [PATCH 2/8] fix linter --- app/schemas/Schemas.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/schemas/Schemas.py b/app/schemas/Schemas.py index 46239d3..31d5f05 100644 --- a/app/schemas/Schemas.py +++ b/app/schemas/Schemas.py @@ -1,6 +1,7 @@ from pydantic import BaseModel from typing import Dict + class UserSchema(BaseModel): id: int name: str From bb86c43e66df3e07a1497033714edb11d7df5bd6 Mon Sep 17 00:00:00 2001 From: feijooso Date: Tue, 2 Apr 2024 09:33:34 -0300 Subject: [PATCH 3/8] add birthdate to user --- app/docker/tablas.sql | 11 ++++++----- app/models/users.py | 3 ++- app/schemas/Schemas.py | 2 ++ app/service/Users.py | 1 - 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/docker/tablas.sql b/app/docker/tablas.sql index 697f94f..a649ed8 100644 --- a/app/docker/tablas.sql +++ b/app/docker/tablas.sql @@ -6,12 +6,13 @@ CREATE TABLE IF NOT EXISTS users_service.users ( email VARCHAR(255) UNIQUE NOT NULL, gender VARCHAR(20), photo VARCHAR(255), + birthdate DATE, location JSONB ); INSERT INTO - users_service.users (name, email, location) -VALUES ('Agus', 'agus@fi.uba.ar', '{"lat": 20, "long": 100}'), - ('Pach', 'pach@fi.uba.ar','{"lat": 10, "long": 200}'), - ('Sofi', 'sofi@fi.uba.ar', '{"lat": 1190, "long": 500}'), - ('Violeta', 'violeta@fi.uba.ar', '{"lat": 330, "long": 2333}'); \ 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/models/users.py b/app/models/users.py index 443a9da..cc63fbf 100644 --- a/app/models/users.py +++ b/app/models/users.py @@ -1,4 +1,4 @@ -from sqlalchemy import Column, Integer, String, JSON +from sqlalchemy import Column, Integer, String, JSON, Date from models.database import Base from os import environ @@ -13,4 +13,5 @@ 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) diff --git a/app/schemas/Schemas.py b/app/schemas/Schemas.py index 31d5f05..852db1b 100644 --- a/app/schemas/Schemas.py +++ b/app/schemas/Schemas.py @@ -1,5 +1,6 @@ from pydantic import BaseModel from typing import Dict +from datetime import date class UserSchema(BaseModel): @@ -8,6 +9,7 @@ class UserSchema(BaseModel): email: str gender: str photo: str + birthdate: date location: Dict diff --git a/app/service/Users.py b/app/service/Users.py index 14d1efe..a6d8164 100644 --- a/app/service/Users.py +++ b/app/service/Users.py @@ -29,7 +29,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: From d601fab37070b28a9b677f22d848106f9475f592 Mon Sep 17 00:00:00 2001 From: feijooso Date: Wed, 3 Apr 2024 00:57:14 -0300 Subject: [PATCH 4/8] location validations --- app/exceptions/UserException.py | 6 +++++ app/repository/Users.py | 7 ++++++ app/schemas/Schemas.py | 5 +++++ app/service/Users.py | 39 ++++++++++++++++++++++++++------- 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/app/exceptions/UserException.py b/app/exceptions/UserException.py index abfdeda..5aa7b1a 100644 --- a/app/exceptions/UserException.py +++ b/app/exceptions/UserException.py @@ -6,3 +6,9 @@ def __init__(self, id: int): status_code = status.HTTP_404_NOT_FOUND detail = f"User with id {id} not found" super().__init__(status_code=status_code, detail=detail) + +class InvalidData(HTTPException): + def __init__(self): + status_code = status.HTTP_400_BAD_REQUEST + detail = f"Invalid user data was provided" + super().__init__(status_code=status_code, detail=detail) \ No newline at end of file diff --git a/app/repository/Users.py b/app/repository/Users.py index e633e95..3a3b686 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,8 @@ def create_user( name: Optional[str] = None, gender: Optional[str] = None, photo: Optional[str] = None, + location: Optional[dict] = None, + birthdate: Optional[date] = None ) -> User: user_data = {"email": email} @@ -57,6 +60,10 @@ 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 new_user = User(**user_data) self.session.add(new_user) diff --git a/app/schemas/Schemas.py b/app/schemas/Schemas.py index 158e696..1c36ca5 100644 --- a/app/schemas/Schemas.py +++ b/app/schemas/Schemas.py @@ -15,6 +15,11 @@ class UserSchema(BaseModel): 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 class LoginRequest(BaseModel): diff --git a/app/service/Users.py b/app/service/Users.py index 3d6e680..604d566 100644 --- a/app/service/Users.py +++ b/app/service/Users.py @@ -1,4 +1,4 @@ -from exceptions.UserException import UserNotFound +from exceptions.UserException import UserNotFound, InvalidData from exceptions.LoginException import AuthenticationError from models.users import User from repository.Users import UsersRepository @@ -21,7 +21,9 @@ def get_all_users(self): 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 login(self, auth_code: str): access_token = self._get_access_token(auth_code) @@ -37,6 +39,13 @@ def login(self, auth_code: str): return user + 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) + self.user_repository.edit_user(user_id, update_data) + def _get_access_token(self, authorization_code): token_url = "https://oauth2.googleapis.com/token" payload = { @@ -70,9 +79,23 @@ 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) - self.user_repository.edit_user(user_id, update_data) + def _validate_location(self, location): + if "lat" in location and "long" in location: + latitud = location["lat"] + longitud = location["long"] + + if (-90<=location["lat"]<= 90 and -180<=location["long"]<= 180): + return True + + return False + +""" if isinstance(latitud, (int, float)) and isinstance(longitud, (int, float)): + if -90 <= latitud <= 90 and -90 <= longitud <= 90: + return True + else: + return False + else: + return False + else: + return False """ + From e50ffb72a6cc43e3eb7811f22b900fe6f71e0292 Mon Sep 17 00:00:00 2001 From: feijooso Date: Wed, 3 Apr 2024 01:01:35 -0300 Subject: [PATCH 5/8] refactor --- app/service/Users.py | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/app/service/Users.py b/app/service/Users.py index 604d566..b219f17 100644 --- a/app/service/Users.py +++ b/app/service/Users.py @@ -81,21 +81,7 @@ def _get_user_info(self, access_token): def _validate_location(self, location): if "lat" in location and "long" in location: - latitud = location["lat"] - longitud = location["long"] + if (-90<=location["lat"]<= 90 and -180<=location["long"]<= 180): return True + return False - if (-90<=location["lat"]<= 90 and -180<=location["long"]<= 180): - return True - - return False - -""" if isinstance(latitud, (int, float)) and isinstance(longitud, (int, float)): - if -90 <= latitud <= 90 and -90 <= longitud <= 90: - return True - else: - return False - else: - return False - else: - return False """ From fb3b1178965c8c5cada02b1b568ac75c3f37d4f3 Mon Sep 17 00:00:00 2001 From: feijooso Date: Wed, 3 Apr 2024 01:30:13 -0300 Subject: [PATCH 6/8] add bio and nickname --- app/docker/tablas.sql | 4 +++- app/exceptions/UserException.py | 7 +++++-- app/models/users.py | 2 ++ app/repository/Users.py | 6 ++++++ app/schemas/Schemas.py | 8 ++++++++ app/service/Users.py | 9 ++++----- 6 files changed, 28 insertions(+), 8 deletions(-) diff --git a/app/docker/tablas.sql b/app/docker/tablas.sql index a649ed8..f5f45eb 100644 --- a/app/docker/tablas.sql +++ b/app/docker/tablas.sql @@ -7,7 +7,9 @@ CREATE TABLE IF NOT EXISTS users_service.users ( gender VARCHAR(20), photo VARCHAR(255), birthdate DATE, - location JSONB + location JSONB, + nickname VARCHAR(30), + biography VARCHAR(255) ); INSERT INTO diff --git a/app/exceptions/UserException.py b/app/exceptions/UserException.py index 5aa7b1a..c644507 100644 --- a/app/exceptions/UserException.py +++ b/app/exceptions/UserException.py @@ -7,8 +7,11 @@ def __init__(self, id: int): detail = f"User with id {id} not found" super().__init__(status_code=status_code, detail=detail) + class InvalidData(HTTPException): def __init__(self): status_code = status.HTTP_400_BAD_REQUEST - detail = f"Invalid user data was provided" - super().__init__(status_code=status_code, detail=detail) \ No newline at end of file + super().__init__( + status_code=status_code, + detail="Invalid user data was provided" + ) diff --git a/app/models/users.py b/app/models/users.py index cc63fbf..ab73efe 100644 --- a/app/models/users.py +++ b/app/models/users.py @@ -15,3 +15,5 @@ class User(Base): 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 3a3b686..54cfdaa 100644 --- a/app/repository/Users.py +++ b/app/repository/Users.py @@ -49,6 +49,8 @@ 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: @@ -64,6 +66,10 @@ def create_user( 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 1c36ca5..ad0718c 100644 --- a/app/schemas/Schemas.py +++ b/app/schemas/Schemas.py @@ -11,6 +11,8 @@ class UserSchema(BaseModel): photo: str birthdate: date location: Dict + nickname: str + biography: str class CreateUserSchema(BaseModel): @@ -20,6 +22,8 @@ class CreateUserSchema(BaseModel): photo: Optional[str] = None birthdate: Optional[date] = None location: Optional[Dict] = None + nickname: Optional[str] = None + biography: Optional[str] = None class LoginRequest(BaseModel): @@ -30,3 +34,7 @@ class UpdateUserSchema(BaseModel): name: Optional[str] = None gender: Optional[str] = None photo: Optional[HttpUrl] = 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 b219f17..a9f7cf6 100644 --- a/app/service/Users.py +++ b/app/service/Users.py @@ -20,8 +20,7 @@ def get_all_users(self): return self.user_repository.get_all_users() def create_user(self, user_data: dict): - email = user_data.get("email") - if (not self._validate_location(user_data.get("location"))): + if not self._validate_location(user_data.get("location")): raise InvalidData() return self.user_repository.create_user(**user_data) @@ -81,7 +80,7 @@ def _get_user_info(self, access_token): 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 + if -90 <= location["lat"] <= 90 and \ + -180 <= location["long"] <= 180: + return True return False - - From b37b6ee4ddbc1db01e7edfdb6c14bb245833a7fc Mon Sep 17 00:00:00 2001 From: feijooso Date: Thu, 4 Apr 2024 01:19:14 -0300 Subject: [PATCH 7/8] fix url regex --- app/schemas/Schemas.py | 2 +- app/service/Users.py | 29 ++++++++++++++--------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/app/schemas/Schemas.py b/app/schemas/Schemas.py index ad0718c..f9eac47 100644 --- a/app/schemas/Schemas.py +++ b/app/schemas/Schemas.py @@ -33,7 +33,7 @@ class LoginRequest(BaseModel): class UpdateUserSchema(BaseModel): name: Optional[str] = None gender: Optional[str] = None - photo: Optional[HttpUrl] = None + photo: Optional[str] = None birthdate: Optional[date] = None location: Optional[Dict] = None nickname: Optional[str] = None diff --git a/app/service/Users.py b/app/service/Users.py index e039a11..514799e 100644 --- a/app/service/Users.py +++ b/app/service/Users.py @@ -25,6 +25,20 @@ def create_user(self, user_data: dict): 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) if access_token is None: @@ -72,21 +86,6 @@ 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 \ From 6e84896ecfe3b0cf618a6d4b6d6fe262b04d71dd Mon Sep 17 00:00:00 2001 From: feijooso Date: Thu, 4 Apr 2024 01:22:07 -0300 Subject: [PATCH 8/8] fix linter --- app/schemas/Schemas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/schemas/Schemas.py b/app/schemas/Schemas.py index f9eac47..d4d0b58 100644 --- a/app/schemas/Schemas.py +++ b/app/schemas/Schemas.py @@ -1,5 +1,5 @@ from datetime import date -from pydantic import BaseModel, HttpUrl +from pydantic import BaseModel from typing import Optional, Dict