khawir commited on
Commit
8ed8485
1 Parent(s): ec8d1e2

initial commit

Browse files
.env ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ SECRET_KEY=09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7
2
+ ALGORITHM=HS256
3
+ ACCESS_TOKEN_EXPIRE_MINUTES=30
4
+
5
+ # DB_SERVER=localhost
6
+ # DB_NAME=tian
7
+ # DB_PORT=5432
8
+ # DB_USER=postgres
9
+ # DB_PASSWORD=serG
10
+
11
+ DB_SQLITE=test.db
12
+
13
+ SU_NAME=khawir
14
+ SU_PASSWORD=1qaz2wsx
15
+
16
+ EZV_KEY=fc2c7d28d761409ca6c2a1340490cd12
17
+ EZV_SECRET=ed8d3fbe7316413b90fdf00df9c13c01
__init.py ADDED
File without changes
__pycache__/config.cpython-311.pyc ADDED
Binary file (1.88 kB). View file
 
__pycache__/crud.cpython-311.pyc ADDED
Binary file (4.99 kB). View file
 
__pycache__/main.cpython-311.pyc ADDED
Binary file (6.67 kB). View file
 
__pycache__/models.cpython-311.pyc ADDED
Binary file (14.8 kB). View file
 
__pycache__/utils.cpython-311.pyc ADDED
Binary file (2.7 kB). View file
 
config.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+
4
+ from pathlib import Path
5
+ env_path = Path('.env')
6
+ load_dotenv(dotenv_path=env_path)
7
+
8
+ class Settings:
9
+ PROJECT_NAME:str = "Pulsse"
10
+ PROJECT_VERSION: str = "1.0.0"
11
+ DB_SERVER = os.getenv("DB_SERVER", "localhost")
12
+ DB_NAME = os.getenv("DB_NAME")
13
+ DB_PORT = os.getenv("DB_PORT", 5432)
14
+ DB_USER = os.getenv("DB_USER")
15
+ DB_PASSWORD = os.getenv("DB_PASSWORD")
16
+ DB_SQLITE = os.getenv("DB_SQLITE")
17
+ DB_URL = f"postgresql://{DB_USER}:{DB_PASSWORD}@{DB_SERVER}:{DB_PORT}/{DB_NAME}"
18
+ DB_SQLITE = f"sqlite:///{DB_SQLITE}"
19
+ SECRET_KEY = os.getenv("SECRET_KEY")
20
+ ALGORITHM = os.getenv("ALGORITHM")
21
+ ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES"))
22
+ SU_NAME = os.getenv("SU_NAME")
23
+ SU_PASSWORD = os.getenv("SU_PASSWORD")
24
+ EZV_KEY = os.getenv("EZV_KEY")
25
+ EZV_SECRET = os.getenv("EZV_SECRET")
26
+
27
+ # INSERT INTO user (username, full_name, email, password, disabled) VALUES ('johndoe', 'John Doe', 'johndoe@example.com', '$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW', False)
28
+
29
+ settings = Settings()
core/__init__.py ADDED
File without changes
core/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (167 Bytes). View file
 
core/__pycache__/crud.cpython-311.pyc ADDED
Binary file (11.4 kB). View file
 
core/__pycache__/utils.cpython-311.pyc ADDED
Binary file (2.76 kB). View file
 
core/crud.py ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+ from typing import Annotated
3
+ from sqlmodel import Session, select
4
+ from fastapi import Depends, HTTPException, status
5
+ from fastapi.security import OAuth2PasswordBearer
6
+ from jose import JWTError, jwt
7
+ import core.utils as utils
8
+ from models import User, UserCreate, Site, SiteCreate, Guest
9
+ from models import TokenData
10
+ from config import settings
11
+
12
+
13
+ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")
14
+
15
+
16
+ def authenticate_user(username: str, password: str):
17
+ with Session(utils.engine) as session:
18
+ statement = select(User).where(User.username == username)
19
+ user = session.exec(statement).first()
20
+ if not user:
21
+ return False
22
+ if not utils.verify_password(password, user.password):
23
+ return False
24
+ return user
25
+
26
+
27
+
28
+ def get_user(username: str):
29
+ with Session(utils.engine) as session:
30
+ statement = select(User).where(User.username == username)
31
+ user = session.exec(statement).first()
32
+ if not user:
33
+ raise HTTPException(status_code=404, detail="User not found")
34
+ return user
35
+
36
+
37
+
38
+ def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
39
+ credentials_exception = HTTPException(
40
+ status_code=status.HTTP_401_UNAUTHORIZED,
41
+ detail="Could not validate credentials",
42
+ headers={"WWW-Authenticate": "Bearer"},
43
+ )
44
+ try:
45
+ payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
46
+ username: str = payload.get("sub")
47
+ if username is None:
48
+ raise credentials_exception
49
+ token_data = TokenData(username=username)
50
+ except JWTError:
51
+ raise credentials_exception
52
+ user = get_user(username=token_data.username)
53
+ if user is None:
54
+ raise credentials_exception
55
+ return user
56
+
57
+
58
+
59
+ def get_current_active_user(
60
+ current_user: Annotated[User, Depends(get_current_user)],
61
+ ):
62
+ if current_user.disabled:
63
+ raise HTTPException(status_code=400, detail="Inactive user")
64
+ return current_user
65
+
66
+
67
+
68
+ def get_current_super_user(
69
+ current_user: Annotated[User, Depends(get_current_user)],
70
+ ):
71
+ if not current_user.is_su:
72
+ raise HTTPException(status_code=403, detail="Action only allowed for admin")
73
+ return current_user
74
+
75
+
76
+
77
+ def add_user(session: Session, user: UserCreate):
78
+ statement = select(User).where(User.username == user.email)
79
+ db_user = session.exec(statement).first()
80
+ if not db_user:
81
+ hashed_password = utils.get_password_hash(user.password)
82
+ extra_data = {"password": hashed_password,
83
+ "username": user.email,
84
+ "created_at": datetime.now(),
85
+ "updated_at": datetime.now()}
86
+ db_user = User.model_validate(user, update=extra_data)
87
+ session.add(db_user)
88
+ session.commit()
89
+ session.refresh(db_user)
90
+ return db_user
91
+ raise HTTPException(status_code=400, detail="Email already registered")
92
+
93
+
94
+
95
+ def edit_user(session: Session, db_user: User, user):
96
+ user_data = user.model_dump(exclude_unset=True)
97
+ if "password" in user_data:
98
+ hashed_password = utils.get_password_hash(user_data["password"])
99
+ user_data["password"] = hashed_password
100
+ extra_data = {"updated_at": datetime.now()}
101
+ db_user.sqlmodel_update(user_data, update=extra_data)
102
+ try:
103
+ session.add(db_user)
104
+ session.commit()
105
+ except Exception as e:
106
+ raise HTTPException(status_code=400, detail="Update failed -> Hint: check for unique username")
107
+ else:
108
+ session.refresh(db_user)
109
+ return db_user
110
+
111
+
112
+
113
+ # --------------------
114
+ # ------ Sites -------
115
+ # --------------------
116
+
117
+
118
+
119
+ def camera_exists(session: Session, site):
120
+ exists = None
121
+ cameras = session.exec(select(Site.in_camera, Site.out_camera)).all()
122
+ camera_list = [item for inner_tuple in cameras for item in inner_tuple if item is not None]
123
+ if site.in_camera is not None:
124
+ exists = "in_camera" if site.in_camera in camera_list else None
125
+ if site.out_camera is not None:
126
+ exists = "out_camera" if site.out_camera in camera_list else None
127
+ if exists is not None:
128
+ raise HTTPException(status_code=400, detail=f"Camera (Device ID) already exists in {exists}s")
129
+
130
+
131
+
132
+ def push_site(session: Session, site: SiteCreate):
133
+ try:
134
+ session.add(site)
135
+ session.commit()
136
+ except Exception as e:
137
+ raise HTTPException(status_code=400, detail="Action failed -> Hint: Check for unique site name")
138
+ else:
139
+ session.refresh(site)
140
+ return site
141
+
142
+
143
+
144
+ def get_current_site(session: Session, current_user: User, site_id: int):
145
+ session.add(current_user)
146
+ user_site_ids = [site.id for site in current_user.sites]
147
+ if site_id not in user_site_ids:
148
+ raise HTTPException(status_code=403, detail="Access only allowed for own sites")
149
+ site = session.get(Site, site_id)
150
+ if not site:
151
+ raise HTTPException(status_code=404, detail="Site not found")
152
+ return site
153
+
154
+
155
+
156
+
157
+ # --------------------
158
+ # ------ Hosts -------
159
+ # --------------------
160
+
161
+ def vector_exists(session: Session, guest):
162
+ statement = select(Guest).where(Guest.vector == guest.vector)
163
+ db_guest = session.exec(statement).first()
164
+ if db_guest:
165
+ raise HTTPException(status_code=400, detail=f"Guest/Host vector already exists at id {db_guest.id}")
166
+
167
+
168
+
169
+ def get_host_of_site(session: Session, current_site: Site, host_id:int):
170
+ site_host_ids = [host.id for host in current_site.hosts]
171
+ if host_id not in site_host_ids:
172
+ raise HTTPException(status_code=403, detail="Access only allowed for own hosts")
173
+ host = session.get(Guest, host_id)
174
+ if not host:
175
+ raise HTTPException(status_code=404, detail="Host not found")
176
+ return host
177
+
178
+
179
+
180
+
181
+
182
+ # --------------------
183
+ # ------ Admin -------
184
+ # --------------------
185
+
186
+
187
+
188
+ def create_su():
189
+ with Session(utils.engine) as session:
190
+ statement = select(User).where(User.username == settings.SU_NAME)
191
+ su = session.exec(statement).first()
192
+ if not su:
193
+ su = User(
194
+ username = settings.SU_NAME,
195
+ password = utils.get_password_hash(settings.SU_PASSWORD),
196
+ is_su=True,
197
+ disabled=False
198
+ )
199
+ session.add(su)
200
+ session.commit()
core/utils.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime, timedelta, timezone
2
+ from sqlmodel import Session, SQLModel, create_engine
3
+ from passlib.context import CryptContext
4
+ from jose import jwt
5
+ from config import settings
6
+
7
+
8
+ db_url = settings.DB_SQLITE
9
+
10
+
11
+ engine = create_engine(db_url, echo=True,
12
+ connect_args={"check_same_thread": False}
13
+ )
14
+
15
+
16
+ pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
17
+
18
+
19
+ def create_db_and_tables():
20
+ SQLModel.metadata.create_all(engine)
21
+
22
+
23
+ def get_session():
24
+ with Session(engine) as session:
25
+ yield session
26
+
27
+
28
+ def verify_password(plain_password, hashed_password):
29
+ return pwd_context.verify(plain_password, hashed_password)
30
+
31
+
32
+ def get_password_hash(password):
33
+ return pwd_context.hash(password)
34
+
35
+
36
+ def create_access_token(data: dict, expires_delta: timedelta | None = None):
37
+ to_encode = data.copy()
38
+ if expires_delta:
39
+ expire = datetime.now(timezone.utc) + expires_delta
40
+ else:
41
+ expire = datetime.now(timezone.utc) + timedelta(minutes=15)
42
+ to_encode.update({"exp": expire})
43
+ print(to_encode)
44
+ encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
45
+ return encoded_jwt
main.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime, timedelta
2
+ from typing import Annotated
3
+ from fastapi import Depends, FastAPI, HTTPException, status
4
+ from fastapi.security import OAuth2PasswordRequestForm
5
+ from sqlmodel import Session, select
6
+ from config import settings
7
+
8
+ from models import Token, Site, Guest, Visit, User, UserPublicMe, UserCreate
9
+ from routers import users, sites, visits, guests, profile
10
+ from core import utils, crud
11
+
12
+ app = FastAPI()
13
+
14
+ app.include_router(profile.router)
15
+ app.include_router(users.router)
16
+ app.include_router(sites.router)
17
+ app.include_router(visits.router)
18
+ app.include_router(guests.router)
19
+
20
+ @app.on_event("startup")
21
+ def on_startup():
22
+ utils.create_db_and_tables()
23
+ crud.create_su()
24
+
25
+
26
+
27
+ @app.post("/login")
28
+ def login_for_access_token(
29
+ form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
30
+ ) -> Token:
31
+ user = crud.authenticate_user(form_data.username, form_data.password)
32
+ if not user:
33
+ raise HTTPException(
34
+ status_code=status.HTTP_401_UNAUTHORIZED,
35
+ detail="Incorrect username or password",
36
+ headers={"WWW-Authenticate": "Bearer"},
37
+ )
38
+ access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
39
+ access_token = utils.create_access_token(
40
+ data={"sub": user.username}, expires_delta=access_token_expires
41
+ )
42
+ return Token(access_token=access_token, token_type="bearer")
43
+
44
+
45
+
46
+ @app.post("/register", response_model=UserPublicMe)
47
+ def register_user(*, session: Session = Depends(utils.get_session), user: UserCreate):
48
+ return crud.add_user(session, user)
49
+
50
+
51
+
52
+ @app.get("/seed")
53
+ def seed_db(*,
54
+ session: Session = Depends(utils.get_session),
55
+ user: Annotated[User, Depends(crud.get_current_super_user)],
56
+ ):
57
+ u1 = User(username="cs_admin", password=utils.get_password_hash("csa"))
58
+ u2 = User(username="is_admin", password=utils.get_password_hash("isa"))
59
+ u3 = User(username="cs_is_admin", password=utils.get_password_hash("csisa"))
60
+ u4 = User(username="ee_admin", password=utils.get_password_hash("eea"))
61
+ s1 = Site(name="CS Dept", location="MCS", in_camera="2a")
62
+ s2 = Site(name="IS Dept", location="NRB", in_camera="3a")
63
+ s3 = Site(name="EE Dept", location="MCS", in_camera="1a")
64
+ u1.sites.append(s1)
65
+ u2.sites.append(s2)
66
+ u3.sites.extend([s1, s2])
67
+ u4.sites.append(s3)
68
+ session.add(u1)
69
+ session.add(u2)
70
+ session.add(u3)
71
+ session.add(u4)
72
+ session.add(s1)
73
+ session.add(s2)
74
+ session.add(s3)
75
+ session.add(Guest(name="Arsalan", vector="9874676132", is_female=False,site_id=1))
76
+ session.add(Guest(name="Zubeela", vector="8564245686", is_female=True, site_id=1))
77
+ session.add(Guest(name="Zarnaab", vector="5458615856", is_female=False,site_id=2))
78
+ session.add(Guest(name="Sarmaad", vector="1265645865", is_female=False,site_id=3))
79
+ session.add(Visit(is_group=False, is_female=False, is_new=False, site_id=1, guest_id=1))
80
+ session.add(Visit(is_group=False, is_female=True, is_new=True, site_id=1, guest_id=2))
81
+ session.add(Visit(is_group=True, is_female=False, is_new=False, site_id=2, guest_id=1))
82
+ session.add(Visit(is_group=True, is_female=False, is_new=False, site_id=2, guest_id=3))
83
+ session.add(Visit(is_group=False, is_female=False, is_new=False, site_id=2, guest_id=4))
84
+ session.add(Visit(is_group=False, is_female=False, is_new=False, site_id=3, guest_id=4))
85
+ session.commit()
86
+
models.py ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime, date, time
2
+ from sqlmodel import SQLModel, Field, Relationship
3
+
4
+
5
+ # --------------------
6
+ # --- Token Model ----
7
+ # --------------------
8
+
9
+ class Token(SQLModel):
10
+ access_token: str
11
+ token_type: str
12
+
13
+ class TokenData(SQLModel):
14
+ username: str | None = None
15
+
16
+
17
+
18
+ # --------------------
19
+ # --- Many-to-Many ---
20
+ # --------------------
21
+
22
+ class UserSiteLink(SQLModel, table=True):
23
+ user_id: int | None = Field(default=None, foreign_key="user.id", primary_key=True)
24
+ site_id: int | None = Field(default=None, foreign_key="site.id", primary_key=True)
25
+
26
+
27
+
28
+ # --------------------
29
+ # --- User Model -----
30
+ # --------------------
31
+
32
+ class UserBase(SQLModel):
33
+ username: str = Field(unique=True)
34
+ email: str | None = None
35
+ full_name: str | None = None
36
+
37
+ class User(UserBase, table=True):
38
+ id: int | None = Field(default=None, primary_key=True)
39
+ password: str
40
+ disabled: bool | None = False
41
+ is_su: bool | None = False
42
+ created_at: datetime | None = datetime.now()
43
+ updated_at: datetime | None = datetime.now()
44
+ sites: list["Site"] = Relationship(back_populates="users", link_model=UserSiteLink)
45
+
46
+
47
+ class UserCreate(SQLModel):
48
+ email: str
49
+ full_name: str
50
+ password: str
51
+
52
+ class UserPublic(UserBase):
53
+ id: int
54
+ disabled: bool
55
+ is_su: bool
56
+ created_at: datetime
57
+ updated_at: datetime
58
+
59
+ class UserUpdate(SQLModel):
60
+ username: str | None = None
61
+ email: str | None = None
62
+ full_name: str | None = None
63
+ password: str | None = None
64
+ disabled: bool | None = None
65
+ is_su: bool | None = None
66
+
67
+ class UserPublicMe(UserBase):
68
+ id: int
69
+
70
+ class UserUpdateMe(SQLModel):
71
+ username: str | None = None
72
+ email: str | None = None
73
+ full_name: str | None = None
74
+ password: str | None = None
75
+
76
+
77
+
78
+ # --------------------
79
+ # --- Site Model ----
80
+ # --------------------
81
+
82
+ class SiteBase(SQLModel):
83
+ name: str = Field(unique=True)
84
+ location: str | None = None
85
+ contact: str | None = None
86
+ in_camera: str | None = Field(default=None, unique=True)
87
+ out_camera: str | None = Field(default=None, unique=True)
88
+
89
+ class Site(SiteBase, table=True):
90
+ id: int | None = Field(default=None, primary_key=True)
91
+ created_at: datetime | None = datetime.now()
92
+ updated_at: datetime | None = datetime.now()
93
+ visits: list["Visit"] = Relationship(back_populates="site")
94
+ hosts: list["Guest"] = Relationship(back_populates="site")
95
+ users: list[User] = Relationship(back_populates="sites", link_model=UserSiteLink)
96
+
97
+ class SiteCreate(SiteBase):
98
+ pass
99
+
100
+ class SitePublic(SiteBase):
101
+ id: int
102
+ created_at: datetime | None = None
103
+ updated_at: datetime | None = None
104
+
105
+ class SiteUpdate(SQLModel):
106
+ name: str | None = None
107
+ location: str | None = None
108
+ contact: str | None = None
109
+ in_camera: str | None = Field(default=None, unique=True)
110
+ out_camera: str | None = Field(default=None, unique=True)
111
+
112
+ class SitePublicMe(SiteBase):
113
+ id: int
114
+
115
+
116
+ # --------------------
117
+ # --- Guest Model ----
118
+ # --------------------
119
+
120
+ class GuestBase(SQLModel):
121
+ name: str | None = None
122
+ vector: str = Field(unique=True)
123
+ is_female: bool | None = None
124
+
125
+ class Guest(GuestBase, table=True):
126
+ id: int | None = Field(default=None, primary_key=True)
127
+ site_id: int | None = Field(default=None, foreign_key="site.id")
128
+ created_at: datetime | None = datetime.now()
129
+ updated_at: datetime | None = datetime.now()
130
+ visits: list["Visit"] = Relationship(back_populates="guest")
131
+ site: Site | None = Relationship(back_populates="hosts")
132
+
133
+ class GuestCreateMe(GuestBase):
134
+ pass
135
+
136
+ class GuestCreate(GuestCreateMe):
137
+ site_id: int | None = None
138
+
139
+ class GuestPublicMe(GuestBase):
140
+ id: int
141
+ site_id: int | None = None
142
+
143
+ class GuestPublic(GuestPublicMe):
144
+ created_at: datetime | None = None
145
+ updated_at: datetime | None = None
146
+
147
+ class GuestUpdateMe(SQLModel):
148
+ name: str | None = None
149
+ vector: str | None = None
150
+ is_female: bool | None = None
151
+
152
+ class GuestUpdate(GuestUpdateMe):
153
+ site_id: bool | None = None
154
+
155
+
156
+
157
+ # --------------------
158
+ # --- Visit Model ----
159
+ # --------------------
160
+
161
+ class VisitBase(SQLModel):
162
+ date_in: date | None = datetime.now().date()
163
+ time_in: time | None = datetime.now().time()
164
+ time_out: time | None = None
165
+ is_group: bool | None = False
166
+ is_female: bool | None = None
167
+ is_new: bool | None = False
168
+ site_id: int | None = Field(default=None, foreign_key="site.id")
169
+ guest_id: int | None = Field(default=None, foreign_key="guest.id")
170
+
171
+ class Visit(VisitBase, table=True):
172
+ id: int | None = Field(default=None, primary_key=True)
173
+ site: Site | None = Relationship(back_populates="visits")
174
+ guest: Guest | None = Relationship(back_populates="visits")
175
+
176
+ class VisitCreate(VisitBase):
177
+ pass
178
+
179
+ class VisitPublic(VisitBase):
180
+ id: int
181
+
182
+ class VisitUpdate(SQLModel):
183
+ time_out: time | None = None
184
+ is_group: bool | None = None
185
+ is_female: bool | None = None
186
+ is_new: bool | None = None
187
+
188
+
189
+ # --------------------
190
+ # --- Relational -----
191
+ # --------------------
192
+
193
+ class UserPublicWith(UserPublic):
194
+ sites: list[SitePublic] = []
195
+
196
+ class VisitPublicWith(VisitPublic):
197
+ site: SitePublic | None = None
198
+ guest: GuestPublic | None = None
199
+
200
+ class SitePublicWith(SitePublic):
201
+ visits: list[VisitPublic] = []
202
+ hosts: list[GuestPublic] = []
203
+ users: list[UserPublic] = []
204
+
205
+ class GuestPublicWith(GuestPublic):
206
+ # site: SitePublic | None = None
207
+ visits: list[VisitPublic] = []
208
+
209
+ class VisitPublicWithGuest(VisitPublic):
210
+ guest: GuestPublicMe | None = None
211
+
212
+ class UserPublicMeWith(UserPublicMe):
213
+ sites: list[SitePublicMe] = []
214
+
215
+ class SitePublicMeWith(SitePublicMe):
216
+ visits: list[VisitPublicWithGuest] = []
217
+ hosts: list[GuestPublicMe] = []
218
+
routers/__init__.py ADDED
File without changes
routers/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (170 Bytes). View file
 
routers/__pycache__/guests.cpython-311.pyc ADDED
Binary file (4.6 kB). View file
 
routers/__pycache__/me.cpython-311.pyc ADDED
Binary file (164 Bytes). View file
 
routers/__pycache__/profile.cpython-311.pyc ADDED
Binary file (9.05 kB). View file
 
routers/__pycache__/sites.cpython-311.pyc ADDED
Binary file (4.43 kB). View file
 
routers/__pycache__/users.cpython-311.pyc ADDED
Binary file (3.73 kB). View file
 
routers/__pycache__/visits.cpython-311.pyc ADDED
Binary file (4.53 kB). View file
 
routers/guests.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+ from fastapi import Depends, APIRouter, HTTPException, Query
3
+ from models import Guest, GuestCreate, GuestPublic, GuestUpdate, GuestPublicWith
4
+ from sqlmodel import Session, select
5
+ from core import crud, utils
6
+
7
+ router = APIRouter(
8
+ prefix="/guests",
9
+ tags=["guests"],
10
+ dependencies=[Depends(crud.get_current_super_user)]
11
+ )
12
+
13
+ @router.post("/", response_model=GuestPublic)
14
+ def create_guest(*,
15
+ session: Session = Depends(utils.get_session),
16
+ guest: GuestCreate
17
+ ):
18
+ crud.vector_exists(session, guest)
19
+ db_guest = Guest.model_validate(guest)
20
+ session.add(db_guest)
21
+ session.commit()
22
+ session.refresh(db_guest)
23
+ return db_guest
24
+
25
+
26
+ @router.get("/", response_model=list[GuestPublic])
27
+ def read_guests(*,
28
+ session: Session = Depends(utils.get_session),
29
+ offset: int = 0,
30
+ limit: int = Query(default=100, le=100),
31
+ ):
32
+ guests = session.exec(select(Guest).offset(offset).limit(limit)).all()
33
+ return guests
34
+
35
+
36
+
37
+ @router.get("/{guest_id}", response_model=GuestPublicWith)
38
+ def read_guest(*,
39
+ session: Session = Depends(utils.get_session),
40
+ guest_id: int
41
+ ):
42
+ guest = session.get(Guest, guest_id)
43
+ if not guest:
44
+ raise HTTPException(status_code=404, detail="Guest not found")
45
+ return guest
46
+
47
+
48
+
49
+ @router.patch("/{guest_id}", response_model=GuestPublic)
50
+ def update_guest(*,
51
+ session: Session = Depends(utils.get_session),
52
+ guest_id: int,
53
+ guest: GuestUpdate
54
+ ):
55
+ db_guest = session.get(Guest, guest_id)
56
+ if not db_guest:
57
+ raise HTTPException(status_code=404, detail="Guest not found")
58
+ if guest.vector is not None:
59
+ crud.vector_exists(session, guest)
60
+ guest_data = guest.model_dump(exclude_unset=True)
61
+ extra_data = {"updated_at": datetime.now()}
62
+ db_guest.sqlmodel_update(guest_data, update=extra_data)
63
+ session.add(db_guest)
64
+ session.commit()
65
+ session.refresh(db_guest)
66
+ return db_guest
67
+
68
+
69
+ @router.delete("/{guest_id}")
70
+ def delete_guest(*,
71
+ session: Session = Depends(utils.get_session),
72
+ guest_id: int
73
+ ):
74
+ db_guest = session.get(Guest, guest_id)
75
+ if not db_guest:
76
+ raise HTTPException(status_code=404, detail="Guest not found")
77
+ session.delete(db_guest)
78
+ session.commit()
79
+ return {"ok": True}
routers/profile.py ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+ from typing import Annotated
3
+ from fastapi import Depends, APIRouter
4
+ from sqlmodel import Session
5
+ from models import User, UserPublicMe, UserPublicMeWith, UserUpdateMe
6
+ from models import Site, SiteCreate, SitePublicMe, SitePublicMeWith, SiteUpdate
7
+ from models import Guest, GuestCreateMe, GuestPublicMe, GuestPublicWith, GuestUpdateMe
8
+ from core import crud, utils
9
+
10
+ router = APIRouter(
11
+ prefix="/dashboard",
12
+ tags=["dashboard"],
13
+ )
14
+
15
+
16
+ # @router.get("/", response_model=UserPublicMeWith)
17
+ # def read_me(current_user: Annotated[User, Depends(crud.get_current_active_user)]):
18
+ # return current_user
19
+
20
+
21
+ @router.get("/", response_model=UserPublicMeWith)
22
+ def read_dashboard(*,
23
+ session: Session = Depends(utils.get_session),
24
+ current_user: Annotated[User, Depends(crud.get_current_active_user)]
25
+ ):
26
+ session.add(current_user)
27
+ return current_user
28
+
29
+
30
+ @router.patch("/", response_model=UserPublicMe)
31
+ def update_dashboard(*,
32
+ session: Session = Depends(utils.get_session),
33
+ current_user: Annotated[User, Depends(crud.get_current_active_user)],
34
+ user: UserUpdateMe
35
+ ):
36
+ return crud.edit_user(session, current_user, user)
37
+
38
+
39
+
40
+ @router.delete("/")
41
+ def delete_dashboard(*,
42
+ session: Session = Depends(utils.get_session),
43
+ current_user: Annotated[User, Depends(crud.get_current_active_user)],
44
+ ):
45
+ session.delete(current_user)
46
+ session.commit()
47
+ return {"ok": True}
48
+
49
+
50
+ # --------------------
51
+ # ------ Sites -------
52
+ # --------------------
53
+
54
+ @router.post("/sites", response_model=SitePublicMe)
55
+ def create_site(*,
56
+ session: Session = Depends(utils.get_session),
57
+ current_user: Annotated[User, Depends(crud.get_current_active_user)],
58
+ site: SiteCreate
59
+ ):
60
+ crud.camera_exists(session, site)
61
+ extra_data = {"created_at": datetime.now(),
62
+ "updated_at": datetime.now()}
63
+ db_site = Site.model_validate(site, update=extra_data)
64
+ db_site = crud.push_site(session, db_site)
65
+ session.add(current_user)
66
+ current_user.sites.append(db_site)
67
+ session.commit()
68
+ return db_site
69
+
70
+
71
+
72
+ @router.get("/sites/{site_id}", response_model=SitePublicMeWith)
73
+ def read_site(*,
74
+ session: Session = Depends(utils.get_session),
75
+ current_user: Annotated[User, Depends(crud.get_current_active_user)],
76
+ site_id: int
77
+ ):
78
+ return crud.get_current_site(session, current_user, site_id)
79
+
80
+
81
+
82
+ @router.patch("/sites/{site_id}", response_model=SitePublicMe)
83
+ def update_site(*,
84
+ session: Session = Depends(utils.get_session),
85
+ current_user: Annotated[User, Depends(crud.get_current_active_user)],
86
+ site_id: int,
87
+ site: SiteUpdate
88
+ ):
89
+ db_site = crud.get_current_site(session, current_user, site_id)
90
+ crud.camera_exists(session, db_site)
91
+ site_data = site.model_dump(exclude_unset=True)
92
+ extra_data = {"updated_at": datetime.now()}
93
+ db_site.sqlmodel_update(site_data, update=extra_data)
94
+ return crud.push_site(session, db_site)
95
+
96
+
97
+
98
+ @router.delete("/sites/{site_id}")
99
+ def delete_site(*,
100
+ session: Session = Depends(utils.get_session),
101
+ current_user: Annotated[User, Depends(crud.get_current_active_user)],
102
+ site_id: int):
103
+ db_site = crud.get_current_site(session, current_user, site_id)
104
+ session.delete(db_site)
105
+ session.commit()
106
+ return {"ok": True}
107
+
108
+
109
+ # --------------------
110
+ # ------ Hosts -------
111
+ # --------------------
112
+
113
+
114
+ @router.post("/sites/{side_id}/hosts", response_model=GuestPublicMe)
115
+ def create_host(*,
116
+ session: Session = Depends(utils.get_session),
117
+ current_user: Annotated[User, Depends(crud.get_current_active_user)],
118
+ site_id: int,
119
+ host: GuestCreateMe
120
+ ):
121
+ _ = crud.get_current_site(session, current_user, site_id)
122
+ if host.vector is not None:
123
+ crud.vector_exists(session, host)
124
+ extra_data = {"site_id": site_id}
125
+ db_host = Guest.model_validate(host, update=extra_data)
126
+ session.add(db_host)
127
+ session.commit()
128
+ session.refresh(db_host)
129
+ return db_host
130
+
131
+
132
+
133
+ @router.get("/sites/{site_id}/hosts/{host_id}", response_model=GuestPublicWith)
134
+ def read_host(*,
135
+ session: Session = Depends(utils.get_session),
136
+ current_user: Annotated[User, Depends(crud.get_current_active_user)],
137
+ site_id: int,
138
+ host_id: int,
139
+ ):
140
+ current_site = crud.get_current_site(session, current_user, site_id)
141
+ return crud.get_host_of_site(session, current_site, host_id)
142
+
143
+
144
+
145
+ @router.patch("/sites/{site_id}/hosts/{host_id}", response_model=GuestPublicMe)
146
+ def update_host(*,
147
+ session: Session = Depends(utils.get_session),
148
+ current_user: Annotated[User, Depends(crud.get_current_active_user)],
149
+ site_id: int,
150
+ host_id: int,
151
+ host: GuestUpdateMe
152
+ ):
153
+ current_site = crud.get_current_site(session, current_user, site_id)
154
+ db_host = crud.get_host_of_site(session, current_site, host_id)
155
+ if host.vector is not None:
156
+ crud.vector_exists(session, host)
157
+ host_data = host.model_dump(exclude_unset=True)
158
+ extra_data = {"site_id": site_id,
159
+ "updated_at": datetime.now()}
160
+ db_host.sqlmodel_update(host_data, update=extra_data)
161
+ session.add(db_host)
162
+ session.commit()
163
+ session.refresh(db_host)
164
+ return db_host
165
+
166
+
167
+ @router.delete("/sites/{site_id}/hosts/{host_id}")
168
+ def delete_host(*,
169
+ session: Session = Depends(utils.get_session),
170
+ current_user: Annotated[User, Depends(crud.get_current_active_user)],
171
+ site_id: int,
172
+ host_id: int
173
+ ):
174
+ current_site = crud.get_current_site(session, current_user, site_id)
175
+ db_host = crud.get_host_of_site(session, current_site, host_id)
176
+ session.delete(db_host)
177
+ session.commit()
178
+ return {"ok": True}
routers/sites.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+ from fastapi import Depends, APIRouter, HTTPException, Query
3
+ from models import Site, SiteCreate, SitePublic, SiteUpdate, SitePublicWith
4
+ from sqlmodel import Session, select
5
+ from core import crud, utils
6
+
7
+ router = APIRouter(
8
+ prefix="/sites",
9
+ tags=["sites"],
10
+ dependencies=[Depends(crud.get_current_super_user)]
11
+ )
12
+
13
+ @router.post("/", response_model=SitePublic)
14
+ def create_site(*,
15
+ session: Session = Depends(utils.get_session),
16
+ site: SiteCreate
17
+ ):
18
+ crud.camera_exists(session, site)
19
+ extra_data = {"created_at": datetime.now(),
20
+ "updated_at": datetime.now()}
21
+ db_site = Site.model_validate(site, update=extra_data)
22
+ return crud.push_site(session, db_site)
23
+
24
+
25
+ @router.get("/", response_model=list[SitePublic])
26
+ def read_sites(*,
27
+ session: Session = Depends(utils.get_session),
28
+ offset: int = 0,
29
+ limit: int = Query(default=100, le=100),
30
+ ):
31
+ sites = session.exec(select(Site).offset(offset).limit(limit)).all()
32
+ return sites
33
+
34
+
35
+ @router.get("/{site_id}", response_model=SitePublicWith)
36
+ def read_site(*,
37
+ session: Session = Depends(utils.get_session),
38
+ site_id: int
39
+ ):
40
+ db_site = session.get(Site, site_id)
41
+ if not db_site:
42
+ raise HTTPException(status_code=404, detail="Site not found")
43
+ return db_site
44
+
45
+
46
+ @router.patch("/{site_id}", response_model=SitePublic)
47
+ def update_site(*,
48
+ session: Session = Depends(utils.get_session),
49
+ site_id: int,
50
+ site: SiteUpdate
51
+ ):
52
+ crud.camera_exists(session, site)
53
+ db_site = session.get(Site, site_id)
54
+ if not db_site:
55
+ raise HTTPException(status_code=404, detail="Site not found")
56
+ site_data = site.model_dump(exclude_unset=True)
57
+ extra_data = {"updated_at": datetime.now()}
58
+ db_site.sqlmodel_update(site_data, update=extra_data)
59
+ return crud.push_site(session, db_site)
60
+
61
+
62
+
63
+ @router.delete("/{site_id}")
64
+ def delete_site(*,
65
+ session: Session = Depends(utils.get_session),
66
+ site_id: int
67
+ ):
68
+ db_site = session.get(Site, site_id)
69
+ if not db_site:
70
+ raise HTTPException(status_code=404, detail="Site not found")
71
+ session.delete(db_site)
72
+ session.commit()
73
+ return {"ok": True}
routers/users.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+ from fastapi import Depends, APIRouter, HTTPException, Query
3
+ from sqlmodel import Session, select
4
+ from models import User, UserCreate, UserPublic, UserUpdate, UserPublicWith
5
+ from core import crud, utils
6
+
7
+ router = APIRouter(
8
+ prefix="/users",
9
+ tags=["users"],
10
+ dependencies=[Depends(crud.get_current_super_user)]
11
+ )
12
+
13
+
14
+ @router.post("/", response_model=UserPublic)
15
+ def create_user(*, session: Session = Depends(utils.get_session), user: UserCreate):
16
+ return crud.add_user(session, user)
17
+
18
+
19
+ @router.get("/", response_model=list[UserPublic])
20
+ def read_users(*,
21
+ session: Session = Depends(utils.get_session),
22
+ offset: int = 0,
23
+ limit: int = Query(default=100, le=100),
24
+ ):
25
+ users = session.exec(select(User).offset(offset).limit(limit)).all()
26
+ return users
27
+
28
+
29
+ @router.get("/{user_id}", response_model=UserPublicWith)
30
+ def read_user(*, session: Session = Depends(utils.get_session), user_id: int):
31
+ user = session.get(User, user_id)
32
+ if not user:
33
+ raise HTTPException(status_code=404, detail="User not found")
34
+ return user
35
+
36
+
37
+ @router.patch("/{user_id}", response_model=UserPublic)
38
+ def update_user(*,
39
+ session: Session = Depends(utils.get_session),
40
+ user_id: int,
41
+ user: UserUpdate
42
+ ):
43
+ db_user = session.get(User, user_id)
44
+ if not db_user:
45
+ raise HTTPException(status_code=404, detail="User not found")
46
+ return crud.edit_user(session, db_user, user)
47
+
48
+
49
+
50
+ @router.delete("/{user_id}")
51
+ def delete_user(*, session: Session = Depends(utils.get_session), user_id: int):
52
+ user = session.get(User, user_id)
53
+ if not user:
54
+ raise HTTPException(status_code=404, detail="User not found")
55
+ session.delete(user)
56
+ session.commit()
57
+ return {"ok": True}
routers/visits.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+ from fastapi import Depends, APIRouter, HTTPException, Query
3
+ from models import Visit, VisitCreate, VisitPublic, VisitUpdate, VisitPublicWith
4
+ from sqlmodel import Session, select
5
+ from core import crud, utils
6
+
7
+ router = APIRouter(
8
+ prefix="/visits",
9
+ tags=["visits"],
10
+ dependencies=[Depends(crud.get_current_super_user)]
11
+ )
12
+
13
+ @router.post("/", response_model=VisitPublic)
14
+ def create_visit(*, session: Session = Depends(utils.get_session), visit: VisitCreate):
15
+ now = datetime.now()
16
+ extra_data = {"date_in": now.date(),
17
+ "time_in": now.time()}
18
+ db_visit = Visit.model_validate(visit, update=extra_data)
19
+ session.add(db_visit)
20
+ session.commit()
21
+ session.refresh(db_visit)
22
+ return db_visit
23
+
24
+ @router.get("/", response_model=list[VisitPublic])
25
+ def read_visits(*,
26
+ session: Session = Depends(utils.get_session),
27
+ offset: int = 0,
28
+ limit: int = Query(default=100, le=100),
29
+ ):
30
+ visits = session.exec(select(Visit).offset(offset).limit(limit)).all()
31
+ return visits
32
+
33
+ @router.get("/{visit_id}", response_model=VisitPublicWith)
34
+ def read_visit(*, session: Session = Depends(utils.get_session), visit_id: int):
35
+ visit = session.get(Visit, visit_id)
36
+ if not visit:
37
+ raise HTTPException(status_code=404, detail="Visit not found")
38
+ return visit
39
+
40
+ @router.patch("/{visit_id}", response_model=VisitPublic)
41
+ def update_visit(*,
42
+ session: Session = Depends(utils.get_session),
43
+ visit_id: int,
44
+ visit: VisitUpdate
45
+ ):
46
+ db_visit = session.get(Visit, visit_id)
47
+ if not db_visit:
48
+ raise HTTPException(status_code=404, detail="Visit not found")
49
+ visit_data = visit.model_dump(exclude_unset=True)
50
+ db_visit.sqlmodel_update(visit_data)
51
+ session.add(db_visit)
52
+ session.commit()
53
+ session.refresh(db_visit)
54
+ return db_visit
55
+
56
+
57
+ @router.delete("/{visit_id}")
58
+ def delete_visit(*, session: Session = Depends(utils.get_session), visit_id: int):
59
+ visit = session.get(Visit, visit_id)
60
+ if not visit:
61
+ raise HTTPException(status_code=404, detail="Visit not found")
62
+ session.delete(visit)
63
+ session.commit()
64
+ return {"ok": True}
test.db ADDED
Binary file (49.2 kB). View file