Kushal commited on
Commit
6621cba
·
1 Parent(s): 7922299

Fix: Sync missing report routes and templates for production

Browse files
app/database/models.py CHANGED
@@ -137,3 +137,18 @@ class UserSettings(Base):
137
 
138
  created_at = Column(DateTime, default=datetime.utcnow)
139
  updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
  created_at = Column(DateTime, default=datetime.utcnow)
139
  updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
140
+
141
+
142
+ class Report(Base):
143
+ """Generated reports model."""
144
+ __tablename__ = "reports"
145
+
146
+ id = Column(String, primary_key=True, default=generate_id)
147
+ user_id = Column(String, ForeignKey("users.id", ondelete="CASCADE"), nullable=True, index=True)
148
+ template_id = Column(String, nullable=False)
149
+ filename = Column(String, nullable=False)
150
+ file_path = Column(String, nullable=False)
151
+ created_at = Column(DateTime, default=datetime.utcnow)
152
+
153
+ # Relationships
154
+ user = relationship("User")
app/routes/reports.py CHANGED
@@ -1,13 +1,15 @@
1
  """
2
  API routes for report generation.
3
  """
4
- from fastapi import APIRouter, HTTPException, Depends
 
5
  from sqlalchemy.orm import Session
6
  from typing import Optional, Dict
7
  from pydantic import BaseModel
 
8
 
9
  from app.database.connection import get_db
10
- from app.database.models import User
11
  from app.middleware.auth import get_current_user_optional
12
  from app.services.report_service import report_generation_service
13
 
@@ -21,6 +23,12 @@ class GenerateContentRequest(BaseModel):
21
  context: Dict[str, str] = {}
22
 
23
 
 
 
 
 
 
 
24
  @router.post("/generate-content")
25
  async def generate_section_content(
26
  request: GenerateContentRequest,
@@ -56,3 +64,49 @@ async def generate_section_content(
56
  status_code=500,
57
  detail=f"Error generating content: {str(e)}"
58
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
  API routes for report generation.
3
  """
4
+ from fastapi import APIRouter, HTTPException, Depends, status
5
+ from fastapi.responses import FileResponse
6
  from sqlalchemy.orm import Session
7
  from typing import Optional, Dict
8
  from pydantic import BaseModel
9
+ import os
10
 
11
  from app.database.connection import get_db
12
+ from app.database.models import User, Report
13
  from app.middleware.auth import get_current_user_optional
14
  from app.services.report_service import report_generation_service
15
 
 
23
  context: Dict[str, str] = {}
24
 
25
 
26
+ class GenerateReportRequest(BaseModel):
27
+ """Request schema for full report generation."""
28
+ template_id: str
29
+ data: Dict[str, str]
30
+
31
+
32
  @router.post("/generate-content")
33
  async def generate_section_content(
34
  request: GenerateContentRequest,
 
64
  status_code=500,
65
  detail=f"Error generating content: {str(e)}"
66
  )
67
+
68
+
69
+ @router.post("/generate")
70
+ async def generate_report(
71
+ request: GenerateReportRequest,
72
+ current_user: Optional[User] = Depends(get_current_user_optional)
73
+ ):
74
+ """
75
+ Generate a full report and return its ID.
76
+ """
77
+ try:
78
+ user_id = current_user.id if current_user else None
79
+ report_id = report_generation_service.generate_full_pdf(
80
+ template_id=request.template_id,
81
+ data=request.data,
82
+ user_id=user_id
83
+ )
84
+ return {"report_id": report_id, "success": True}
85
+ except Exception as e:
86
+ print(f"Error generating report: {e}")
87
+ raise HTTPException(
88
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
89
+ detail=str(e)
90
+ )
91
+
92
+
93
+ @router.get("/download-pdf/{report_id}")
94
+ async def download_report_pdf(
95
+ report_id: str,
96
+ db: Session = Depends(get_db)
97
+ ):
98
+ """
99
+ Download a generated PDF report.
100
+ """
101
+ report = db.query(Report).filter(Report.id == report_id).first()
102
+ if not report:
103
+ raise HTTPException(status_code=404, detail="Report not found")
104
+
105
+ if not os.path.exists(report.file_path):
106
+ raise HTTPException(status_code=404, detail="Report file not found")
107
+
108
+ return FileResponse(
109
+ path=report.file_path,
110
+ filename=report.filename,
111
+ media_type="application/pdf"
112
+ )
app/services/report_service.py CHANGED
@@ -1,7 +1,8 @@
1
  """
2
  Service for AI-powered report content generation.
3
  """
4
- from typing import Dict
 
5
  from app.llm.client import llm_client
6
 
7
 
@@ -139,5 +140,97 @@ Return ONLY the content:"""
139
  return f"Error generating content for {section_name}. Please try again or edit manually."
140
 
141
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  # Global service instance
143
  report_generation_service = ReportGenerationService()
 
1
  """
2
  Service for AI-powered report content generation.
3
  """
4
+ from typing import Dict, Optional
5
+ import uuid
6
  from app.llm.client import llm_client
7
 
8
 
 
140
  return f"Error generating content for {section_name}. Please try again or edit manually."
141
 
142
 
143
+ @staticmethod
144
+ def generate_full_pdf(
145
+ template_id: str,
146
+ data: Dict[str, str],
147
+ user_id: Optional[str] = None
148
+ ) -> str:
149
+ """
150
+ Generate a full PDF report from an HTML template.
151
+
152
+ Args:
153
+ template_id: ID of the template to use
154
+ data: Data to populate the template with
155
+ user_id: Optional user ID
156
+
157
+ Returns:
158
+ ID of the generated report record
159
+ """
160
+ import os
161
+ from xhtml2pdf import pisa
162
+ from app.database.models import Report
163
+ from app.database.connection import SessionLocal
164
+ from datetime import datetime
165
+
166
+ template_map = {
167
+ 'property_evaluation': 'property-evaluation.html',
168
+ 'investor_pitch_deck': 'investor-pitch-deck.html',
169
+ 'legal_compliance': 'legal-compliance.html'
170
+ }
171
+
172
+ template_file = template_map.get(template_id)
173
+ if not template_file:
174
+ raise ValueError(f"Template {template_id} not found")
175
+
176
+ # Get absolute path to template
177
+ base_dir = os.path.dirname(os.path.dirname(__file__))
178
+ template_path = os.path.join(base_dir, "templates", template_file)
179
+
180
+ if not os.path.exists(template_path):
181
+ raise FileNotFoundError(f"Template file not found at {template_path}")
182
+
183
+ # Load template
184
+ with open(template_path, "r", encoding="utf-8") as f:
185
+ template_html = f.read()
186
+
187
+ # Populate template (simple replacement)
188
+ populated_html = template_html
189
+
190
+ # Add date
191
+ today = datetime.now().strftime("%d %b %Y")
192
+ populated_html = populated_html.replace("{{DATE}}", today)
193
+
194
+ # Add data placeholders
195
+ for key, value in data.items():
196
+ placeholder = f"{{{{{key.upper()}}}}}"
197
+ populated_html = populated_html.replace(placeholder, str(value or ""))
198
+
199
+ # Remove AI buttons and other non-print elements
200
+ populated_html = populated_html.replace('<button class="ai-button"', '<div style="display:none"')
201
+ populated_html = populated_html.replace('</button>', '</div>')
202
+
203
+ # Define output path
204
+ reports_dir = os.path.join(os.getcwd(), "data", "generated_reports")
205
+ os.makedirs(reports_dir, exist_ok=True)
206
+
207
+ report_id = str(uuid.uuid4())
208
+ filename = f"{template_id}_{report_id}.pdf"
209
+ file_path = os.path.join(reports_dir, filename)
210
+
211
+ # Generate PDF
212
+ with open(file_path, "wb") as pdf_file:
213
+ pisa_status = pisa.CreatePDF(populated_html, dest=pdf_file)
214
+
215
+ if pisa_status.err:
216
+ raise RuntimeError(f"PDF generation failed: {pisa_status.err}")
217
+
218
+ # Save to database
219
+ db = SessionLocal()
220
+ try:
221
+ report_record = Report(
222
+ id=report_id,
223
+ user_id=user_id,
224
+ template_id=template_id,
225
+ filename=filename,
226
+ file_path=file_path
227
+ )
228
+ db.add(report_record)
229
+ db.commit()
230
+ return report_id
231
+ finally:
232
+ db.close()
233
+
234
+
235
  # Global service instance
236
  report_generation_service = ReportGenerationService()