11from fastapi import FastAPI , UploadFile , File , Depends , HTTPException , Form
22from fastapi .middleware .cors import CORSMiddleware
3- from sqlalchemy import create_engine , Column , Integer , String , Date , Text , func
3+ from sqlalchemy import create_engine , Column , Integer , String , Date , Text
44from sqlalchemy .orm import sessionmaker , declarative_base , Session
55from pydantic import BaseModel
66from datetime import date
77import os , shutil
88from dotenv import load_dotenv
99import requests
1010
11+ # ✅ Import users and models
12+ from app .models import user
13+ from app .routers import users
14+
1115# Optional OpenAI
1216try :
1317 from openai import OpenAI
1418except Exception :
1519 OpenAI = None
1620
21+ # Load environment variables
1722load_dotenv ()
1823
24+ # === Database Setup ===
1925DATABASE_URL = os .getenv ("DATABASE_URL" , "sqlite:///./app.db" )
2026engine = create_engine (DATABASE_URL , pool_pre_ping = True )
2127SessionLocal = sessionmaker (autocommit = False , autoflush = False , bind = engine )
2228Base = declarative_base ()
2329
30+ # === Models ===
2431class Task (Base ):
2532 __tablename__ = "tasks"
2633 id = Column (Integer , primary_key = True , index = True )
@@ -39,17 +46,21 @@ class AuditLog(Base):
3946 action = Column (String (50 ))
4047 detail = Column (Text )
4148
49+ # ✅ Create all tables (includes users table too)
4250Base .metadata .create_all (bind = engine )
4351
52+ # === DB Session ===
4453def get_db ():
4554 db = SessionLocal ()
4655 try :
4756 yield db
4857 finally :
4958 db .close ()
5059
60+ # === FastAPI App ===
5161app = FastAPI (title = "FAT-EIBL (Edme) – API" )
5262
63+ # === CORS ===
5364allow = os .getenv ("ALLOW_ORIGINS" , "*" ).split ("," )
5465app .add_middleware (
5566 CORSMiddleware ,
@@ -59,7 +70,7 @@ def get_db():
5970 allow_headers = ["*" ],
6071)
6172
62- # Models
73+ # === Pydantic Schemas ===
6374class TaskCreate (BaseModel ):
6475 title : str
6576 department : str | None = None
@@ -79,14 +90,17 @@ class TaskOut(BaseModel):
7990 priority : str | None
8091 remarks : str | None
8192 attachment : str | None
93+
8294 class Config :
8395 from_attributes = True
8496
85- # Health
97+ # === Routes ===
98+
8699@app .get ("/health" )
87- def health (): return { "status" : "ok" }
100+ def health ():
101+ return {"status" : "ok" }
88102
89- # Tasks CRUD
103+ # --- TASK CRUD ---
90104@app .get ("/tasks" , response_model = list [TaskOut ])
91105def list_tasks (db : Session = Depends (get_db )):
92106 return db .query (Task ).order_by (Task .due_date .is_ (None ), Task .due_date ).all ()
@@ -103,35 +117,44 @@ def create_task(task: TaskCreate, db: Session = Depends(get_db)):
103117@app .put ("/tasks/{task_id}" , response_model = TaskOut )
104118def update_task (task_id : int , task : TaskCreate , db : Session = Depends (get_db )):
105119 obj = db .get (Task , task_id )
106- if not obj : raise HTTPException (status_code = 404 , detail = "Task not found" )
107- for k , v in task .dict ().items (): setattr (obj , k , v )
120+ if not obj :
121+ raise HTTPException (status_code = 404 , detail = "Task not found" )
122+ for k , v in task .dict ().items ():
123+ setattr (obj , k , v )
108124 db .add (AuditLog (action = "update" , detail = f"Task ID { task_id } " ))
109- db .commit (); db .refresh (obj )
125+ db .commit ()
126+ db .refresh (obj )
110127 return obj
111128
112129@app .delete ("/tasks/{task_id}" )
113130def delete_task (task_id : int , db : Session = Depends (get_db )):
114131 obj = db .get (Task , task_id )
115- if not obj : raise HTTPException (status_code = 404 , detail = "Task not found" )
116- db .delete (obj ); db .add (AuditLog (action = "delete" , detail = f"Task ID { task_id } " ))
117- db .commit (); return { "ok" : True }
132+ if not obj :
133+ raise HTTPException (status_code = 404 , detail = "Task not found" )
134+ db .delete (obj )
135+ db .add (AuditLog (action = "delete" , detail = f"Task ID { task_id } " ))
136+ db .commit ()
137+ return {"ok" : True }
118138
119- # Uploads
120- UPLOAD_DIR = os .path .join (os .getcwd (), "uploads" ); os .makedirs (UPLOAD_DIR , exist_ok = True )
139+ # --- UPLOAD ---
140+ UPLOAD_DIR = os .path .join (os .getcwd (), "uploads" )
141+ os .makedirs (UPLOAD_DIR , exist_ok = True )
121142
122143@app .post ("/upload/{task_id}" )
123144async def upload_file (task_id : int , file : UploadFile = File (...), db : Session = Depends (get_db )):
124145 obj = db .get (Task , task_id )
125- if not obj : raise HTTPException (status_code = 404 , detail = "Task not found" )
146+ if not obj :
147+ raise HTTPException (status_code = 404 , detail = "Task not found" )
126148 safe_name = f"task_{ task_id } _" + os .path .basename (file .filename )
127149 dest = os .path .join (UPLOAD_DIR , safe_name )
128- with open (dest , "wb" ) as buffer : shutil .copyfileobj (file .file , buffer )
150+ with open (dest , "wb" ) as buffer :
151+ shutil .copyfileobj (file .file , buffer )
129152 obj .attachment = safe_name
130153 db .add (AuditLog (action = "upload" , detail = f"Task ID { task_id } file { safe_name } " ))
131154 db .commit ()
132- return { "task_id" : task_id , "filename" : safe_name }
155+ return {"task_id" : task_id , "filename" : safe_name }
133156
134- # Seed
157+ # --- SEED ---
135158@app .get ("/seed" )
136159def seed (db : Session = Depends (get_db )):
137160 if db .query (Task ).count () == 0 :
@@ -143,21 +166,27 @@ def seed(db: Session = Depends(get_db)):
143166 db .add (t )
144167 db .add (AuditLog (action = "seed" , detail = "Sample tasks added" ))
145168 db .commit ()
146- return { "seeded" : True }
169+ return {"seeded" : True }
147170
148- # AI Chat (proxy to OpenAI if key provided)
171+ # --- AI CHAT ---
149172@app .post ("/ai/chat" )
150173def ai_chat (prompt : str = Form (...)):
151174 api_key = os .getenv ("OPENAI_API_KEY" , "" )
152175 if not api_key or OpenAI is None :
153- return { "reply" : "AI is not configured. Please set OPENAI_API_KEY in backend/.env." }
176+ return {"reply" : "AI is not configured. Please set OPENAI_API_KEY in backend/.env." }
154177 client = OpenAI (api_key = api_key )
155178 try :
156179 resp = client .chat .completions .create (
157180 model = "gpt-4o-mini" ,
158- messages = [{"role" :"system" ,"content" :"You are Vani, an AI assistant for an audit tracker." },
159- {"role" :"user" ,"content" :prompt }]
181+ messages = [
182+ {"role" : "system" , "content" : "You are Vani, an AI assistant for an audit tracker." },
183+ {"role" : "user" , "content" : prompt }
184+ ]
160185 )
161- return { "reply" : resp .choices [0 ].message .content }
186+ return {"reply" : resp .choices [0 ].message .content }
162187 except Exception as e :
163- return { "reply" : f"AI error: { e } " }
188+ return {"reply" : f"AI error: { e } " }
189+
190+ # --- USER ROUTES ---
191+ app .include_router (users .router , prefix = "/users" , tags = ["Users" ])
192+
0 commit comments