Replaced monolithic modules/ package with a clean architecture:
- core/ config.py, utils.py
- ai/ models.py (ResilientModel), setup.py (init_models)
- story/ planner.py, writer.py, editor.py, style_persona.py, bible_tracker.py
- marketing/ cover.py, blurb.py, fonts.py, assets.py
- export/ exporter.py
- web/ app.py (Flask factory), db.py, helpers.py, tasks.py, routes/{auth,project,run,persona,admin}.py
- cli/ engine.py (run_generation), wizard.py (BookWizard)
Flask routes split into 5 Blueprints; all templates updated with blueprint-
prefixed url_for() calls. Dockerfile and docker-compose updated to use
web.app entry point and new package paths.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
51 lines
1.9 KiB
Python
51 lines
1.9 KiB
Python
from flask_sqlalchemy import SQLAlchemy
|
|
from flask_login import UserMixin
|
|
from datetime import datetime
|
|
|
|
db = SQLAlchemy()
|
|
|
|
|
|
class User(UserMixin, db.Model):
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
username = db.Column(db.String(150), unique=True, nullable=False)
|
|
password = db.Column(db.String(150), nullable=False)
|
|
api_key = db.Column(db.String(200), nullable=True)
|
|
total_spend = db.Column(db.Float, default=0.0)
|
|
is_admin = db.Column(db.Boolean, default=False)
|
|
|
|
|
|
class Project(db.Model):
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
|
name = db.Column(db.String(150), nullable=False)
|
|
folder_path = db.Column(db.String(300), nullable=False)
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
|
|
|
runs = db.relationship('Run', backref='project', lazy=True, cascade="all, delete-orphan")
|
|
|
|
|
|
class Run(db.Model):
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
project_id = db.Column(db.Integer, db.ForeignKey('project.id'), nullable=False)
|
|
status = db.Column(db.String(50), default="queued")
|
|
start_time = db.Column(db.DateTime, default=datetime.utcnow)
|
|
end_time = db.Column(db.DateTime, nullable=True)
|
|
log_file = db.Column(db.String(300), nullable=True)
|
|
cost = db.Column(db.Float, default=0.0)
|
|
progress = db.Column(db.Integer, default=0)
|
|
|
|
logs = db.relationship('LogEntry', backref='run', lazy=True, cascade="all, delete-orphan")
|
|
|
|
def duration(self):
|
|
if self.end_time and self.start_time:
|
|
return str(self.end_time - self.start_time).split('.')[0]
|
|
return "Running..."
|
|
|
|
|
|
class LogEntry(db.Model):
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
run_id = db.Column(db.Integer, db.ForeignKey('run.id'), nullable=False)
|
|
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
|
|
phase = db.Column(db.String(50))
|
|
message = db.Column(db.Text)
|