From 636494df3c77f1fe45215abfe275585dbc6dabbb Mon Sep 17 00:00:00 2001 From: sai shivva Date: Sun, 2 Mar 2025 15:39:43 +0530 Subject: [PATCH] Feature Completion for Dating App --- database.py | 7 ++- main.py | 143 +++++++++++++++++++++++++++++++++++++++++++++++----- models.py | 10 +++- schemas.py | 24 +++++++++ test.db | Bin 0 -> 24576 bytes 5 files changed, 167 insertions(+), 17 deletions(-) create mode 100644 test.db diff --git a/database.py b/database.py index accf2f5..f4e7379 100644 --- a/database.py +++ b/database.py @@ -2,10 +2,15 @@ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker -// Free to use remote db or create a local database. Modify the URl appropriately +# // Free to use remote db or create a local database. Modify the URl appropriately SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db" +# Is responsible for FastApi to interact with database engine = create_engine(SQLALCHEMY_DATABASE_URL) + +# It is responsible for interacting with db SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +# It can be commonly used by all classes Base = declarative_base() diff --git a/main.py b/main.py index f83e868..5ccbe72 100644 --- a/main.py +++ b/main.py @@ -1,12 +1,23 @@ +from typing import List from fastapi import FastAPI, HTTPException, Depends +from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import Session from database import SessionLocal, engine, Base import models, schemas +import logging + +logging.basicConfig( + level=logging.INFO, # Adjust level as needed (DEBUG, INFO, etc.) + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" +) +logger = logging.getLogger(__name__) + app = FastAPI() Base.metadata.create_all(bind=engine) + def get_db(): db = SessionLocal() try: @@ -14,23 +25,127 @@ def get_db(): finally: db.close() -@app.post("/users/", response_model=schemas.User) + +@app.post("/create-user", response_model=schemas.User) def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)): - db_user = models.User(**user.dict()) - db.add(db_user) - db.commit() - db.refresh(db_user) - return db_user + try: + logger.info("Creating new user with email: %s", user.email) + db_user = models.User( + name=user.name, + age=user.age, + gender=user.gender, + email=user.email, + city=user.city, + interests=user.interests + ) + db.add(db_user) + db.commit() + db.refresh(db_user) + logger.info("User created successfully with id: %s", db_user.id) + return db_user + except IntegrityError as he: + db.rollback() + logger.warning("IntegrityError: %s", str(he)) + raise HTTPException(status_code=409, detail="User with this email already exists") + except Exception as e: + logger.error("Error occurred while creating user: %s", str(e)) + db.rollback() + raise HTTPException(status_code=500, detail="Internal Server Error while creating user ") + + +@app.get("/find-all-users", response_model=List[schemas.User]) +def read_users(db: Session = Depends(get_db)): + try: + logger.info("Fetching all users") + users = db.query(models.User).all() + logger.info("Fetched %d users", len(users)) + return users + except Exception as e: + logger.error("Error occurred while fetching users: %s", str(e)) + raise HTTPException(status_code=500, detail="Internal Server Error while fetching users") -@app.get("/users/", response_model=list[schemas.User]) -def read_users(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)): - users = db.query(models.User).offset(skip).limit(limit).all() - return users @app.get("/users/{user_id}", response_model=schemas.User) def read_user(user_id: int, db: Session = Depends(get_db)): - user = db.query(models.User).filter(models.User.id == user_id).first() - if user is None: - raise HTTPException(status_code=404, detail="User not found") - return user + try: + logger.info("Fetching user with id: %s", user_id) + user = db.query(models.User).filter(models.User.id == user_id).first() + if user is None: + logger.warning("User with id %s not found", user_id) + raise HTTPException(status_code=404, detail="User not found") + logger.info("User with id %s fetched successfully", user_id) + return user + except Exception as e: + logger.error("Error occurred while fetching user with id %s: %s", user_id, str(e)) + raise HTTPException(status_code=500, detail="Internal Server Error while fetching user "+str(e)) + +# ssss +@app.get("/match", response_model=List[schemas.CandidateSuggestion]) +def calculate_match(user_identifier: schemas.UserIdentifier, db: Session = Depends(get_db)): + try: + logger.info("Calculating match for user: %s", user_identifier.email) + # Retrieve the full user profile from the database using name and email + user = db.query(models.User).filter( + models.User.email == user_identifier.email, + models.User.name == user_identifier.name + ).first() + if not user: + logger.warning("User not found with email: %s and name: %s", user_identifier.email, user_identifier.name) + raise HTTPException(status_code=404, detail="User not found") + + # Get candidates of the opposite gender + candidates = db.query(models.User).filter(models.User.gender != user.gender).all() + if not candidates: + logger.warning("No matches found for user with email: %s", user_identifier.email) + raise HTTPException(status_code=404, detail="No matches found Dont worry you are unique in your own way") + + suggestions = [] + user_interests = set(user.interests) + + for candidate in candidates: + candidate_interests = set(candidate.interests) + # Calculate interests similarity using Jaccard similarity + common_interests = user_interests.intersection(candidate_interests) + union_interests = user_interests.union(candidate_interests) + interest_score = len(common_interests) / len(union_interests) if union_interests else 0 + + # Calculate age compatibility: score decreases linearly with age difference (threshold 10 years) + age_diff = abs(candidate.age - user.age) + max_age_diff = 5 + age_score = max(0, 1 - (age_diff / max_age_diff)) + + # City match: bonus if same city (score 1 if same, 0 otherwise) + city_score = 1 if candidate.city.lower() == user.city.lower() else 0 + + # Weight factors for composite match_percentage + weight_interest = 0.5 + weight_age = 0.3 + weight_city = 0.2 + + total_score = (interest_score * weight_interest) + (age_score * weight_age) + (city_score * weight_city) + match_percentage = round(total_score * 100, 2) # Convert to percentage + + candidate_suggestion = schemas.CandidateSuggestion( + id=candidate.id, + name=candidate.name, + email=candidate.email, + age=candidate.age, + gender=candidate.gender, + city=candidate.city, + interests=candidate.interests, + match_percentage=match_percentage + ) + suggestions.append(candidate_suggestion) + + # Sort suggestions by match_percentage in descending order and return the top 3 + suggestions.sort(key=lambda x: x.match_percentage, reverse=True) + top_suggestions = suggestions[:3] + logger.info("Match calculation successful for user %s. Found %d suggestions.", user_identifier.email, + len(top_suggestions)) + return top_suggestions + except HTTPException as he: + raise he + except Exception as e: + logger.error("Error occurred during match calculation: %s", str(e)) + raise HTTPException(status_code=500, detail="Internal Server Error during match calculation") diff --git a/models.py b/models.py index 0e42748..8c8b3c2 100644 --- a/models.py +++ b/models.py @@ -1,5 +1,7 @@ from sqlalchemy import Column, Integer, String, ARRAY -from database import Base +from database import Base, engine +from sqlalchemy.types import JSON + class User(Base): __tablename__ = "users" @@ -10,5 +12,9 @@ class User(Base): gender = Column(String) email = Column(String, unique=True, index=True) city = Column(String, index=True) - interests = Column(ARRAY(String)) + interests = Column(JSON) + +# used to create tables in database + +# Base.metadata.createAll(bird=engine) diff --git a/schemas.py b/schemas.py index 5872710..5baca5e 100644 --- a/schemas.py +++ b/schemas.py @@ -1,6 +1,7 @@ from pydantic import BaseModel from typing import List + class UserBase(BaseModel): name: str age: int @@ -18,3 +19,26 @@ class User(UserBase): class Config: orm_mode = True +# New schema: used to identify a user by name and email +class UserIdentifier(BaseModel): + name: str + email: str + + + +# Define the CandidateSuggestion schema for match suggestions +class CandidateSuggestion(BaseModel): + id: int + name: str + email: str + age: int + gender: str + city: str + interests: List[str] + match_percentage: float # Compatibility rating as a percentage + + class Config: + orm_mode = True + + + diff --git a/test.db b/test.db new file mode 100644 index 0000000000000000000000000000000000000000..00fea7d9bae6007a16c39ecdbe26ad11a01d4b82 GIT binary patch literal 24576 zcmeI(K~EDw7zW^(&Tefhc8m&ZLrf<%Q5(<;C%s@P)fTa6DMr!|ve30oEL+kR0!O|2 z4>)o+-aH%69yRggMNQO`iHV7c2WMtmxa zKmY;|fB*y_009U<00PHDV9Jg<7ah7N{kpd$x0W|LUc0j_>*hV0uM~3Cf+&@z3lBx; zu9zzeGeTUE^=!q8I$4Ky_Gfr&u56k?b}we3T)MyTPnsDQ3N-y}kwu-$4t4iu`tF)% zBCUgGrtgQMbT?a!9F(*wyW1v-9h{`^hmv$R`}77q;W!R`Hs5tun!!X&Mx>Wf%}vb~ z`Y?&OtSh-{;dY@S%5zmwUYMO77m2u;-)(sPak|^^)JM@4p#gzV=z^JC zs4xAl*Y-Nyj+mLBE9;PZmD1f@Wl`KIESirfkqv6|UBX}SHouhol-xZYUqH+P0uX=z z1Rwwb2tWV=5P$##{x^Y1J4S|SD(g(<*4HFMUphF(gzw%S@N`E}{mGDX*K zNWURk8{N#f$lPkR>e9ZVm$Sk3HXa<)INXEJhrf8oO|3y{&%m ze2`(zl2%SIbm9x2`6lCy`fk2wMH=OhsZOMTf7ZuXnoMrst>h^90R7R&iSu z`|gTcuVtp&(tol)ZK+6OBu$4WREAm#kxw4W=6Ms-?;V!3*$e8=a7q7>-{x-!|G?k! zpZtqz;(!1IAOHafKmY;|fB*y_009U<;MfVonM2J2h<%cc(%wpjHN-B^{(=NM#nNp}hh zneadS7yrS(^Up{8Scr!J1Rwwb2tWV=5P$##AOHafK;TFOY(}YV>$7F)Gh_N3i7*zA F{{>mHXb%7Y literal 0 HcmV?d00001