Spaces:
Running
Running
| import datetime | |
| import uuid | |
| from sqlalchemy import ( | |
| Column, | |
| String, | |
| Integer, | |
| Float, | |
| DateTime, | |
| ForeignKey, | |
| Boolean, | |
| Text, | |
| JSON, | |
| ) | |
| from sqlalchemy.orm import relationship, backref | |
| from core.subscription.db import Base | |
| class Project(Base): | |
| __tablename__ = "projects" | |
| id = Column(String, primary_key=True, index=True, default=lambda: str(uuid.uuid4())) | |
| clerk_user_id = Column( | |
| String, ForeignKey("users.clerk_id"), index=True, nullable=False | |
| ) | |
| title = Column(String, nullable=False) | |
| description = Column(String, nullable=True) | |
| status = Column(String, default="draft") # draft / in_progress / completed | |
| estimated_value = Column(Float, nullable=True) | |
| program_type = Column( | |
| String, default="SMART", nullable=False | |
| ) # e.g. "SMART", "ARIMR" | |
| program_name = Column( | |
| String, nullable=True | |
| ) # e.g. "Ścieżka SMART - Innowacje 2026" | |
| last_generated_at = Column(DateTime, nullable=True) | |
| final_document_markdown = Column(Text, nullable=True) | |
| final_document_generated_at = Column(DateTime, nullable=True) | |
| final_document_audit_result = Column(JSON, nullable=True) | |
| external_context = Column(JSON, nullable=True) | |
| foreign_grant_extract_text = Column( | |
| Text, nullable=True | |
| ) # Tekst wniosku zewnętrznego z LlamaParse | |
| created_at = Column( | |
| DateTime, default=lambda: datetime.datetime.now(datetime.timezone.utc) | |
| ) | |
| updated_at = Column( | |
| DateTime, | |
| default=lambda: datetime.datetime.now(datetime.timezone.utc), | |
| onupdate=lambda: datetime.datetime.now(datetime.timezone.utc), | |
| ) | |
| user = relationship("User", backref="projects") | |
| class ProjectSectionTemplate(Base): | |
| __tablename__ = "project_section_templates" | |
| id = Column(Integer, primary_key=True) | |
| program_type = Column( | |
| String, index=True, nullable=False | |
| ) # "SMART", "ARIMR", "ZUS_BHP", "REGIONAL" | |
| section_type = Column( | |
| String, nullable=False | |
| ) # unique identifier like "description", "market_analysis" | |
| order = Column(Integer, default=0) | |
| title = Column(String, nullable=False) | |
| description = Column(String, nullable=True) # krótki opis sekcji dla użytkownika | |
| default_prompt = Column(Text, nullable=True) # podpowiedź dla Wizard | |
| ai_prompt_template = Column( | |
| Text, nullable=True | |
| ) # szczegółowy precyzyjny prompt per sekcja | |
| is_required = Column(Boolean, default=True) | |
| class ProjectSection(Base): | |
| __tablename__ = "project_sections" | |
| id = Column(String, primary_key=True, index=True, default=lambda: str(uuid.uuid4())) | |
| project_id = Column( | |
| String, | |
| ForeignKey("projects.id", ondelete="CASCADE"), | |
| index=True, | |
| nullable=False, | |
| ) | |
| order = Column(Integer, default=0) | |
| section_type = Column(String, nullable=False) # np. description, budget, innovation | |
| content = Column(String, nullable=True) # Treść Markdown | |
| is_approved = Column(Boolean, default=False) | |
| generated_by_ai = Column(Boolean, default=False) | |
| last_reviewed_at = Column(DateTime, nullable=True) | |
| project = relationship( | |
| "Project", backref=backref("sections", cascade="all, delete-orphan") | |
| ) | |
| class ProjectSectionVersion(Base): | |
| __tablename__ = "section_versions" | |
| id = Column(String, primary_key=True, index=True, default=lambda: str(uuid.uuid4())) | |
| section_id = Column( | |
| String, | |
| ForeignKey("project_sections.id", ondelete="CASCADE"), | |
| index=True, | |
| nullable=False, | |
| ) | |
| old_content = Column(String, nullable=True) | |
| author = Column(String, default="Ręczna") # np. "Ręczna", "Asystent AI", "Autofix" | |
| summary = Column( | |
| String, nullable=True | |
| ) # krótki opis, np. "Aktualizacja na podstawie audytu" | |
| timestamp = Column( | |
| DateTime, default=lambda: datetime.datetime.now(datetime.timezone.utc) | |
| ) | |
| section = relationship( | |
| "ProjectSection", | |
| backref=backref( | |
| "versions", | |
| cascade="all, delete-orphan", | |
| order_by="desc(ProjectSectionVersion.timestamp)", | |
| ), | |
| ) | |
| class ProjectQuestion(Base): | |
| __tablename__ = "project_questions" | |
| id = Column(String, primary_key=True, index=True, default=lambda: str(uuid.uuid4())) | |
| project_id = Column( | |
| String, | |
| ForeignKey("projects.id", ondelete="CASCADE"), | |
| index=True, | |
| nullable=False, | |
| ) | |
| question = Column(String, nullable=False) | |
| answer = Column(String, nullable=False) | |
| sources = Column( | |
| String, nullable=True | |
| ) # Zapisywanie jako string połączony lub JSON | |
| confidence = Column(Float, nullable=True) | |
| recommendation = Column(String, nullable=True) | |
| created_at = Column( | |
| DateTime, default=lambda: datetime.datetime.now(datetime.timezone.utc) | |
| ) | |
| project = relationship( | |
| "Project", backref=backref("questions_history", cascade="all, delete-orphan") | |
| ) | |
| class ProjectExportVersion(Base): | |
| __tablename__ = "project_export_versions" | |
| id = Column(String, primary_key=True, index=True, default=lambda: str(uuid.uuid4())) | |
| project_id = Column( | |
| String, | |
| ForeignKey("projects.id", ondelete="CASCADE"), | |
| index=True, | |
| nullable=False, | |
| ) | |
| version_number = Column(Integer, default=1) | |
| title = Column(String, nullable=True) # np. "Wersja Draft" | |
| export_type = Column(String, default="archived") # "current" | "archived" | |
| content_markdown = Column(Text, nullable=True) | |
| created_at = Column( | |
| DateTime, default=lambda: datetime.datetime.now(datetime.timezone.utc) | |
| ) | |
| project = relationship( | |
| "Project", backref=backref("export_versions", cascade="all, delete-orphan") | |
| ) | |
| class ProjectChatMessage(Base): | |
| __tablename__ = "project_chat_messages" | |
| id = Column(String, primary_key=True, index=True, default=lambda: str(uuid.uuid4())) | |
| project_id = Column( | |
| String, | |
| ForeignKey("projects.id", ondelete="CASCADE"), | |
| index=True, | |
| nullable=False, | |
| ) | |
| role = Column(String, nullable=False) # 'user' or 'assistant' | |
| content = Column(Text, nullable=False) | |
| created_at = Column( | |
| DateTime, default=lambda: datetime.datetime.now(datetime.timezone.utc) | |
| ) | |
| project = relationship( | |
| "Project", | |
| backref=backref( | |
| "chat_messages", | |
| cascade="all, delete-orphan", | |
| order_by="ProjectChatMessage.created_at", | |
| ), | |
| ) | |
| class ProjectDocument(Base): | |
| """ | |
| Dokument PDF przesłany przez użytkownika do projektu. | |
| Po indeksacji trafia do Pinecone (namespace=tenant_{user_id}). | |
| Statusy: | |
| uploaded — plik zapisany, oczekuje na przetworzenie | |
| processing — trwa parsowanie PDF i indeksacja w RAG | |
| indexed — dostępny w vector store | |
| error — błąd parsowania/indeksacji | |
| """ | |
| __tablename__ = "project_documents" | |
| id = Column(String, primary_key=True, index=True, default=lambda: str(uuid.uuid4())) | |
| project_id = Column( | |
| String, | |
| ForeignKey("projects.id", ondelete="CASCADE"), | |
| index=True, | |
| nullable=False, | |
| ) | |
| filename = Column(String, nullable=False) | |
| original_filename = Column(String, nullable=False) | |
| file_size_bytes = Column(Integer, nullable=True) | |
| mime_type = Column(String, default="application/pdf") | |
| doc_type = Column( | |
| String, default="knowledge_base" | |
| ) # knowledge_base | external_grant | |
| # Ścieżka pliku na dysku / URL (np. /data/uploads/{project_id}/{id}.pdf) | |
| storage_path = Column(String, nullable=True) | |
| # Pipeline RAG | |
| status = Column( | |
| String, default="uploaded" | |
| ) # uploaded | processing | indexed | error | |
| parser_used = Column(String, nullable=True) # llamaparse | pypdf | unstructured | |
| chunks_count = Column(Integer, nullable=True) # liczba child chunks w Pinecone | |
| rag_namespace = Column(String, nullable=True) # tenant_{user_id}_{project_id} | |
| error_message = Column(Text, nullable=True) | |
| processing_metadata = Column(JSON, nullable=True) # dodatkowe dane z parsera | |
| uploaded_at = Column( | |
| DateTime, default=lambda: datetime.datetime.now(datetime.timezone.utc) | |
| ) | |
| indexed_at = Column(DateTime, nullable=True) | |
| project = relationship( | |
| "Project", backref=backref("documents", cascade="all, delete-orphan") | |
| ) | |