Upload 4 files
Browse files- users/models.py +32 -0
- users/routes.py +125 -0
- users/schemas.py +40 -0
- users/services.py +84 -0
users/models.py
CHANGED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Float, ARRAY, DateTime
|
2 |
+
from sqlalchemy.orm import relationship
|
3 |
+
from core.database import Base
|
4 |
+
from datetime import datetime
|
5 |
+
|
6 |
+
class User(Base):
|
7 |
+
__tablename__ = "users"
|
8 |
+
|
9 |
+
id = Column(Integer, primary_key=True, index=True)
|
10 |
+
email = Column(String, unique=True, index=True)
|
11 |
+
username = Column(String, unique=True, index=True)
|
12 |
+
hashed_password = Column(String)
|
13 |
+
first_name = Column(String)
|
14 |
+
last_name = Column(String)
|
15 |
+
age = Column(Integer)
|
16 |
+
preferences = Column(ARRAY(String))
|
17 |
+
is_active = Column(Boolean, default=True)
|
18 |
+
is_admin = Column(Boolean, default=False)
|
19 |
+
updated_at = Column(DateTime, default=datetime.utcnow)
|
20 |
+
created_at = Column(DateTime, default=datetime.utcnow)
|
21 |
+
|
22 |
+
orders = relationship("Order", back_populates="user")
|
23 |
+
embeddings = relationship("UserEmbeddings", back_populates="user", uselist=False)
|
24 |
+
|
25 |
+
class UserEmbeddings(Base):
|
26 |
+
__tablename__ = "user_embeddings"
|
27 |
+
|
28 |
+
id = Column(Integer, primary_key=True, index=True)
|
29 |
+
user_id = Column(Integer, ForeignKey("users.id"))
|
30 |
+
embeddings = Column(ARRAY(Float))
|
31 |
+
|
32 |
+
user = relationship("User", back_populates="embeddings")
|
users/routes.py
ADDED
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter, status, Depends, Request, UploadFile, File, HTTPException
|
2 |
+
from fastapi.responses import JSONResponse
|
3 |
+
from sqlalchemy.orm import Session
|
4 |
+
from core.database import get_db
|
5 |
+
from core.security import get_current_user, create_access_token
|
6 |
+
from users.schemas import UserCreate, UserBase, UserEmbeddingsBase, User
|
7 |
+
from users.services import create_user_account, create_user_embeddings, update_user, update_user_embeddings, get_user_by_id, get_user_by_email
|
8 |
+
from services.facial_processing import FacialProcessing
|
9 |
+
from services.face_match import FaceMatch
|
10 |
+
import os
|
11 |
+
from datetime import timedelta
|
12 |
+
from dotenv import load_dotenv
|
13 |
+
from auth.services import get_token
|
14 |
+
|
15 |
+
|
16 |
+
env_path = (os.path.abspath(os.path.join(os.path.dirname(__file__), '.env')))
|
17 |
+
load_dotenv()
|
18 |
+
|
19 |
+
router = APIRouter(
|
20 |
+
prefix="/users",
|
21 |
+
tags=["Users"],
|
22 |
+
responses={404: {"description": "Not found"}},
|
23 |
+
)
|
24 |
+
|
25 |
+
@router.get("/health", tags=["Health"])
|
26 |
+
def health_check():
|
27 |
+
return {"status": "ok"}
|
28 |
+
|
29 |
+
@router.post("/", status_code=status.HTTP_201_CREATED, response_model=UserBase)
|
30 |
+
async def create_user(data: UserCreate, db: Session = Depends(get_db)):
|
31 |
+
await create_user_account(data, db)
|
32 |
+
payload = {"message": "User created successfully"}
|
33 |
+
return JSONResponse(content=payload, status_code=status.HTTP_201_CREATED)
|
34 |
+
|
35 |
+
|
36 |
+
@router.post("/me",status_code=status.HTTP_200_OK)
|
37 |
+
def get_user_details(request: Request):
|
38 |
+
user = request.user
|
39 |
+
return user
|
40 |
+
|
41 |
+
@router.get("/me", response_model=UserBase)
|
42 |
+
async def read_users_me(current_user: User = Depends(get_current_user)):
|
43 |
+
return current_user
|
44 |
+
|
45 |
+
@router.put("/me", response_model=UserBase)
|
46 |
+
async def updating_user(user: UserBase, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
47 |
+
user = update_user(db, current_user.id, user)
|
48 |
+
return current_user
|
49 |
+
|
50 |
+
|
51 |
+
@router.post("/me/face",status_code=status.HTTP_200_OK)
|
52 |
+
async def create_face_embeddings(file: UploadFile = File(...), user:User = Depends(get_current_user), db: Session = Depends(get_db)):
|
53 |
+
face_processor = FacialProcessing()
|
54 |
+
|
55 |
+
# Process the uploaded image
|
56 |
+
image_path = f"faces/{user.id}.jpg"
|
57 |
+
with open(image_path, "wb") as buffer:
|
58 |
+
buffer.write(await file.read())
|
59 |
+
|
60 |
+
# Extract embeddings
|
61 |
+
embeddings = face_processor.extract_embeddings_vgg(image_path)
|
62 |
+
if embeddings:
|
63 |
+
create_user_embeddings(user.id, embeddings, db)
|
64 |
+
return {"message": "Face embeddings created successfully"}
|
65 |
+
|
66 |
+
raise HTTPException(status_code=400,
|
67 |
+
detail="Failed to process face"
|
68 |
+
)
|
69 |
+
|
70 |
+
@router.get("/me/face",status_code=status.HTTP_200_OK)
|
71 |
+
async def get_face_embeddings(user:User = Depends(get_current_user), db: Session = Depends(get_db)):
|
72 |
+
face = db.query(UserEmbeddingsBase).filter(UserEmbeddingsBase.user_id == user.id).first()
|
73 |
+
if not face:
|
74 |
+
raise HTTPException(status_code=404,
|
75 |
+
detail="Face embeddings not found"
|
76 |
+
)
|
77 |
+
return JSONResponse(content={"embeddings": face.embeddings}, status_code=status.HTTP_200_OK)
|
78 |
+
|
79 |
+
@router.put("/me/face",status_code=status.HTTP_200_OK)
|
80 |
+
async def updating_face_embeddings(file: UploadFile = File(...), user:User = Depends(get_current_user), db: Session = Depends(get_db)):
|
81 |
+
face_processor = FacialProcessing()
|
82 |
+
|
83 |
+
# Process the uploaded image
|
84 |
+
image_path = f"faces/{user.id}.jpg"
|
85 |
+
with open(image_path, "wb") as buffer:
|
86 |
+
buffer.write(await file.read())
|
87 |
+
|
88 |
+
# Extract embeddings
|
89 |
+
embeddings = face_processor.extract_embeddings_vgg(image_path)
|
90 |
+
if embeddings:
|
91 |
+
update_user_embeddings(user.id, embeddings, db)
|
92 |
+
return {"message": "Face embeddings updated successfully"}
|
93 |
+
|
94 |
+
raise HTTPException(status_code=400,
|
95 |
+
detail="Failed to process face"
|
96 |
+
)
|
97 |
+
|
98 |
+
@router.post("/login/face")
|
99 |
+
async def face_login(file: UploadFile = File(...), db: Session = Depends(get_db)):
|
100 |
+
face_processor = FacialProcessing()
|
101 |
+
face_matcher = FaceMatch(db)
|
102 |
+
|
103 |
+
# Process the uploaded image
|
104 |
+
image_path = f"temp_{file.filename}"
|
105 |
+
with open(image_path, "wb") as buffer:
|
106 |
+
buffer.write(await file.read())
|
107 |
+
|
108 |
+
# Extract embeddings
|
109 |
+
embeddings = await face_processor.extract_embeddings(image_path)
|
110 |
+
if not embeddings:
|
111 |
+
raise HTTPException(status_code=400, detail="Failed to process face")
|
112 |
+
|
113 |
+
# Match face
|
114 |
+
match_result = face_matcher.new_face_matching(embeddings)
|
115 |
+
if match_result['status'] == 'Success':
|
116 |
+
user = get_user_by_id(match_result['user_id'], db)
|
117 |
+
if not user:
|
118 |
+
raise HTTPException(status_code=404, detail="User not found")
|
119 |
+
|
120 |
+
access_token_expires = timedelta(minutes=os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES"))
|
121 |
+
payload = {"id":user.id, "sub": user.email}
|
122 |
+
token = get_token(payload, db)
|
123 |
+
return JSONResponse(content=token.dict(), status_code=status.HTTP_200_OK)
|
124 |
+
|
125 |
+
raise HTTPException(status_code=401, detail="Face not recognized")
|
users/schemas.py
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pydantic import BaseModel, EmailStr
|
2 |
+
from typing import Optional, List
|
3 |
+
|
4 |
+
class UserBase(BaseModel):
|
5 |
+
username: str
|
6 |
+
first_name: str
|
7 |
+
last_name: str
|
8 |
+
email: EmailStr
|
9 |
+
age: Optional[int] = None
|
10 |
+
preferences: Optional[dict] = None
|
11 |
+
is_active: Optional[bool] = True
|
12 |
+
|
13 |
+
|
14 |
+
class UserCreate(UserBase):
|
15 |
+
password: str
|
16 |
+
|
17 |
+
|
18 |
+
class UserUpdate(UserBase):
|
19 |
+
password: Optional[str] = None
|
20 |
+
|
21 |
+
class User(UserBase):
|
22 |
+
id: int
|
23 |
+
|
24 |
+
class Config:
|
25 |
+
orm_mode = True
|
26 |
+
|
27 |
+
class UserEmbeddingsBase(BaseModel):
|
28 |
+
embeddings: List[float]
|
29 |
+
|
30 |
+
class UserEmbeddingsCreate(UserEmbeddingsBase):
|
31 |
+
pass
|
32 |
+
|
33 |
+
|
34 |
+
class UserEmbeddings(UserEmbeddingsBase):
|
35 |
+
id: int
|
36 |
+
user_id: int
|
37 |
+
|
38 |
+
class Config:
|
39 |
+
orm_mode = True
|
40 |
+
|
users/services.py
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from users.models import User, UserEmbeddings
|
2 |
+
from fastapi.exceptions import HTTPException
|
3 |
+
from core.security import get_password_hash
|
4 |
+
from datetime import datetime
|
5 |
+
from sqlalchemy.orm import Session
|
6 |
+
from users.schemas import UserCreate, UserUpdate, UserEmbeddingsModel
|
7 |
+
|
8 |
+
|
9 |
+
async def create_user_account(data:UserCreate, db:Session):
|
10 |
+
user = db.query(User).filter(User.email == data.email).first()
|
11 |
+
if user:
|
12 |
+
raise HTTPException(status_code=422, detail="Email already registered")
|
13 |
+
|
14 |
+
new_user = User(
|
15 |
+
email=data.email,
|
16 |
+
first_name=data.first_name,
|
17 |
+
last_name=data.last_name,
|
18 |
+
age=data.age,
|
19 |
+
preferences=data.preferences,
|
20 |
+
password = get_password_hash(data.password),
|
21 |
+
registered_at=datetime.now(),
|
22 |
+
updated_at=datetime.now(),
|
23 |
+
)
|
24 |
+
|
25 |
+
db.add(new_user)
|
26 |
+
db.commit()
|
27 |
+
db.refresh(new_user)
|
28 |
+
return new_user
|
29 |
+
|
30 |
+
async def create_user_embeddings(user_id:int, embeddings:UserEmbeddingsModel, db:Session):
|
31 |
+
user = db.query(User).filter(User.id == user_id).first()
|
32 |
+
if not user:
|
33 |
+
embeddings = UserEmbeddings(user_id=user_id, embeddings=embeddings.embeddings)
|
34 |
+
db.add(embeddings)
|
35 |
+
db.commit()
|
36 |
+
db.refresh(embeddings)
|
37 |
+
return embeddings
|
38 |
+
|
39 |
+
def get_user_by_id(user_id, db:Session):
|
40 |
+
return db.query(User).filter(User.id == user_id).first()
|
41 |
+
|
42 |
+
|
43 |
+
def get_user_by_email(email, db:Session):
|
44 |
+
return db.query(User).filter(User.email == email).first()
|
45 |
+
|
46 |
+
|
47 |
+
def get_users(db:Session, skip=0, limit=100):
|
48 |
+
return db.query(User).offset(skip).limit(limit).all()
|
49 |
+
|
50 |
+
|
51 |
+
def update_user(db:Session, user_id:int, user:UserUpdate):
|
52 |
+
user = db.query(User).filter(User.id == user_id).first()
|
53 |
+
if user:
|
54 |
+
update_data = user.dict(exclude_unset=True)
|
55 |
+
if 'password' in update_data:
|
56 |
+
update_data['password'] = get_password_hash(update_data['password'])
|
57 |
+
del update_data['password']
|
58 |
+
|
59 |
+
for key, value in update_data.items():
|
60 |
+
setattr(user, key, value)
|
61 |
+
db.commit()
|
62 |
+
db.refresh(user)
|
63 |
+
return user
|
64 |
+
|
65 |
+
|
66 |
+
def delete_user(user_id, db:Session):
|
67 |
+
user = db.query(User).filter(User.id == user_id).first()
|
68 |
+
if user:
|
69 |
+
db.delete(user)
|
70 |
+
db.commit()
|
71 |
+
return user
|
72 |
+
return None
|
73 |
+
|
74 |
+
|
75 |
+
async def update_user_embeddings(user_id:int, embeddings:UserEmbeddingsModel, db:Session):
|
76 |
+
embeddings = db.query(UserEmbeddings).filter(UserEmbeddings.user_id == user_id).first()
|
77 |
+
if embeddings:
|
78 |
+
embeddings.embeddings = embeddings.embeddings
|
79 |
+
db.commit()
|
80 |
+
db.refresh(embeddings)
|
81 |
+
return embeddings
|
82 |
+
return None
|
83 |
+
|
84 |
+
|