Spaces:
Sleeping
Sleeping
bilalsxadad1231231
commited on
Commit
·
bf78a48
0
Parent(s):
Initial commit: AI Todo App Backend for Hugging Face Spaces
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .dockerignore +75 -0
- .env +16 -0
- DEPLOYMENT.md +117 -0
- Dockerfile +40 -0
- README.md +110 -0
- alembic.ini +41 -0
- alembic/env.py +59 -0
- app/__init__.py +1 -0
- app/__pycache__/__init__.cpython-312.pyc +0 -0
- app/__pycache__/main.cpython-312.pyc +0 -0
- app/api/__init__.py +1 -0
- app/api/__pycache__/__init__.cpython-312.pyc +0 -0
- app/api/v1/__init__.py +1 -0
- app/api/v1/__pycache__/__init__.cpython-312.pyc +0 -0
- app/api/v1/__pycache__/api.cpython-312.pyc +0 -0
- app/api/v1/api.py +8 -0
- app/api/v1/endpoints/__init__.py +1 -0
- app/api/v1/endpoints/__pycache__/__init__.cpython-312.pyc +0 -0
- app/api/v1/endpoints/__pycache__/subtasks.cpython-312.pyc +0 -0
- app/api/v1/endpoints/__pycache__/todos.cpython-312.pyc +0 -0
- app/api/v1/endpoints/__pycache__/translation.cpython-312.pyc +0 -0
- app/api/v1/endpoints/subtasks.py +62 -0
- app/api/v1/endpoints/todos.py +75 -0
- app/api/v1/endpoints/translation.py +67 -0
- app/core/__init__.py +1 -0
- app/core/__pycache__/__init__.cpython-312.pyc +0 -0
- app/core/__pycache__/config.cpython-312.pyc +0 -0
- app/core/__pycache__/database.cpython-312.pyc +0 -0
- app/core/config.py +28 -0
- app/core/database.py +27 -0
- app/main.py +39 -0
- app/models/__init__.py +1 -0
- app/models/__pycache__/__init__.cpython-312.pyc +0 -0
- app/models/__pycache__/subtask.cpython-312.pyc +0 -0
- app/models/__pycache__/todo.cpython-312.pyc +0 -0
- app/models/subtask.py +31 -0
- app/models/todo.py +18 -0
- app/schemas/__init__.py +1 -0
- app/schemas/__pycache__/__init__.cpython-312.pyc +0 -0
- app/schemas/__pycache__/subtask.cpython-312.pyc +0 -0
- app/schemas/__pycache__/todo.cpython-312.pyc +0 -0
- app/schemas/__pycache__/translation.cpython-312.pyc +0 -0
- app/schemas/subtask.py +31 -0
- app/schemas/todo.py +44 -0
- app/schemas/translation.py +23 -0
- app/services/__init__.py +1 -0
- app/services/__pycache__/__init__.cpython-312.pyc +0 -0
- app/services/__pycache__/ai_service.cpython-312.pyc +0 -0
- app/services/__pycache__/todo_service.cpython-312.pyc +0 -0
- app/services/__pycache__/translation_service.cpython-312.pyc +0 -0
.dockerignore
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Python
|
2 |
+
__pycache__/
|
3 |
+
*.py[cod]
|
4 |
+
*$py.class
|
5 |
+
*.so
|
6 |
+
.Python
|
7 |
+
build/
|
8 |
+
develop-eggs/
|
9 |
+
dist/
|
10 |
+
downloads/
|
11 |
+
eggs/
|
12 |
+
.eggs/
|
13 |
+
lib/
|
14 |
+
lib64/
|
15 |
+
parts/
|
16 |
+
sdist/
|
17 |
+
var/
|
18 |
+
wheels/
|
19 |
+
*.egg-info/
|
20 |
+
.installed.cfg
|
21 |
+
*.egg
|
22 |
+
MANIFEST
|
23 |
+
|
24 |
+
# Virtual environments
|
25 |
+
env/
|
26 |
+
venv/
|
27 |
+
ENV/
|
28 |
+
env.bak/
|
29 |
+
venv.bak/
|
30 |
+
|
31 |
+
# IDE
|
32 |
+
.vscode/
|
33 |
+
.idea/
|
34 |
+
*.swp
|
35 |
+
*.swo
|
36 |
+
*~
|
37 |
+
|
38 |
+
# OS
|
39 |
+
.DS_Store
|
40 |
+
.DS_Store?
|
41 |
+
._*
|
42 |
+
.Spotlight-V100
|
43 |
+
.Trashes
|
44 |
+
ehthumbs.db
|
45 |
+
Thumbs.db
|
46 |
+
|
47 |
+
# Git
|
48 |
+
.git/
|
49 |
+
.gitignore
|
50 |
+
|
51 |
+
# Database
|
52 |
+
*.db
|
53 |
+
*.sqlite
|
54 |
+
*.sqlite3
|
55 |
+
|
56 |
+
# Logs
|
57 |
+
*.log
|
58 |
+
|
59 |
+
# Environment files
|
60 |
+
.env
|
61 |
+
.env.local
|
62 |
+
.env.production
|
63 |
+
|
64 |
+
# Test files
|
65 |
+
.pytest_cache/
|
66 |
+
.coverage
|
67 |
+
htmlcov/
|
68 |
+
|
69 |
+
# Documentation
|
70 |
+
README.md
|
71 |
+
*.md
|
72 |
+
|
73 |
+
# Docker
|
74 |
+
Dockerfile
|
75 |
+
.dockerignore
|
.env
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Database
|
2 |
+
DATABASE_URL=sqlite:///./todo_app.db
|
3 |
+
# For production use PostgreSQL:
|
4 |
+
# DATABASE_URL=postgresql://user:password@localhost/todo_app
|
5 |
+
|
6 |
+
# Groq Configuration
|
7 |
+
GROQ_API_KEY=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
|
8 |
+
GROQ_MODEL_NAME=llama3-8b-8192
|
9 |
+
|
10 |
+
# API Configuration
|
11 |
+
API_V1_STR=/api/v1
|
12 |
+
PROJECT_NAME=AI Todo App
|
13 |
+
DEBUG=True
|
14 |
+
|
15 |
+
# CORS
|
16 |
+
BACKEND_CORS_ORIGINS=["http://localhost:3000", "https://yourdomain.vercel.app"]
|
DEPLOYMENT.md
ADDED
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Deployment Guide
|
2 |
+
|
3 |
+
## Hugging Face Spaces Deployment
|
4 |
+
|
5 |
+
### 1. Create a Hugging Face Space
|
6 |
+
|
7 |
+
1. Go to [Hugging Face Spaces](https://huggingface.co/spaces)
|
8 |
+
2. Click "Create new Space"
|
9 |
+
3. Choose "Docker" as the SDK
|
10 |
+
4. Set the Space name and visibility
|
11 |
+
5. Click "Create Space"
|
12 |
+
|
13 |
+
### 2. Configure Environment Variables
|
14 |
+
|
15 |
+
In your Hugging Face Space settings, add these environment variables:
|
16 |
+
|
17 |
+
```
|
18 |
+
DATABASE_URL=sqlite:///./todo_app.db
|
19 |
+
GROQ_API_KEY=your_actual_groq_api_key
|
20 |
+
GROQ_MODEL_NAME=llama3-8b-8192
|
21 |
+
API_V1_STR=/api/v1
|
22 |
+
PROJECT_NAME=AI Todo App
|
23 |
+
DEBUG=False
|
24 |
+
BACKEND_CORS_ORIGINS=["https://your-frontend-domain.com"]
|
25 |
+
```
|
26 |
+
|
27 |
+
### 3. Update Dockerfile for HF Spaces
|
28 |
+
|
29 |
+
The Dockerfile is already optimized for Hugging Face Spaces. Make sure to:
|
30 |
+
|
31 |
+
1. Set `DEBUG=False` in production
|
32 |
+
2. Configure proper CORS origins
|
33 |
+
3. Set your actual Groq API key
|
34 |
+
|
35 |
+
### 4. Build and Deploy
|
36 |
+
|
37 |
+
The Space will automatically build and deploy when you push to the repository.
|
38 |
+
|
39 |
+
## Local Development
|
40 |
+
|
41 |
+
### Using Docker Compose
|
42 |
+
|
43 |
+
```bash
|
44 |
+
# Build and run
|
45 |
+
docker-compose up --build
|
46 |
+
|
47 |
+
# Run in background
|
48 |
+
docker-compose up -d
|
49 |
+
|
50 |
+
# View logs
|
51 |
+
docker-compose logs -f
|
52 |
+
|
53 |
+
# Stop
|
54 |
+
docker-compose down
|
55 |
+
```
|
56 |
+
|
57 |
+
### Using Docker directly
|
58 |
+
|
59 |
+
```bash
|
60 |
+
# Build image
|
61 |
+
docker build -t todo-ai-app .
|
62 |
+
|
63 |
+
# Run container
|
64 |
+
docker run -p 8000:8000 -e GROQ_API_KEY=your_key todo-ai-app
|
65 |
+
```
|
66 |
+
|
67 |
+
## Environment Variables
|
68 |
+
|
69 |
+
| Variable | Description | Required | Default |
|
70 |
+
|----------|-------------|----------|---------|
|
71 |
+
| `DATABASE_URL` | Database connection string | No | `sqlite:///./todo_app.db` |
|
72 |
+
| `GROQ_API_KEY` | Groq API key for AI features | Yes | - |
|
73 |
+
| `GROQ_MODEL_NAME` | Groq model to use | No | `llama3-8b-8192` |
|
74 |
+
| `API_V1_STR` | API version prefix | No | `/api/v1` |
|
75 |
+
| `PROJECT_NAME` | Application name | No | `AI Todo App` |
|
76 |
+
| `DEBUG` | Debug mode | No | `True` |
|
77 |
+
| `BACKEND_CORS_ORIGINS` | CORS allowed origins | No | `["http://localhost:3000"]` |
|
78 |
+
|
79 |
+
## Health Check
|
80 |
+
|
81 |
+
The application includes a health check endpoint at `/health` that returns:
|
82 |
+
|
83 |
+
```json
|
84 |
+
{
|
85 |
+
"status": "healthy"
|
86 |
+
}
|
87 |
+
```
|
88 |
+
|
89 |
+
## API Documentation
|
90 |
+
|
91 |
+
Once deployed, you can access:
|
92 |
+
- API Documentation: `https://your-space.hf.space/docs`
|
93 |
+
- Health Check: `https://your-space.hf.space/health`
|
94 |
+
- Root endpoint: `https://your-space.hf.space/`
|
95 |
+
|
96 |
+
**Note**: The application runs on port 7860, which is the standard port for Hugging Face Spaces.
|
97 |
+
|
98 |
+
## Troubleshooting
|
99 |
+
|
100 |
+
### Common Issues
|
101 |
+
|
102 |
+
1. **Port binding**: Make sure port 8000 is exposed
|
103 |
+
2. **Environment variables**: Ensure all required env vars are set
|
104 |
+
3. **Database**: SQLite database will be created automatically
|
105 |
+
4. **CORS**: Update CORS origins for your frontend domain
|
106 |
+
|
107 |
+
### Logs
|
108 |
+
|
109 |
+
Check application logs for debugging:
|
110 |
+
|
111 |
+
```bash
|
112 |
+
# Docker Compose
|
113 |
+
docker-compose logs app
|
114 |
+
|
115 |
+
# Docker
|
116 |
+
docker logs <container_id>
|
117 |
+
```
|
Dockerfile
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Use Python 3.11 slim image for smaller size
|
2 |
+
FROM python:3.11-slim
|
3 |
+
|
4 |
+
# Set working directory
|
5 |
+
WORKDIR /app
|
6 |
+
|
7 |
+
# Set environment variables
|
8 |
+
ENV PYTHONDONTWRITEBYTECODE=1 \
|
9 |
+
PYTHONUNBUFFERED=1 \
|
10 |
+
PYTHONPATH=/app
|
11 |
+
|
12 |
+
# Install system dependencies
|
13 |
+
RUN apt-get update && apt-get install -y \
|
14 |
+
gcc \
|
15 |
+
&& rm -rf /var/lib/apt/lists/*
|
16 |
+
|
17 |
+
# Copy requirements first for better caching
|
18 |
+
COPY requirements.txt .
|
19 |
+
|
20 |
+
# Install Python dependencies
|
21 |
+
RUN pip install --no-cache-dir --upgrade pip && \
|
22 |
+
pip install --no-cache-dir -r requirements.txt
|
23 |
+
|
24 |
+
# Copy application code
|
25 |
+
COPY . .
|
26 |
+
|
27 |
+
# Create a non-root user for security
|
28 |
+
RUN useradd --create-home --shell /bin/bash app && \
|
29 |
+
chown -R app:app /app
|
30 |
+
USER app
|
31 |
+
|
32 |
+
# Expose port
|
33 |
+
EXPOSE 7860
|
34 |
+
|
35 |
+
# Health check
|
36 |
+
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
|
37 |
+
CMD python -c "import requests; requests.get('http://localhost:7860/health')" || exit 1
|
38 |
+
|
39 |
+
# Run the application
|
40 |
+
CMD ["python", "run.py"]
|
README.md
ADDED
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: AI Todo App Backend
|
3 |
+
emoji: 🤖
|
4 |
+
colorFrom: blue
|
5 |
+
colorTo: purple
|
6 |
+
sdk: docker
|
7 |
+
pinned: false
|
8 |
+
license: mit
|
9 |
+
---
|
10 |
+
|
11 |
+
# AI Todo App Backend
|
12 |
+
|
13 |
+
A FastAPI-based backend application for an AI-powered todo management system with Groq AI integration.
|
14 |
+
|
15 |
+
## Features
|
16 |
+
|
17 |
+
- **FastAPI REST API** with automatic documentation
|
18 |
+
- **SQLite Database** with SQLAlchemy ORM
|
19 |
+
- **Groq AI Integration** for intelligent todo management
|
20 |
+
- **CORS Support** for frontend integration
|
21 |
+
- **Health Check Endpoints** for monitoring
|
22 |
+
- **Docker Containerization** for easy deployment
|
23 |
+
|
24 |
+
## API Endpoints
|
25 |
+
|
26 |
+
- **GET /** - Root endpoint with app info
|
27 |
+
- **GET /health** - Health check endpoint
|
28 |
+
- **GET /docs** - Interactive API documentation (Swagger UI)
|
29 |
+
- **GET /api/v1/** - API v1 endpoints
|
30 |
+
|
31 |
+
## Environment Variables
|
32 |
+
|
33 |
+
| Variable | Description | Required | Default |
|
34 |
+
|----------|-------------|----------|---------|
|
35 |
+
| `DATABASE_URL` | Database connection string | No | `sqlite:///./todo_app.db` |
|
36 |
+
| `GROQ_API_KEY` | Groq API key for AI features | Yes | - |
|
37 |
+
| `GROQ_MODEL_NAME` | Groq model to use | No | `llama3-8b-8192` |
|
38 |
+
| `API_V1_STR` | API version prefix | No | `/api/v1` |
|
39 |
+
| `PROJECT_NAME` | Application name | No | `AI Todo App` |
|
40 |
+
| `DEBUG` | Debug mode | No | `True` |
|
41 |
+
| `BACKEND_CORS_ORIGINS` | CORS allowed origins | No | `["http://localhost:3000"]` |
|
42 |
+
|
43 |
+
## Local Development
|
44 |
+
|
45 |
+
### Using Docker Compose
|
46 |
+
|
47 |
+
```bash
|
48 |
+
# Build and run
|
49 |
+
docker-compose up --build
|
50 |
+
|
51 |
+
# Run in background
|
52 |
+
docker-compose up -d
|
53 |
+
|
54 |
+
# View logs
|
55 |
+
docker-compose logs -f
|
56 |
+
|
57 |
+
# Stop
|
58 |
+
docker-compose down
|
59 |
+
```
|
60 |
+
|
61 |
+
### Using Docker directly
|
62 |
+
|
63 |
+
```bash
|
64 |
+
# Build image
|
65 |
+
docker build -t todo-ai-app .
|
66 |
+
|
67 |
+
# Run container
|
68 |
+
docker run -p 7860:7860 -e GROQ_API_KEY=your_key todo-ai-app
|
69 |
+
```
|
70 |
+
|
71 |
+
## Deployment
|
72 |
+
|
73 |
+
This application is deployed on Hugging Face Spaces and runs on port 7860.
|
74 |
+
|
75 |
+
### Access Points
|
76 |
+
|
77 |
+
- **Application**: https://huggingface.co/spaces/Muhammadbilal10101/todo-ai
|
78 |
+
- **API Documentation**: https://huggingface.co/spaces/Muhammadbilal10101/todo-ai/docs
|
79 |
+
- **Health Check**: https://huggingface.co/spaces/Muhammadbilal10101/todo-ai/health
|
80 |
+
|
81 |
+
## Technology Stack
|
82 |
+
|
83 |
+
- **FastAPI** - Modern, fast web framework for building APIs
|
84 |
+
- **SQLAlchemy** - SQL toolkit and ORM
|
85 |
+
- **Alembic** - Database migration tool
|
86 |
+
- **Pydantic** - Data validation using Python type annotations
|
87 |
+
- **Groq** - Fast AI inference API
|
88 |
+
- **Docker** - Containerization platform
|
89 |
+
- **SQLite** - Lightweight database
|
90 |
+
|
91 |
+
## Project Structure
|
92 |
+
|
93 |
+
```
|
94 |
+
backend/
|
95 |
+
├── app/
|
96 |
+
│ ├── api/ # API routes
|
97 |
+
│ ├── core/ # Core configuration
|
98 |
+
│ ├── models/ # Database models
|
99 |
+
│ ├── schemas/ # Pydantic schemas
|
100 |
+
│ └── services/ # Business logic
|
101 |
+
├── alembic/ # Database migrations
|
102 |
+
├── Dockerfile # Docker configuration
|
103 |
+
├── docker-compose.yml # Local development
|
104 |
+
├── requirements.txt # Python dependencies
|
105 |
+
└── run.py # Application entry point
|
106 |
+
```
|
107 |
+
|
108 |
+
## License
|
109 |
+
|
110 |
+
MIT License - see LICENSE file for details.
|
alembic.ini
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[alembic]
|
2 |
+
script_location = alembic
|
3 |
+
prepend_sys_path = .
|
4 |
+
version_path_separator = os
|
5 |
+
sqlalchemy.url = sqlite:///./todo_app.db
|
6 |
+
|
7 |
+
[post_write_hooks]
|
8 |
+
|
9 |
+
[loggers]
|
10 |
+
keys = root,sqlalchemy,alembic
|
11 |
+
|
12 |
+
[handlers]
|
13 |
+
keys = console
|
14 |
+
|
15 |
+
[formatters]
|
16 |
+
keys = generic
|
17 |
+
|
18 |
+
[logger_root]
|
19 |
+
level = WARN
|
20 |
+
handlers = console
|
21 |
+
qualname =
|
22 |
+
|
23 |
+
[logger_sqlalchemy]
|
24 |
+
level = WARN
|
25 |
+
handlers =
|
26 |
+
qualname = sqlalchemy.engine
|
27 |
+
|
28 |
+
[logger_alembic]
|
29 |
+
level = INFO
|
30 |
+
handlers =
|
31 |
+
qualname = alembic
|
32 |
+
|
33 |
+
[handler_console]
|
34 |
+
class = StreamHandler
|
35 |
+
args = (sys.stderr,)
|
36 |
+
level = NOTSET
|
37 |
+
formatter = generic
|
38 |
+
|
39 |
+
[formatter_generic]
|
40 |
+
format = %(levelname)-5.5s [%(name)s] %(message)s
|
41 |
+
datefmt = %H:%M:%S
|
alembic/env.py
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from logging.config import fileConfig
|
2 |
+
from sqlalchemy import engine_from_config, pool
|
3 |
+
from alembic import context
|
4 |
+
import os
|
5 |
+
import sys
|
6 |
+
|
7 |
+
# Add your project directory to Python path
|
8 |
+
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
|
9 |
+
|
10 |
+
from app.core.config import settings
|
11 |
+
from app.core.database import Base
|
12 |
+
from app.models import todo, subtask # Import all models
|
13 |
+
|
14 |
+
# Alembic Config object
|
15 |
+
config = context.config
|
16 |
+
|
17 |
+
# Set SQLAlchemy URL from settings
|
18 |
+
config.set_main_option("sqlalchemy.url", settings.DATABASE_URL)
|
19 |
+
|
20 |
+
# Interpret the config file for Python logging
|
21 |
+
if config.config_file_name is not None:
|
22 |
+
fileConfig(config.config_file_name)
|
23 |
+
|
24 |
+
# Target metadata for 'autogenerate' support
|
25 |
+
target_metadata = Base.metadata
|
26 |
+
|
27 |
+
def run_migrations_offline() -> None:
|
28 |
+
"""Run migrations in 'offline' mode."""
|
29 |
+
url = config.get_main_option("sqlalchemy.url")
|
30 |
+
context.configure(
|
31 |
+
url=url,
|
32 |
+
target_metadata=target_metadata,
|
33 |
+
literal_binds=True,
|
34 |
+
dialect_opts={"paramstyle": "named"},
|
35 |
+
)
|
36 |
+
|
37 |
+
with context.begin_transaction():
|
38 |
+
context.run_migrations()
|
39 |
+
|
40 |
+
def run_migrations_online() -> None:
|
41 |
+
"""Run migrations in 'online' mode."""
|
42 |
+
connectable = engine_from_config(
|
43 |
+
config.get_section(config.config_ini_section, {}),
|
44 |
+
prefix="sqlalchemy.",
|
45 |
+
poolclass=pool.NullPool,
|
46 |
+
)
|
47 |
+
|
48 |
+
with connectable.connect() as connection:
|
49 |
+
context.configure(
|
50 |
+
connection=connection, target_metadata=target_metadata
|
51 |
+
)
|
52 |
+
|
53 |
+
with context.begin_transaction():
|
54 |
+
context.run_migrations()
|
55 |
+
|
56 |
+
if context.is_offline_mode():
|
57 |
+
run_migrations_offline()
|
58 |
+
else:
|
59 |
+
run_migrations_online()
|
app/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
# AI Todo App Backend
|
app/__pycache__/__init__.cpython-312.pyc
ADDED
Binary file (174 Bytes). View file
|
|
app/__pycache__/main.cpython-312.pyc
ADDED
Binary file (1.75 kB). View file
|
|
app/api/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
# API endpoints
|
app/api/__pycache__/__init__.cpython-312.pyc
ADDED
Binary file (178 Bytes). View file
|
|
app/api/v1/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
# API v1 endpoints
|
app/api/v1/__pycache__/__init__.cpython-312.pyc
ADDED
Binary file (181 Bytes). View file
|
|
app/api/v1/__pycache__/api.cpython-312.pyc
ADDED
Binary file (676 Bytes). View file
|
|
app/api/v1/api.py
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter
|
2 |
+
from .endpoints import todos, subtasks, translation
|
3 |
+
|
4 |
+
api_router = APIRouter()
|
5 |
+
|
6 |
+
api_router.include_router(todos.router, prefix="/todos", tags=["todos"])
|
7 |
+
api_router.include_router(subtasks.router, prefix="", tags=["subtasks"])
|
8 |
+
api_router.include_router(translation.router, prefix="/todos", tags=["translation"])
|
app/api/v1/endpoints/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
# API endpoint modules
|
app/api/v1/endpoints/__pycache__/__init__.cpython-312.pyc
ADDED
Binary file (191 Bytes). View file
|
|
app/api/v1/endpoints/__pycache__/subtasks.cpython-312.pyc
ADDED
Binary file (2.92 kB). View file
|
|
app/api/v1/endpoints/__pycache__/todos.cpython-312.pyc
ADDED
Binary file (3.7 kB). View file
|
|
app/api/v1/endpoints/__pycache__/translation.cpython-312.pyc
ADDED
Binary file (3.16 kB). View file
|
|
app/api/v1/endpoints/subtasks.py
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter, Depends, HTTPException, status
|
2 |
+
from sqlalchemy.orm import Session
|
3 |
+
from typing import List
|
4 |
+
from ....core.database import get_db
|
5 |
+
from ....schemas.subtask import Subtask, SubtaskGenerateRequest, SubtaskUpdate
|
6 |
+
from ....services.todo_service import TodoService
|
7 |
+
|
8 |
+
router = APIRouter()
|
9 |
+
|
10 |
+
@router.post("/todos/{todo_id}/generate", response_model=List[Subtask])
|
11 |
+
async def generate_subtasks(
|
12 |
+
todo_id: int,
|
13 |
+
request: SubtaskGenerateRequest,
|
14 |
+
db: Session = Depends(get_db)
|
15 |
+
):
|
16 |
+
"""Generate AI subtasks for a todo"""
|
17 |
+
todo_service = TodoService(db)
|
18 |
+
|
19 |
+
try:
|
20 |
+
subtasks = await todo_service.generate_subtasks(
|
21 |
+
todo_id=todo_id,
|
22 |
+
max_subtasks=request.max_subtasks
|
23 |
+
)
|
24 |
+
return subtasks
|
25 |
+
except ValueError as e:
|
26 |
+
raise HTTPException(
|
27 |
+
status_code=status.HTTP_404_NOT_FOUND,
|
28 |
+
detail=str(e)
|
29 |
+
)
|
30 |
+
except Exception as e:
|
31 |
+
raise HTTPException(
|
32 |
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
33 |
+
detail=f"Failed to generate subtasks: {str(e)}"
|
34 |
+
)
|
35 |
+
|
36 |
+
@router.get("/todos/{todo_id}/subtasks", response_model=List[Subtask])
|
37 |
+
def get_todo_subtasks(todo_id: int, db: Session = Depends(get_db)):
|
38 |
+
"""Get all subtasks for a todo"""
|
39 |
+
todo_service = TodoService(db)
|
40 |
+
todo = todo_service.get_todo(todo_id)
|
41 |
+
if not todo:
|
42 |
+
raise HTTPException(
|
43 |
+
status_code=status.HTTP_404_NOT_FOUND,
|
44 |
+
detail="Todo not found"
|
45 |
+
)
|
46 |
+
return todo.subtasks
|
47 |
+
|
48 |
+
@router.put("/subtasks/{subtask_id}", response_model=Subtask)
|
49 |
+
def update_subtask(
|
50 |
+
subtask_id: int,
|
51 |
+
subtask_update: SubtaskUpdate,
|
52 |
+
db: Session = Depends(get_db)
|
53 |
+
):
|
54 |
+
"""Update a subtask"""
|
55 |
+
todo_service = TodoService(db)
|
56 |
+
updated_subtask = todo_service.update_subtask(subtask_id, subtask_update)
|
57 |
+
if not updated_subtask:
|
58 |
+
raise HTTPException(
|
59 |
+
status_code=status.HTTP_404_NOT_FOUND,
|
60 |
+
detail="Subtask not found"
|
61 |
+
)
|
62 |
+
return updated_subtask
|
app/api/v1/endpoints/todos.py
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter, Depends, HTTPException, status
|
2 |
+
from sqlalchemy.orm import Session
|
3 |
+
from typing import List
|
4 |
+
from ....core.database import get_db
|
5 |
+
from ....schemas.todo import Todo, TodoCreate, TodoUpdate, TodoWithRelations
|
6 |
+
from ....services.todo_service import TodoService
|
7 |
+
|
8 |
+
router = APIRouter()
|
9 |
+
|
10 |
+
@router.get("/", response_model=List[TodoWithRelations])
|
11 |
+
def get_todos(
|
12 |
+
skip: int = 0,
|
13 |
+
limit: int = 100,
|
14 |
+
db: Session = Depends(get_db)
|
15 |
+
):
|
16 |
+
"""Get all todos with subtasks and translations"""
|
17 |
+
todo_service = TodoService(db)
|
18 |
+
return todo_service.get_todos(skip=skip, limit=limit)
|
19 |
+
|
20 |
+
@router.get("/{todo_id}", response_model=TodoWithRelations)
|
21 |
+
def get_todo(todo_id: int, db: Session = Depends(get_db)):
|
22 |
+
"""Get a specific todo with subtasks and translations"""
|
23 |
+
todo_service = TodoService(db)
|
24 |
+
todo = todo_service.get_todo(todo_id)
|
25 |
+
if not todo:
|
26 |
+
raise HTTPException(
|
27 |
+
status_code=status.HTTP_404_NOT_FOUND,
|
28 |
+
detail="Todo not found"
|
29 |
+
)
|
30 |
+
return todo
|
31 |
+
|
32 |
+
@router.post("/", response_model=TodoWithRelations, status_code=status.HTTP_201_CREATED)
|
33 |
+
def create_todo(todo: TodoCreate, db: Session = Depends(get_db)):
|
34 |
+
"""Create a new todo"""
|
35 |
+
todo_service = TodoService(db)
|
36 |
+
return todo_service.create_todo(todo)
|
37 |
+
|
38 |
+
@router.put("/{todo_id}", response_model=TodoWithRelations)
|
39 |
+
def update_todo(
|
40 |
+
todo_id: int,
|
41 |
+
todo_update: TodoUpdate,
|
42 |
+
db: Session = Depends(get_db)
|
43 |
+
):
|
44 |
+
"""Update an existing todo"""
|
45 |
+
todo_service = TodoService(db)
|
46 |
+
updated_todo = todo_service.update_todo(todo_id, todo_update)
|
47 |
+
if not updated_todo:
|
48 |
+
raise HTTPException(
|
49 |
+
status_code=status.HTTP_404_NOT_FOUND,
|
50 |
+
detail="Todo not found"
|
51 |
+
)
|
52 |
+
return updated_todo
|
53 |
+
|
54 |
+
@router.delete("/{todo_id}", status_code=status.HTTP_204_NO_CONTENT)
|
55 |
+
def delete_todo(todo_id: int, db: Session = Depends(get_db)):
|
56 |
+
"""Delete a todo"""
|
57 |
+
todo_service = TodoService(db)
|
58 |
+
success = todo_service.delete_todo(todo_id)
|
59 |
+
if not success:
|
60 |
+
raise HTTPException(
|
61 |
+
status_code=status.HTTP_404_NOT_FOUND,
|
62 |
+
detail="Todo not found"
|
63 |
+
)
|
64 |
+
|
65 |
+
@router.patch("/{todo_id}/toggle", response_model=TodoWithRelations)
|
66 |
+
def toggle_todo_completion(todo_id: int, db: Session = Depends(get_db)):
|
67 |
+
"""Toggle todo completion status"""
|
68 |
+
todo_service = TodoService(db)
|
69 |
+
updated_todo = todo_service.toggle_todo_completion(todo_id)
|
70 |
+
if not updated_todo:
|
71 |
+
raise HTTPException(
|
72 |
+
status_code=status.HTTP_404_NOT_FOUND,
|
73 |
+
detail="Todo not found"
|
74 |
+
)
|
75 |
+
return updated_todo
|
app/api/v1/endpoints/translation.py
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter, Depends, HTTPException, status
|
2 |
+
from sqlalchemy.orm import Session
|
3 |
+
from typing import List
|
4 |
+
from ....core.database import get_db
|
5 |
+
from ....schemas.translation import Translation, TranslationRequest, TodoTranslationRequest
|
6 |
+
from ....services.todo_service import TodoService
|
7 |
+
|
8 |
+
router = APIRouter()
|
9 |
+
|
10 |
+
@router.post("/{todo_id}/translate", response_model=Translation)
|
11 |
+
async def translate_todo(
|
12 |
+
todo_id: int,
|
13 |
+
request: TodoTranslationRequest,
|
14 |
+
db: Session = Depends(get_db)
|
15 |
+
):
|
16 |
+
"""Translate a todo to target language"""
|
17 |
+
todo_service = TodoService(db)
|
18 |
+
|
19 |
+
try:
|
20 |
+
translation = await todo_service.translate_todo(
|
21 |
+
todo_id=todo_id,
|
22 |
+
target_language=request.target_language
|
23 |
+
)
|
24 |
+
if not translation:
|
25 |
+
raise HTTPException(
|
26 |
+
status_code=status.HTTP_404_NOT_FOUND,
|
27 |
+
detail="Todo not found"
|
28 |
+
)
|
29 |
+
return translation
|
30 |
+
except Exception as e:
|
31 |
+
raise HTTPException(
|
32 |
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
33 |
+
detail=str(e)
|
34 |
+
)
|
35 |
+
|
36 |
+
@router.get("/{todo_id}/translations", response_model=List[Translation])
|
37 |
+
def get_todo_translations(todo_id: int, db: Session = Depends(get_db)):
|
38 |
+
"""Get all translations for a todo"""
|
39 |
+
todo_service = TodoService(db)
|
40 |
+
todo = todo_service.get_todo(todo_id)
|
41 |
+
if not todo:
|
42 |
+
raise HTTPException(
|
43 |
+
status_code=status.HTTP_404_NOT_FOUND,
|
44 |
+
detail="Todo not found"
|
45 |
+
)
|
46 |
+
return todo_service.get_todo_translations(todo_id)
|
47 |
+
|
48 |
+
@router.post("/translate", response_model=dict)
|
49 |
+
async def translate_text(request: TranslationRequest):
|
50 |
+
"""Translate any text to target language"""
|
51 |
+
from ....services.translation_service import translation_service
|
52 |
+
|
53 |
+
try:
|
54 |
+
translated_text = await translation_service.translate_text(
|
55 |
+
text=request.text,
|
56 |
+
target_language=request.target_language
|
57 |
+
)
|
58 |
+
return {
|
59 |
+
"original_text": request.text,
|
60 |
+
"translated_text": translated_text,
|
61 |
+
"target_language": request.target_language
|
62 |
+
}
|
63 |
+
except Exception as e:
|
64 |
+
raise HTTPException(
|
65 |
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
66 |
+
detail=str(e)
|
67 |
+
)
|
app/core/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
# Core configuration and database setup
|
app/core/__pycache__/__init__.cpython-312.pyc
ADDED
Binary file (179 Bytes). View file
|
|
app/core/__pycache__/config.cpython-312.pyc
ADDED
Binary file (1.17 kB). View file
|
|
app/core/__pycache__/database.cpython-312.pyc
ADDED
Binary file (1.07 kB). View file
|
|
app/core/config.py
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pydantic_settings import BaseSettings
|
2 |
+
from typing import List
|
3 |
+
import os
|
4 |
+
|
5 |
+
class Settings(BaseSettings):
|
6 |
+
# API Settings
|
7 |
+
API_V1_STR: str = "/api/v1"
|
8 |
+
PROJECT_NAME: str = "AI Todo App"
|
9 |
+
DEBUG: bool = True
|
10 |
+
|
11 |
+
# Database
|
12 |
+
DATABASE_URL: str = "sqlite:///./todo_app.db"
|
13 |
+
|
14 |
+
# Groq Configuration
|
15 |
+
GROQ_API_KEY: str
|
16 |
+
GROQ_MODEL_NAME: str = "llama3-8b-8192"
|
17 |
+
|
18 |
+
# CORS
|
19 |
+
BACKEND_CORS_ORIGINS: List[str] = [
|
20 |
+
"http://localhost:3000",
|
21 |
+
"http://127.0.0.1:3000",
|
22 |
+
]
|
23 |
+
|
24 |
+
class Config:
|
25 |
+
env_file = ".env"
|
26 |
+
case_sensitive = True
|
27 |
+
|
28 |
+
settings = Settings()
|
app/core/database.py
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from sqlalchemy import create_engine
|
2 |
+
from sqlalchemy.ext.declarative import declarative_base
|
3 |
+
from sqlalchemy.orm import sessionmaker
|
4 |
+
from .config import settings
|
5 |
+
|
6 |
+
# Create engine
|
7 |
+
if settings.DATABASE_URL.startswith("sqlite"):
|
8 |
+
engine = create_engine(
|
9 |
+
settings.DATABASE_URL,
|
10 |
+
connect_args={"check_same_thread": False}
|
11 |
+
)
|
12 |
+
else:
|
13 |
+
engine = create_engine(settings.DATABASE_URL)
|
14 |
+
|
15 |
+
# Create session
|
16 |
+
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
17 |
+
|
18 |
+
# Base class for models
|
19 |
+
Base = declarative_base()
|
20 |
+
|
21 |
+
# Dependency to get database session
|
22 |
+
def get_db():
|
23 |
+
db = SessionLocal()
|
24 |
+
try:
|
25 |
+
yield db
|
26 |
+
finally:
|
27 |
+
db.close()
|
app/main.py
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI
|
2 |
+
from fastapi.middleware.cors import CORSMiddleware
|
3 |
+
from .core.config import settings
|
4 |
+
from .core.database import engine, Base
|
5 |
+
from .api.v1.api import api_router
|
6 |
+
|
7 |
+
# Create database tables
|
8 |
+
Base.metadata.create_all(bind=engine)
|
9 |
+
|
10 |
+
# Create FastAPI app
|
11 |
+
app = FastAPI(
|
12 |
+
title=settings.PROJECT_NAME,
|
13 |
+
openapi_url=f"{settings.API_V1_STR}/openapi.json",
|
14 |
+
debug=settings.DEBUG
|
15 |
+
)
|
16 |
+
|
17 |
+
# Add CORS middleware
|
18 |
+
app.add_middleware(
|
19 |
+
CORSMiddleware,
|
20 |
+
allow_origins=settings.BACKEND_CORS_ORIGINS,
|
21 |
+
allow_credentials=True,
|
22 |
+
allow_methods=["*"],
|
23 |
+
allow_headers=["*"],
|
24 |
+
)
|
25 |
+
|
26 |
+
# Include API router
|
27 |
+
app.include_router(api_router, prefix=settings.API_V1_STR)
|
28 |
+
|
29 |
+
@app.get("/")
|
30 |
+
async def root():
|
31 |
+
return {"message": "AI Todo App API", "version": "1.0.0"}
|
32 |
+
|
33 |
+
@app.get("/health")
|
34 |
+
async def health_check():
|
35 |
+
return {"status": "healthy"}
|
36 |
+
|
37 |
+
if __name__ == "__main__":
|
38 |
+
import uvicorn
|
39 |
+
uvicorn.run("app.main:app", host="0.0.0.0", port=8000, reload=True)
|
app/models/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
# Database models
|
app/models/__pycache__/__init__.cpython-312.pyc
ADDED
Binary file (181 Bytes). View file
|
|
app/models/__pycache__/subtask.cpython-312.pyc
ADDED
Binary file (2 kB). View file
|
|
app/models/__pycache__/todo.cpython-312.pyc
ADDED
Binary file (1.38 kB). View file
|
|
app/models/subtask.py
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from sqlalchemy import Column, Integer, String, Text, Boolean, DateTime, ForeignKey
|
2 |
+
from sqlalchemy.orm import relationship
|
3 |
+
from sqlalchemy.sql import func
|
4 |
+
from ..core.database import Base
|
5 |
+
|
6 |
+
class Subtask(Base):
|
7 |
+
__tablename__ = "subtasks"
|
8 |
+
|
9 |
+
id = Column(Integer, primary_key=True, index=True)
|
10 |
+
todo_id = Column(Integer, ForeignKey("todos.id"), nullable=False)
|
11 |
+
title = Column(String(255), nullable=False)
|
12 |
+
description = Column(Text, nullable=True)
|
13 |
+
completed = Column(Boolean, default=False, nullable=False)
|
14 |
+
order_index = Column(Integer, default=0)
|
15 |
+
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
16 |
+
|
17 |
+
# Relationships
|
18 |
+
todo = relationship("Todo", back_populates="subtasks")
|
19 |
+
|
20 |
+
class Translation(Base):
|
21 |
+
__tablename__ = "translations"
|
22 |
+
|
23 |
+
id = Column(Integer, primary_key=True, index=True)
|
24 |
+
todo_id = Column(Integer, ForeignKey("todos.id"), nullable=False)
|
25 |
+
language = Column(String(50), nullable=False)
|
26 |
+
translated_title = Column(String(500), nullable=False)
|
27 |
+
translated_description = Column(Text, nullable=True)
|
28 |
+
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
29 |
+
|
30 |
+
# Relationships
|
31 |
+
todo = relationship("Todo", back_populates="translations")
|
app/models/todo.py
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from sqlalchemy import Column, Integer, String, Text, Boolean, DateTime
|
2 |
+
from sqlalchemy.orm import relationship
|
3 |
+
from sqlalchemy.sql import func
|
4 |
+
from ..core.database import Base
|
5 |
+
|
6 |
+
class Todo(Base):
|
7 |
+
__tablename__ = "todos"
|
8 |
+
|
9 |
+
id = Column(Integer, primary_key=True, index=True)
|
10 |
+
title = Column(String(255), nullable=False, index=True)
|
11 |
+
description = Column(Text, nullable=True)
|
12 |
+
completed = Column(Boolean, default=False, nullable=False)
|
13 |
+
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
14 |
+
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
15 |
+
|
16 |
+
# Relationships
|
17 |
+
subtasks = relationship("Subtask", back_populates="todo", cascade="all, delete-orphan")
|
18 |
+
translations = relationship("Translation", back_populates="todo", cascade="all, delete-orphan")
|
app/schemas/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
# Pydantic schemas for request/response models
|
app/schemas/__pycache__/__init__.cpython-312.pyc
ADDED
Binary file (182 Bytes). View file
|
|
app/schemas/__pycache__/subtask.cpython-312.pyc
ADDED
Binary file (2.14 kB). View file
|
|
app/schemas/__pycache__/todo.cpython-312.pyc
ADDED
Binary file (2.18 kB). View file
|
|
app/schemas/__pycache__/translation.cpython-312.pyc
ADDED
Binary file (1.82 kB). View file
|
|
app/schemas/subtask.py
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pydantic import BaseModel, Field
|
2 |
+
from typing import Optional
|
3 |
+
from datetime import datetime
|
4 |
+
|
5 |
+
class SubtaskBase(BaseModel):
|
6 |
+
title: str = Field(..., min_length=1, max_length=255)
|
7 |
+
description: Optional[str] = None
|
8 |
+
|
9 |
+
class SubtaskCreate(SubtaskBase):
|
10 |
+
todo_id: int
|
11 |
+
order_index: Optional[int] = 0
|
12 |
+
|
13 |
+
class SubtaskUpdate(BaseModel):
|
14 |
+
title: Optional[str] = Field(None, min_length=1, max_length=255)
|
15 |
+
description: Optional[str] = None
|
16 |
+
completed: Optional[bool] = None
|
17 |
+
order_index: Optional[int] = None
|
18 |
+
|
19 |
+
class Subtask(SubtaskBase):
|
20 |
+
id: int
|
21 |
+
todo_id: int
|
22 |
+
completed: bool
|
23 |
+
order_index: int
|
24 |
+
created_at: datetime
|
25 |
+
|
26 |
+
class Config:
|
27 |
+
from_attributes = True
|
28 |
+
|
29 |
+
class SubtaskGenerateRequest(BaseModel):
|
30 |
+
todo_id: int
|
31 |
+
max_subtasks: Optional[int] = Field(default=5, ge=1, le=10)
|
app/schemas/todo.py
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pydantic import BaseModel, Field
|
2 |
+
from typing import Optional, List, TYPE_CHECKING
|
3 |
+
from datetime import datetime
|
4 |
+
|
5 |
+
if TYPE_CHECKING:
|
6 |
+
from .subtask import Subtask
|
7 |
+
from .translation import Translation
|
8 |
+
|
9 |
+
# Base Todo schema
|
10 |
+
class TodoBase(BaseModel):
|
11 |
+
title: str = Field(..., min_length=1, max_length=255)
|
12 |
+
description: Optional[str] = None
|
13 |
+
|
14 |
+
# Schema for creating Todo
|
15 |
+
class TodoCreate(TodoBase):
|
16 |
+
pass
|
17 |
+
|
18 |
+
# Schema for updating Todo
|
19 |
+
class TodoUpdate(BaseModel):
|
20 |
+
title: Optional[str] = Field(None, min_length=1, max_length=255)
|
21 |
+
description: Optional[str] = None
|
22 |
+
completed: Optional[bool] = None
|
23 |
+
|
24 |
+
# Schema for Todo response
|
25 |
+
class Todo(TodoBase):
|
26 |
+
id: int
|
27 |
+
completed: bool
|
28 |
+
created_at: datetime
|
29 |
+
updated_at: Optional[datetime]
|
30 |
+
|
31 |
+
class Config:
|
32 |
+
from_attributes = True
|
33 |
+
|
34 |
+
# Schema for Todo with relations
|
35 |
+
class TodoWithRelations(Todo):
|
36 |
+
subtasks: List["Subtask"] = []
|
37 |
+
translations: List["Translation"] = []
|
38 |
+
|
39 |
+
# Import the actual classes to resolve forward references
|
40 |
+
from .subtask import Subtask
|
41 |
+
from .translation import Translation
|
42 |
+
|
43 |
+
# Rebuild the model to resolve forward references
|
44 |
+
TodoWithRelations.model_rebuild()
|
app/schemas/translation.py
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pydantic import BaseModel, Field
|
2 |
+
from typing import Optional
|
3 |
+
from datetime import datetime
|
4 |
+
|
5 |
+
class TranslationBase(BaseModel):
|
6 |
+
language: str = Field(..., min_length=2, max_length=50)
|
7 |
+
translated_title: str = Field(..., min_length=1, max_length=500)
|
8 |
+
translated_description: Optional[str] = None
|
9 |
+
|
10 |
+
class Translation(TranslationBase):
|
11 |
+
id: int
|
12 |
+
todo_id: int
|
13 |
+
created_at: datetime
|
14 |
+
|
15 |
+
class Config:
|
16 |
+
from_attributes = True
|
17 |
+
|
18 |
+
class TranslationRequest(BaseModel):
|
19 |
+
text: str = Field(..., min_length=1)
|
20 |
+
target_language: str = Field(..., min_length=2, max_length=50)
|
21 |
+
|
22 |
+
class TodoTranslationRequest(BaseModel):
|
23 |
+
target_language: str = Field(..., min_length=2, max_length=50)
|
app/services/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
# Business logic services
|
app/services/__pycache__/__init__.cpython-312.pyc
ADDED
Binary file (183 Bytes). View file
|
|
app/services/__pycache__/ai_service.cpython-312.pyc
ADDED
Binary file (9.56 kB). View file
|
|
app/services/__pycache__/todo_service.cpython-312.pyc
ADDED
Binary file (8.91 kB). View file
|
|
app/services/__pycache__/translation_service.cpython-312.pyc
ADDED
Binary file (4.22 kB). View file
|
|