Skip to content

Commit 4f68a23

Browse files
committed
First pass of AI generated backend
1 parent fedb96d commit 4f68a23

File tree

3 files changed

+341
-0
lines changed

3 files changed

+341
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ venv/
2323
.env.test.local
2424
.env.production.local
2525
.vscode
26+
projects.db
2627

2728
npm-debug.log*
2829
yarn-debug.log*

backend/API_README.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Project and Module Management API
2+
3+
A Flask-based REST API for managing projects and modules with JSON content, using SQLite as the database.
4+
5+
## Features
6+
7+
- **Projects**: Create, read, update, and delete projects
8+
- **Modules**: Create, read, update, and delete modules within projects
9+
- **JSON Content**: Each module stores JSON content as a string
10+
- **SQLite Database**: Persistent storage using SQLite
11+
12+
## API Endpoints
13+
14+
### Projects
15+
16+
- `GET /projects` - Get list of all projects
17+
- `POST /projects` - Create a new project
18+
- `GET /projects/<project_id>` - Get a specific project with module list
19+
- `PUT /projects/<project_id>` - Update a project
20+
- `DELETE /projects/<project_id>` - Delete a project and all its modules
21+
22+
### Modules
23+
24+
- `GET /projects/<project_id>/modules` - Get all modules for a project
25+
- `POST /projects/<project_id>/modules` - Create a new module in a project
26+
- `GET /projects/<project_id>/modules/<module_id>` - Get a specific module
27+
- `PUT /projects/<project_id>/modules/<module_id>` - Update a module
28+
- `DELETE /projects/<project_id>/modules/<module_id>` - Delete a module
29+
30+
## Running the Application
31+
32+
1. Install dependencies:
33+
```bash
34+
pip install flask flask-restful flask-sqlalchemy
35+
```
36+
37+
2. Run the application:
38+
```bash
39+
python main.py
40+
```
41+
42+
3. The API will be available at `http://localhost:5001`
43+
44+
## Example Usage
45+
46+
### Create a Project
47+
```bash
48+
curl -X POST http://localhost:5001/projects \
49+
-H "Content-Type: application/json" \
50+
-d '{"name": "Robot Controller", "description": "Main robot control project"}'
51+
```
52+
53+
### Create a Module
54+
```bash
55+
curl -X POST http://localhost:5001/projects/1/modules \
56+
-H "Content-Type: application/json" \
57+
-d '{"name": "motor_controller", "json_content": {"type": "motor", "settings": {"speed": 100}}}'
58+
```
59+
60+
### Get All Projects
61+
```bash
62+
curl http://localhost:5001/projects
63+
```
64+
65+
### Get Project Modules
66+
```bash
67+
curl http://localhost:5001/projects/1/modules
68+
```
69+
70+
### Update Module Content
71+
```bash
72+
curl -X PUT http://localhost:5001/projects/1/modules/1 \
73+
-H "Content-Type: application/json" \
74+
-d '{"json_content": {"type": "motor", "settings": {"speed": 150}}}'
75+
```
76+
77+
## Data Models
78+
79+
### Project
80+
- `id`: Integer (Primary Key)
81+
- `name`: String (Unique, Required)
82+
- `description`: Text (Optional)
83+
- `modules`: Relationship to Module objects
84+
85+
### Module
86+
- `id`: Integer (Primary Key)
87+
- `name`: String (Required, Unique per project)
88+
- `project_id`: Integer (Foreign Key)
89+
- `json_content`: Text (Required, stored as JSON string)
90+
91+
## Database
92+
93+
The application creates a SQLite database file `projects.db` in the backend directory automatically when first run.

backend/main.py

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
from flask import Flask, request, jsonify
2+
from flask_restful import Resource, Api
3+
from flask_sqlalchemy import SQLAlchemy
4+
import json
5+
import os
6+
7+
app = Flask(__name__)
8+
api = Api(app)
9+
10+
# Configure SQLite database
11+
basedir = os.path.abspath(os.path.dirname(__file__))
12+
app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{os.path.join(basedir, "projects.db")}'
13+
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
14+
15+
db = SQLAlchemy(app)
16+
17+
# Models
18+
class Project(db.Model):
19+
id = db.Column(db.Integer, primary_key=True)
20+
name = db.Column(db.String(100), nullable=False, unique=True)
21+
description = db.Column(db.Text)
22+
modules = db.relationship('Module', backref='project', lazy=True, cascade='all, delete-orphan')
23+
24+
def to_dict(self):
25+
return {
26+
'id': self.id,
27+
'name': self.name,
28+
'description': self.description,
29+
'module_count': len(self.modules)
30+
}
31+
32+
class Module(db.Model):
33+
id = db.Column(db.Integer, primary_key=True)
34+
name = db.Column(db.String(100), nullable=False)
35+
project_id = db.Column(db.Integer, db.ForeignKey('project.id'), nullable=False)
36+
json_content = db.Column(db.Text, nullable=False)
37+
38+
__table_args__ = (db.UniqueConstraint('name', 'project_id', name='_module_project_uc'),)
39+
40+
def to_dict(self):
41+
return {
42+
'id': self.id,
43+
'name': self.name,
44+
'project_id': self.project_id,
45+
'json_content': json.loads(self.json_content) if self.json_content else {}
46+
}
47+
48+
# Resources
49+
class ProjectListResource(Resource):
50+
def get(self):
51+
"""Get list of all projects"""
52+
projects = Project.query.all()
53+
return {'projects': [project.to_dict() for project in projects]}
54+
55+
def post(self):
56+
"""Create a new project"""
57+
data = request.get_json()
58+
if not data or 'name' not in data:
59+
return {'error': 'Project name is required'}, 400
60+
61+
# Check if project already exists
62+
existing_project = Project.query.filter_by(name=data['name']).first()
63+
if existing_project:
64+
return {'error': 'Project with this name already exists'}, 409
65+
66+
project = Project(
67+
name=data['name'],
68+
description=data.get('description', '')
69+
)
70+
71+
try:
72+
db.session.add(project)
73+
db.session.commit()
74+
return project.to_dict(), 201
75+
except Exception as e:
76+
db.session.rollback()
77+
return {'error': 'Failed to create project'}, 500
78+
79+
class ProjectResource(Resource):
80+
def get(self, project_id):
81+
"""Get a specific project with its modules"""
82+
project = Project.query.get_or_404(project_id)
83+
project_dict = project.to_dict()
84+
project_dict['modules'] = [
85+
{
86+
'id': module.id,
87+
'name': module.name
88+
} for module in project.modules
89+
]
90+
return project_dict
91+
92+
def put(self, project_id):
93+
"""Update a project"""
94+
project = Project.query.get_or_404(project_id)
95+
data = request.get_json()
96+
97+
if not data:
98+
return {'error': 'No data provided'}, 400
99+
100+
if 'name' in data:
101+
# Check if another project has this name
102+
existing = Project.query.filter(Project.name == data['name'], Project.id != project_id).first()
103+
if existing:
104+
return {'error': 'Project with this name already exists'}, 409
105+
project.name = data['name']
106+
107+
if 'description' in data:
108+
project.description = data['description']
109+
110+
try:
111+
db.session.commit()
112+
return project.to_dict()
113+
except Exception as e:
114+
db.session.rollback()
115+
return {'error': 'Failed to update project'}, 500
116+
117+
def delete(self, project_id):
118+
"""Delete a project and all its modules"""
119+
project = Project.query.get_or_404(project_id)
120+
121+
try:
122+
db.session.delete(project)
123+
db.session.commit()
124+
return {'message': 'Project deleted successfully'}
125+
except Exception as e:
126+
db.session.rollback()
127+
return {'error': 'Failed to delete project'}, 500
128+
129+
class ModuleListResource(Resource):
130+
def get(self, project_id):
131+
"""Get all modules for a project"""
132+
project = Project.query.get_or_404(project_id)
133+
modules = Module.query.filter_by(project_id=project_id).all()
134+
return {
135+
'project': project.to_dict(),
136+
'modules': [module.to_dict() for module in modules]
137+
}
138+
139+
def post(self, project_id):
140+
"""Create a new module in a project"""
141+
project = Project.query.get_or_404(project_id)
142+
data = request.get_json()
143+
144+
if not data or 'name' not in data:
145+
return {'error': 'Module name is required'}, 400
146+
147+
if 'json_content' not in data:
148+
return {'error': 'JSON content is required'}, 400
149+
150+
# Check if module already exists in this project
151+
existing_module = Module.query.filter_by(name=data['name'], project_id=project_id).first()
152+
if existing_module:
153+
return {'error': 'Module with this name already exists in this project'}, 409
154+
155+
# Validate JSON content
156+
try:
157+
json_content = json.dumps(data['json_content'])
158+
except (TypeError, ValueError):
159+
return {'error': 'Invalid JSON content'}, 400
160+
161+
module = Module(
162+
name=data['name'],
163+
project_id=project_id,
164+
json_content=json_content
165+
)
166+
167+
try:
168+
db.session.add(module)
169+
db.session.commit()
170+
return module.to_dict(), 201
171+
except Exception as e:
172+
db.session.rollback()
173+
return {'error': 'Failed to create module'}, 500
174+
175+
class ModuleResource(Resource):
176+
def get(self, project_id, module_id):
177+
"""Get a specific module"""
178+
module = Module.query.filter_by(id=module_id, project_id=project_id).first_or_404()
179+
return module.to_dict()
180+
181+
def put(self, project_id, module_id):
182+
"""Update a module's content"""
183+
module = Module.query.filter_by(id=module_id, project_id=project_id).first_or_404()
184+
data = request.get_json()
185+
186+
if not data:
187+
return {'error': 'No data provided'}, 400
188+
189+
if 'name' in data:
190+
# Check if another module in this project has this name
191+
existing = Module.query.filter(
192+
Module.name == data['name'],
193+
Module.project_id == project_id,
194+
Module.id != module_id
195+
).first()
196+
if existing:
197+
return {'error': 'Module with this name already exists in this project'}, 409
198+
module.name = data['name']
199+
200+
if 'json_content' in data:
201+
try:
202+
json_content = json.dumps(data['json_content'])
203+
module.json_content = json_content
204+
except (TypeError, ValueError):
205+
return {'error': 'Invalid JSON content'}, 400
206+
207+
try:
208+
db.session.commit()
209+
return module.to_dict()
210+
except Exception as e:
211+
db.session.rollback()
212+
return {'error': 'Failed to update module'}, 500
213+
214+
def delete(self, project_id, module_id):
215+
"""Delete a module"""
216+
module = Module.query.filter_by(id=module_id, project_id=project_id).first_or_404()
217+
218+
try:
219+
db.session.delete(module)
220+
db.session.commit()
221+
return {'message': 'Module deleted successfully'}
222+
except Exception as e:
223+
db.session.rollback()
224+
return {'error': 'Failed to delete module'}, 500
225+
226+
# Register API routes
227+
api.add_resource(ProjectListResource, '/projects')
228+
api.add_resource(ProjectResource, '/projects/<int:project_id>')
229+
api.add_resource(ModuleListResource, '/projects/<int:project_id>/modules')
230+
api.add_resource(ModuleResource, '/projects/<int:project_id>/modules/<int:module_id>')
231+
232+
@app.route('/')
233+
def index():
234+
return jsonify({
235+
'message': 'Project and Module Management API',
236+
'endpoints': {
237+
'projects': '/projects',
238+
'specific_project': '/projects/<project_id>',
239+
'project_modules': '/projects/<project_id>/modules',
240+
'specific_module': '/projects/<project_id>/modules/<module_id>'
241+
}
242+
})
243+
244+
if __name__ == '__main__':
245+
with app.app_context():
246+
db.create_all()
247+
app.run(debug=True, port=5001)

0 commit comments

Comments
 (0)