c-stone-invoice-check/backend/alembic/env.py
Markus Kruse 937267b507 feat: Backend complete with IMAP, PDF processing, Alembic migrations
- FastAPI Backend mit Router (emails, invoices, auth)
- PostgreSQL DB Models (Invoice, EmailLog, InvoiceDetail, User)
- PyMuPDF PDF-Verarbeitung mit Steuer-Erkennung
- openpyxl Excel-Export
- Alembic Migration initialisiert
- IMAP-Verbindung zu Strato funktioniert
- Docker-Setup mit Traefik-Labels

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-24 23:45:51 +02:00

123 lines
4.2 KiB
Python

"""Alembic Environment Configuration"""
from logging.config import fileConfig
from sqlalchemy import pool
from sqlalchemy import create_engine
from alembic import context
# Import models directly (avoid env file issues)
from sqlalchemy import Column, Integer, String, DateTime, Float, Boolean, Text, JSON
from sqlalchemy.ext.declarative import declarative_base
from datetime import datetime
Base = declarative_base()
# Import models manually
class Invoice(Base):
__tablename__ = "invoices"
id = Column(Integer, primary_key=True)
email_id = Column(String, index=True)
email_subject = Column(String, nullable=True)
email_from = Column(String, nullable=True)
email_date = Column(DateTime, nullable=True)
invoice_number = Column(String, index=True, nullable=True)
invoice_date = Column(DateTime, nullable=True)
invoice_amount = Column(Float, nullable=True)
invoice_tax_amount = Column(Float, nullable=True)
invoice_tax_rate = Column(Float, nullable=True)
invoice_tax_type = Column(String, nullable=True)
issuer_name = Column(String, nullable=True)
issuer_address = Column(Text, nullable=True)
file_path = Column(String, nullable=True)
file_name = Column(String, nullable=True)
storage_year = Column(Integer, nullable=True)
storage_path = Column(String, nullable=True)
processed = Column(Boolean, default=False)
processing_error = Column(Text, nullable=True)
confidence_score = Column(Float, nullable=True)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class EmailLog(Base):
__tablename__ = "email_logs"
id = Column(Integer, primary_key=True)
email_id = Column(String, unique=True, index=True)
email_subject = Column(String, nullable=True)
email_from = Column(String, nullable=True)
email_date = Column(DateTime, nullable=True)
email_folder = Column(String, nullable=True)
matched_keywords = Column(JSON, nullable=True)
has_attachment = Column(Boolean, default=False)
attachment_type = Column(String, nullable=True)
processed = Column(Boolean, default=False)
processing_result = Column(String, nullable=True)
invoice_id = Column(Integer, nullable=True)
created_at = Column(DateTime, default=datetime.utcnow)
class InvoiceDetail(Base):
__tablename__ = "invoice_details"
id = Column(Integer, primary_key=True)
invoice_id = Column(Integer, nullable=False, index=True)
raw_text = Column(Text, nullable=True)
parsed_data = Column(JSON, nullable=True)
confidence_breakdown = Column(JSON, nullable=True)
processing_notes = Column(Text, nullable=True)
created_at = Column(DateTime, default=datetime.utcnow)
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
username = Column(String, unique=True, index=True)
hashed_password = Column(String)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.utcnow)
# Set target metadata for autogenerate
target_metadata = Base.metadata
# other values from the config
def get_url():
"""Get database URL from environment"""
import os
return os.getenv("DATABASE_URL", "postgresql+psycopg2://postgres:postgres@localhost:5432/invoice_check")
def run_migrations_offline() -> None:
"""Run migrations in 'offline' mode."""
url = get_url()
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
render_as_batch=True,
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online() -> None:
"""Run migrations in 'online' mode."""
url = get_url()
connectable = create_engine(
url,
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata,
render_as_batch=True,
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()