muhammadshaheryar commited on
Commit
43fecd8
·
verified ·
1 Parent(s): 94b03a1

Upload main.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. main.py +233 -0
main.py ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FastAPI application for Todo management."""
2
+ import os
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ # Add src to path for imports - important for Railway deployment
7
+ src_path = Path(__file__).parent / "src"
8
+ sys.path.insert(0, str(src_path))
9
+
10
+ from contextlib import asynccontextmanager
11
+ from datetime import datetime
12
+ from typing import Optional
13
+ from fastapi import FastAPI, Depends, HTTPException, Query
14
+ from fastapi.middleware.cors import CORSMiddleware
15
+ from sqlalchemy.orm import Session
16
+
17
+ from database import get_db, engine, Base
18
+ from models.todo import Todo
19
+ from models.user import User
20
+ from schemas.todo import TodoCreate, TodoUpdate, TodoResponse, TodoListResponse
21
+ from services.todo_service import TodoService
22
+ from api.routes.auth import router as auth_router
23
+ from api.chat_router import router as chat_router
24
+ from api.task_router import router as task_router
25
+ from middleware.auth import get_current_user
26
+
27
+
28
+ @asynccontextmanager
29
+ def lifespan(app: FastAPI):
30
+ Base.metadata.create_all(bind=engine)
31
+ yield
32
+
33
+
34
+ app = FastAPI(
35
+ title="Todo API",
36
+ description="REST API for managing todo items with extended features",
37
+ version="2.0.0",
38
+ lifespan=lifespan
39
+ )
40
+
41
+ # Configure CORS for production - allow your frontend domain
42
+ # For production, replace with your actual frontend URL
43
+ frontend_url = os.getenv("FRONTEND_URL", "https://localhost:3000")
44
+ allow_origins = [frontend_url]
45
+
46
+ # Add localhost origins for development if in development mode
47
+ environment = os.getenv("ENVIRONMENT", "development")
48
+ if environment.lower() != "production":
49
+ allow_origins.extend([
50
+ "http://localhost:3000",
51
+ "http://localhost:3001",
52
+ "http://localhost:3002",
53
+ "http://localhost:3006",
54
+ "http://localhost:3007"
55
+ ])
56
+
57
+ app.add_middleware(
58
+ CORSMiddleware,
59
+ allow_origins=allow_origins,
60
+ allow_credentials=True,
61
+ allow_methods=["*"],
62
+ allow_headers=["*"],
63
+ )
64
+
65
+ # Include auth routes
66
+ app.include_router(auth_router, prefix="/api/v1")
67
+
68
+ # Include chat and task routes
69
+ app.include_router(chat_router)
70
+ app.include_router(task_router, prefix="/api/v1")
71
+
72
+
73
+ def _calculate_overdue(todo: Todo) -> bool:
74
+ """Calculate if a todo is overdue."""
75
+ if todo.due_date is None or todo.completed:
76
+ return False
77
+ return todo.due_date < datetime.utcnow()
78
+
79
+
80
+ def _todo_to_response(todo: Todo) -> TodoResponse:
81
+ """Convert Todo model to TodoResponse schema."""
82
+ response_data = {
83
+ "id": todo.id,
84
+ "user_id": todo.user_id,
85
+ "title": todo.title,
86
+ "description": todo.description,
87
+ "completed": todo.completed,
88
+ "priority": todo.priority,
89
+ "tags": todo.tags,
90
+ "due_date": todo.due_date,
91
+ "recurrence": todo.recurrence,
92
+ "created_at": todo.created_at,
93
+ "updated_at": todo.updated_at,
94
+ "overdue": _calculate_overdue(todo)
95
+ }
96
+ return TodoResponse(**response_data)
97
+
98
+
99
+ @app.get("/api/v1/todos", response_model=TodoListResponse)
100
+ def get_todos(
101
+ db: Session = Depends(get_db),
102
+ current_user: User = Depends(get_current_user),
103
+ search: Optional[str] = Query(None, description="Search in title and description"),
104
+ status: Optional[str] = Query(None, pattern="^(completed|pending)$", description="Filter by status"),
105
+ priority: Optional[str] = Query(None, pattern="^(low|medium|high)$", description="Filter by priority"),
106
+ due_before: Optional[datetime] = Query(None, description="Filter todos due before this date"),
107
+ due_after: Optional[datetime] = Query(None, description="Filter todos due after this date"),
108
+ tag: Optional[str] = Query(None, description="Filter by specific tag"),
109
+ sort_by: Optional[str] = Query("created_at", pattern="^(created_at|due_date|priority|title)$", description="Sort field"),
110
+ sort_order: Optional[str] = Query("desc", pattern="^(asc|desc)$", description="Sort order"),
111
+ ):
112
+ """Get all todos with optional search, filtering, and sorting."""
113
+ service = TodoService(db, user_id=current_user.id)
114
+ todos = service.get_all(
115
+ search=search,
116
+ status=status,
117
+ priority=priority,
118
+ due_before=due_before,
119
+ due_after=due_after,
120
+ tag=tag,
121
+ sort_by=sort_by,
122
+ sort_order=sort_order
123
+ )
124
+
125
+ # Convert to response schema with overdue calculation
126
+ todos_response = [_todo_to_response(todo) for todo in todos]
127
+
128
+ return {
129
+ "todos": todos_response,
130
+ "count": len(todos_response),
131
+ "has_more": False # Pagination not implemented yet
132
+ }
133
+
134
+
135
+ @app.post("/api/v1/todos", response_model=TodoResponse, status_code=201)
136
+ def create_todo(
137
+ todo_data: TodoCreate,
138
+ db: Session = Depends(get_db),
139
+ current_user: User = Depends(get_current_user)
140
+ ):
141
+ """Create a new todo with optional extended fields."""
142
+ try:
143
+ service = TodoService(db, user_id=current_user.id)
144
+ todo = service.create(todo_data)
145
+ return _todo_to_response(todo)
146
+ except ValueError as e:
147
+ raise HTTPException(status_code=400, detail=str(e))
148
+
149
+
150
+ @app.get("/api/v1/todos/{todo_id}", response_model=TodoResponse)
151
+ def get_todo(
152
+ todo_id: int,
153
+ db: Session = Depends(get_db),
154
+ current_user: User = Depends(get_current_user)
155
+ ):
156
+ """Get a single todo by ID."""
157
+ service = TodoService(db, user_id=current_user.id)
158
+ todo = service.get_by_id(todo_id)
159
+ if not todo:
160
+ raise HTTPException(status_code=404, detail="Todo not found")
161
+ return _todo_to_response(todo)
162
+
163
+
164
+ @app.put("/api/v1/todos/{todo_id}", response_model=TodoResponse)
165
+ def update_todo(
166
+ todo_id: int,
167
+ todo_data: TodoUpdate,
168
+ db: Session = Depends(get_db),
169
+ current_user: User = Depends(get_current_user)
170
+ ):
171
+ """Update a todo with optional extended fields."""
172
+ try:
173
+ service = TodoService(db, user_id=current_user.id)
174
+ todo = service.update(todo_id, todo_data)
175
+ if not todo:
176
+ raise HTTPException(status_code=404, detail="Todo not found")
177
+ return _todo_to_response(todo)
178
+ except ValueError as e:
179
+ raise HTTPException(status_code=400, detail=str(e))
180
+
181
+
182
+ @app.delete("/api/v1/todos/{todo_id}")
183
+ def delete_todo(
184
+ todo_id: int,
185
+ db: Session = Depends(get_db),
186
+ current_user: User = Depends(get_current_user)
187
+ ):
188
+ """Delete a todo."""
189
+ service = TodoService(db, user_id=current_user.id)
190
+ if not service.delete(todo_id):
191
+ raise HTTPException(status_code=404, detail="Todo not found")
192
+ return {"message": "Todo deleted successfully"}
193
+
194
+
195
+ @app.patch("/api/v1/todos/{todo_id}/complete")
196
+ def mark_todo_complete(
197
+ todo_id: int,
198
+ db: Session = Depends(get_db),
199
+ current_user: User = Depends(get_current_user)
200
+ ):
201
+ """
202
+ Mark a todo as complete.
203
+ If the todo has a recurrence pattern, automatically creates the next instance.
204
+ Returns both the completed task and the next task (if applicable).
205
+ """
206
+ service = TodoService(db, user_id=current_user.id)
207
+ result = service.mark_complete(todo_id)
208
+ if result is None:
209
+ raise HTTPException(status_code=404, detail="Todo not found")
210
+
211
+ completed_task, next_task = result
212
+
213
+ response = {
214
+ "completed_task": _todo_to_response(completed_task)
215
+ }
216
+
217
+ if next_task:
218
+ response["next_task"] = _todo_to_response(next_task)
219
+
220
+ return response
221
+
222
+
223
+ @app.get("/api/v1/todos/due-soon")
224
+ def get_todos_due_soon(
225
+ hours: int = Query(1, ge=1, le=24, description="Number of hours to look ahead"),
226
+ db: Session = Depends(get_db),
227
+ current_user: User = Depends(get_current_user)
228
+ ):
229
+ """Get todos due within the specified hours for reminder notifications."""
230
+ service = TodoService(db, user_id=current_user.id)
231
+ todos = service.get_todos_due_soon(hours=hours)
232
+ todos_response = [_todo_to_response(todo) for todo in todos]
233
+ return {"todos": todos_response, "count": len(todos_response)}