Add files via upload

This commit is contained in:
jasonMPM
2026-03-05 12:13:22 -06:00
committed by GitHub
parent bfa3887e61
commit 20e71ee7f9
40 changed files with 1352 additions and 368 deletions

33
backend/app/__init__.py Normal file
View File

@@ -0,0 +1,33 @@
import os
from flask import Flask, send_from_directory
from .extensions import db, migrate, cors
from config import config
def create_app(config_name=None):
if config_name is None:
config_name = os.environ.get('FLASK_ENV', 'production')
app = Flask(__name__, static_folder='static', static_url_path='')
app.config.from_object(config.get(config_name, config['default']))
db.init_app(app)
migrate.init_app(app, db)
cors.init_app(app, resources={r'/api/*': {'origins': '*'}})
from .routes.projects import projects_bp
from .routes.deliverables import deliverables_bp
app.register_blueprint(projects_bp, url_prefix='/api')
app.register_blueprint(deliverables_bp, url_prefix='/api')
with app.app_context():
db.create_all()
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def serve_react(path):
static_folder = app.static_folder
if path and os.path.exists(os.path.join(static_folder, path)):
return send_from_directory(static_folder, path)
return send_from_directory(static_folder, 'index.html')
return app

View File

@@ -0,0 +1,7 @@
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_cors import CORS
db = SQLAlchemy()
migrate = Migrate()
cors = CORS()

51
backend/app/models.py Normal file
View File

@@ -0,0 +1,51 @@
from .extensions import db
from datetime import datetime
class Project(db.Model):
__tablename__ = 'projects'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(200), nullable=False)
color = db.Column(db.String(7), nullable=False, default='#C9A84C')
description = db.Column(db.Text)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
deliverables = db.relationship(
'Deliverable', backref='project',
cascade='all, delete-orphan', lazy=True
)
def to_dict(self, include_deliverables=True):
data = {
'id': self.id,
'name': self.name,
'color': self.color,
'description': self.description,
'created_at': self.created_at.isoformat() if self.created_at else None,
}
if include_deliverables:
data['deliverables'] = [
d.to_dict() for d in sorted(self.deliverables, key=lambda x: x.due_date)
]
return data
class Deliverable(db.Model):
__tablename__ = 'deliverables'
id = db.Column(db.Integer, primary_key=True)
project_id = db.Column(db.Integer, db.ForeignKey('projects.id', ondelete='CASCADE'), nullable=False)
title = db.Column(db.String(300), nullable=False)
due_date = db.Column(db.Date, nullable=False)
status = db.Column(db.String(20), nullable=False, default='upcoming')
created_at = db.Column(db.DateTime, default=datetime.utcnow)
def to_dict(self):
return {
'id': self.id,
'project_id': self.project_id,
'title': self.title,
'due_date': self.due_date.isoformat() if self.due_date else None,
'status': self.status,
'created_at': self.created_at.isoformat() if self.created_at else None,
}

View File

View File

@@ -0,0 +1,44 @@
from flask import Blueprint, jsonify, request
from ..models import Deliverable
from ..extensions import db
from datetime import date
deliverables_bp = Blueprint('deliverables', __name__)
@deliverables_bp.route('/deliverables', methods=['GET'])
def get_deliverables():
project_id = request.args.get('project_id')
q = Deliverable.query
if project_id:
q = q.filter_by(project_id=int(project_id))
return jsonify([d.to_dict() for d in q.order_by(Deliverable.due_date).all()])
@deliverables_bp.route('/deliverables', methods=['POST'])
def create_deliverable():
data = request.get_json()
d = Deliverable(
project_id=data['project_id'],
title=data['title'],
due_date=date.fromisoformat(data['due_date']),
status=data.get('status', 'upcoming'),
)
db.session.add(d)
db.session.commit()
return jsonify(d.to_dict()), 201
@deliverables_bp.route('/deliverables/<int:id>', methods=['PATCH'])
def update_deliverable(id):
d = Deliverable.query.get_or_404(id)
data = request.get_json()
if 'title' in data: d.title = data['title']
if 'due_date' in data: d.due_date = date.fromisoformat(data['due_date'])
if 'status' in data: d.status = data['status']
db.session.commit()
return jsonify(d.to_dict())
@deliverables_bp.route('/deliverables/<int:id>', methods=['DELETE'])
def delete_deliverable(id):
d = Deliverable.query.get_or_404(id)
db.session.delete(d)
db.session.commit()
return jsonify({'message': 'Deliverable deleted'}), 200

View File

@@ -0,0 +1,54 @@
from flask import Blueprint, jsonify, request
from ..models import Project, Deliverable
from ..extensions import db
from datetime import date
projects_bp = Blueprint('projects', __name__)
@projects_bp.route('/projects', methods=['GET'])
def get_projects():
projects = Project.query.order_by(Project.created_at.desc()).all()
return jsonify([p.to_dict() for p in projects])
@projects_bp.route('/projects/<int:id>', methods=['GET'])
def get_project(id):
project = Project.query.get_or_404(id)
return jsonify(project.to_dict())
@projects_bp.route('/projects', methods=['POST'])
def create_project():
data = request.get_json()
project = Project(
name=data['name'],
color=data.get('color', '#C9A84C'),
description=data.get('description', ''),
)
db.session.add(project)
db.session.flush()
for d in data.get('deliverables', []):
if d.get('title') and d.get('due_date'):
db.session.add(Deliverable(
project_id=project.id,
title=d['title'],
due_date=date.fromisoformat(d['due_date']),
status=d.get('status', 'upcoming'),
))
db.session.commit()
return jsonify(project.to_dict()), 201
@projects_bp.route('/projects/<int:id>', methods=['PATCH'])
def update_project(id):
project = Project.query.get_or_404(id)
data = request.get_json()
for field in ('name', 'color', 'description'):
if field in data:
setattr(project, field, data[field])
db.session.commit()
return jsonify(project.to_dict())
@projects_bp.route('/projects/<int:id>', methods=['DELETE'])
def delete_project(id):
project = Project.query.get_or_404(id)
db.session.delete(project)
db.session.commit()
return jsonify({'message': 'Project deleted'}), 200