Praneeth Yerrapragada commited on
Commit
60ef74b
1 Parent(s): 878d807

feat: crud apis for users table

Browse files
.dockerignore CHANGED
@@ -1,3 +1,4 @@
1
  __pycache__
2
  storage
3
- data/*
 
 
1
  __pycache__
2
  storage
3
+ data/*
4
+ venv
.gitignore CHANGED
@@ -1,4 +1,5 @@
1
  __pycache__
2
  storage
3
  .env
4
- data/*
 
 
1
  __pycache__
2
  storage
3
  .env
4
+ data/*
5
+ venv
.vscode/settings.json CHANGED
@@ -1,3 +1,11 @@
1
  {
2
- "jupyter.notebookFileRoot": "${workspaceFolder}"
 
 
 
 
 
 
 
 
3
  }
 
1
  {
2
+ "jupyter.notebookFileRoot": "${workspaceFolder}",
3
+ "[python]": {
4
+ "editor.defaultFormatter": "ms-python.black-formatter",
5
+ "editor.formatOnSave": true,
6
+ },
7
+ "ms-python.black-formatter.args": [
8
+ "--line-length",
9
+ "119"
10
+ ]
11
  }
app/api/routers/user.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List
2
+ from fastapi import APIRouter, Depends, HTTPException
3
+ from sqlalchemy.orm import Session
4
+
5
+ from app.engine.postgresdb import get_db
6
+ from app.schema.index import UserCreate, User as UserSchema
7
+ from app.model.user import User as UserModel
8
+
9
+
10
+ user_router = r = APIRouter(prefix="/api/v1/users", tags=["users"])
11
+
12
+
13
+ @r.post(
14
+ "/",
15
+ response_model=UserSchema,
16
+ responses={
17
+ 200: {"description": "New user created"},
18
+ 400: {"description": "Bad request"},
19
+ 409: {"description": "Conflict"},
20
+ 500: {"description": "Internal server error"},
21
+ },
22
+ )
23
+ async def create_user(user: UserCreate, db: Session = Depends(get_db)):
24
+ try:
25
+ db_user = (
26
+ db.query(UserModel).filter(UserModel.email == user.email).sort_by(UserModel.updated_at, desc=True).first()
27
+ )
28
+ if db_user and not db_user.is_deleted:
29
+ raise HTTPException(status_code=409, detail="User already exists")
30
+
31
+ db_user = UserModel(**user.dict())
32
+ db.add(db_user)
33
+ db.commit()
34
+ db.refresh(db_user)
35
+ return db_user
36
+ except Exception as e:
37
+ raise HTTPException(status_code=500, detail=str(e))
38
+
39
+
40
+ @r.get(
41
+ "/{user_id}",
42
+ response_model=UserSchema,
43
+ responses={
44
+ 200: {"description": "User found"},
45
+ 404: {"description": "User not found"},
46
+ 500: {"description": "Internal server error"},
47
+ },
48
+ )
49
+ async def get_user(user_id: int, db: Session = Depends(get_db)):
50
+ user = db.query(UserModel).get(user_id)
51
+ if not user:
52
+ raise HTTPException(status_code=404, detail="User not found")
53
+ return user
54
+
55
+
56
+ @r.put(
57
+ "/{user_id}",
58
+ response_model=UserSchema,
59
+ responses={
60
+ 200: {"description": "User updated"},
61
+ 404: {"description": "User not found"},
62
+ 500: {"description": "Internal server error"},
63
+ },
64
+ )
65
+ async def update_user(user_id: int, user_payload: UserCreate, db: Session = Depends(get_db)):
66
+ user = db.query(UserModel).get(user_id)
67
+ if not user:
68
+ raise HTTPException(status_code=404, detail="User not found")
69
+ user.name = user_payload.name
70
+ user.email = user_payload.email
71
+ db.commit()
72
+ db.refresh(user)
73
+ return user
74
+
75
+
76
+ @r.delete(
77
+ "/{user_id}",
78
+ response_model=UserSchema,
79
+ responses={
80
+ 200: {"description": "User deleted"},
81
+ 404: {"description": "User not found"},
82
+ 500: {"description": "Internal server error"},
83
+ },
84
+ )
85
+ async def delete_user(user_id: int, db: Session = Depends(get_db)):
86
+ user = db.query(UserModel).get(user_id)
87
+ if not user:
88
+ raise HTTPException(status_code=404, detail="User not found")
89
+ user.is_deleted = True
90
+ db.commit()
91
+ db.refresh(user)
92
+ return user
app/engine/postgresdb.py CHANGED
@@ -2,14 +2,15 @@ import os
2
  import logging
3
  from sqlalchemy import create_engine
4
  from sqlalchemy.orm import sessionmaker, declarative_base, Session
 
5
 
6
  from dotenv import load_dotenv
7
 
8
  load_dotenv()
9
  logger = logging.getLogger(__name__)
10
 
11
- SQLALCHEMY_DATABASE_URL = os.getenv('SQLALCHEMY_DATABASE_URL')
12
- assert SQLALCHEMY_DATABASE_URL, f'SQLALCHEMY_DATABASE_URL is not set'
13
  logger.info(f"SQLALCHEMY_DATABASE_URL: {SQLALCHEMY_DATABASE_URL}")
14
 
15
  engine = create_engine(SQLALCHEMY_DATABASE_URL)
@@ -27,3 +28,8 @@ Base = declarative_base()
27
  # finally:
28
  # session.close()
29
  # logger.info("Postgres Session closed successfully!")
 
 
 
 
 
 
2
  import logging
3
  from sqlalchemy import create_engine
4
  from sqlalchemy.orm import sessionmaker, declarative_base, Session
5
+ from fastapi import Request
6
 
7
  from dotenv import load_dotenv
8
 
9
  load_dotenv()
10
  logger = logging.getLogger(__name__)
11
 
12
+ SQLALCHEMY_DATABASE_URL = os.getenv("SQLALCHEMY_DATABASE_URL")
13
+ assert SQLALCHEMY_DATABASE_URL, f"SQLALCHEMY_DATABASE_URL is not set"
14
  logger.info(f"SQLALCHEMY_DATABASE_URL: {SQLALCHEMY_DATABASE_URL}")
15
 
16
  engine = create_engine(SQLALCHEMY_DATABASE_URL)
 
28
  # finally:
29
  # session.close()
30
  # logger.info("Postgres Session closed successfully!")
31
+
32
+
33
+ # Dependency
34
+ def get_db(request: Request):
35
+ return request.state.db
app/model/user.py CHANGED
@@ -1,13 +1,16 @@
1
- from sqlalchemy import Column, String
2
  from sqlalchemy.orm import relationship
3
 
4
  from app.model.base import BaseModel
5
  from app.engine.postgresdb import Base
6
 
 
7
  class User(Base, BaseModel):
8
  __tablename__ = "users"
9
 
10
  name = Column(String)
11
  email = Column(String)
 
 
12
 
13
- transactions = relationship("Transaction", back_populates="user")
 
1
+ from sqlalchemy import Column, String, Boolean
2
  from sqlalchemy.orm import relationship
3
 
4
  from app.model.base import BaseModel
5
  from app.engine.postgresdb import Base
6
 
7
+
8
  class User(Base, BaseModel):
9
  __tablename__ = "users"
10
 
11
  name = Column(String)
12
  email = Column(String)
13
+ hashed_password = Column(String)
14
+ is_deleted = Column(Boolean, default=False)
15
 
16
+ transactions = relationship("Transaction", back_populates="user")
app/schema/base.py CHANGED
@@ -8,4 +8,4 @@ class BaseModel(BaseModel):
8
  updated_at: datetime
9
 
10
  class Config:
11
- orm_mode = True
 
8
  updated_at: datetime
9
 
10
  class Config:
11
+ from_attributes = True
app/schema/{transaction.py → index.py} RENAMED
@@ -1,8 +1,8 @@
1
  from enum import Enum
2
  from datetime import datetime
 
3
 
4
  from app.schema.base import BaseModel
5
- from app.schema.user import User
6
 
7
 
8
  class TransactionType(str, Enum):
@@ -10,6 +10,20 @@ class TransactionType(str, Enum):
10
  EXPENSE = "expense"
11
 
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  class Transaction(BaseModel):
14
  transaction_date: datetime
15
  category: str
 
1
  from enum import Enum
2
  from datetime import datetime
3
+ from typing import List
4
 
5
  from app.schema.base import BaseModel
 
6
 
7
 
8
  class TransactionType(str, Enum):
 
10
  EXPENSE = "expense"
11
 
12
 
13
+ class UserCreate(BaseModel):
14
+ name: str
15
+ email: str
16
+ hashed_password: str
17
+
18
+
19
+ class User(BaseModel):
20
+ name: str
21
+ email: str
22
+ hashed_password: str
23
+ is_deleted: bool = False
24
+ transactions: "List[Transaction]"
25
+
26
+
27
  class Transaction(BaseModel):
28
  transaction_date: datetime
29
  category: str
app/schema/user.py DELETED
@@ -1,10 +0,0 @@
1
- from typing import List
2
-
3
- from app.schema.base import BaseModel
4
- from app.schema.transaction import Transaction
5
-
6
-
7
- class User(BaseModel):
8
- name: str
9
- email: str
10
- transactions: "List[Transaction]"
 
 
 
 
 
 
 
 
 
 
 
main.py CHANGED
@@ -10,6 +10,7 @@ from fastapi import FastAPI, Request, Response
10
  from fastapi.middleware.cors import CORSMiddleware
11
  from fastapi.responses import RedirectResponse
12
  from app.api.routers.chat import chat_router
 
13
  from app.settings import init_settings
14
  from app.observability import init_observability
15
  from fastapi.staticfiles import StaticFiles
@@ -24,10 +25,12 @@ logger = logging.getLogger("uvicorn")
24
  # Create all tables in the database
25
  Base.metadata.create_all(bind=engine)
26
 
 
27
  def run_migrations():
28
  alembic_cfg = Config("alembic.ini")
29
  command.upgrade(alembic_cfg, "head")
30
 
 
31
  @asynccontextmanager
32
  async def lifespan(app_: FastAPI):
33
  logger.info("Starting up...")
@@ -36,8 +39,10 @@ async def lifespan(app_: FastAPI):
36
  yield
37
  logger.info("Shutting down...")
38
 
 
39
  app = FastAPI(lifespan=lifespan)
40
 
 
41
  @app.middleware("http")
42
  async def db_session_middleware(request: Request, call_next):
43
  response = Response("Internal server error", status_code=500)
@@ -49,10 +54,6 @@ async def db_session_middleware(request: Request, call_next):
49
  return response
50
 
51
 
52
- # Dependency
53
- def get_db(request: Request):
54
- return request.state.db
55
-
56
  init_settings()
57
  init_observability()
58
 
@@ -77,6 +78,7 @@ if environment == "dev":
77
  if os.path.exists("data"):
78
  app.mount("/api/data", StaticFiles(directory="data"), name="static")
79
  app.include_router(chat_router, prefix="/api/chat")
 
80
 
81
 
82
  if __name__ == "__main__":
 
10
  from fastapi.middleware.cors import CORSMiddleware
11
  from fastapi.responses import RedirectResponse
12
  from app.api.routers.chat import chat_router
13
+ from app.api.routers.user import user_router
14
  from app.settings import init_settings
15
  from app.observability import init_observability
16
  from fastapi.staticfiles import StaticFiles
 
25
  # Create all tables in the database
26
  Base.metadata.create_all(bind=engine)
27
 
28
+
29
  def run_migrations():
30
  alembic_cfg = Config("alembic.ini")
31
  command.upgrade(alembic_cfg, "head")
32
 
33
+
34
  @asynccontextmanager
35
  async def lifespan(app_: FastAPI):
36
  logger.info("Starting up...")
 
39
  yield
40
  logger.info("Shutting down...")
41
 
42
+
43
  app = FastAPI(lifespan=lifespan)
44
 
45
+
46
  @app.middleware("http")
47
  async def db_session_middleware(request: Request, call_next):
48
  response = Response("Internal server error", status_code=500)
 
54
  return response
55
 
56
 
 
 
 
 
57
  init_settings()
58
  init_observability()
59
 
 
78
  if os.path.exists("data"):
79
  app.mount("/api/data", StaticFiles(directory="data"), name="static")
80
  app.include_router(chat_router, prefix="/api/chat")
81
+ app.include_router(user_router)
82
 
83
 
84
  if __name__ == "__main__":
pyproject.toml CHANGED
@@ -41,6 +41,9 @@ version = "^0.23.2"
41
  [tool.poetry.dependencies.llama-index-agent-openai]
42
  version = "0.2.2"
43
 
 
 
 
44
  [build-system]
45
  requires = [ "poetry-core" ]
46
  build-backend = "poetry.core.masonry.api"
 
41
  [tool.poetry.dependencies.llama-index-agent-openai]
42
  version = "0.2.2"
43
 
44
+ [tool.black]
45
+ line-length = 119
46
+
47
  [build-system]
48
  requires = [ "poetry-core" ]
49
  build-backend = "poetry.core.masonry.api"