dina1 commited on
Commit
d038c37
ยท
verified ยท
1 Parent(s): 196cfba

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +71 -118
app.py CHANGED
@@ -1,38 +1,21 @@
1
  import os
2
  import uuid
 
3
  from fastapi import FastAPI, UploadFile, File, Request
4
- from fastapi.responses import HTMLResponse, JSONResponse, FileResponse
5
  from fastapi.staticfiles import StaticFiles
6
  from fastapi.templating import Jinja2Templates
7
  from fastapi.middleware.cors import CORSMiddleware
8
  from dotenv import load_dotenv
9
  from pdfminer.high_level import extract_text as pdf_extract_text
10
  from docx import Document
11
- from playwright_model import generate_ui_report
12
-
13
- # LangChain 1.0.5 for LangGraph / LangSmith
14
- import langchain
15
- print("LangChain version:", langchain.__version__)
16
-
17
-
18
- from langgraph.graph import StateGraph, END
19
- from langsmith import traceable
20
  import google.generativeai as genai
21
-
22
- # --- LangSmith / LangChain Tracing Check ---
23
- api_key = os.getenv("LANGSMITH_API_KEY")
24
- print("๐Ÿ” LangSmith tracing enabled:", os.getenv("LANGCHAIN_TRACING_V2"))
25
- print("๐Ÿ” LangSmith project:", os.getenv("LANGCHAIN_PROJECT"))
26
- print("๐Ÿ” LangSmith API key detected:", bool(api_key))
27
- print("๐Ÿ” LangSmith workspace ID:", workspace_id if workspace_id else "None")
28
-
29
 
30
  # ==========================================================
31
  # ๐Ÿ”ง Setup
32
  # ==========================================================
33
-
34
  load_dotenv("settings.env")
35
-
36
  app = FastAPI(title="PowerApps Mockup Generator")
37
 
38
  # Enable CORS
@@ -44,19 +27,26 @@ app.add_middleware(
44
  allow_headers=["*"],
45
  )
46
 
 
47
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
48
  UPLOAD_FOLDER = os.path.join(BASE_DIR, "static", "uploads")
49
  OUTPUT_FOLDER = os.path.join(BASE_DIR, "static", "outputs")
50
  os.makedirs(UPLOAD_FOLDER, exist_ok=True)
51
  os.makedirs(OUTPUT_FOLDER, exist_ok=True)
52
 
 
53
  app.mount("/static", StaticFiles(directory="static"), name="static")
 
 
54
  templates = Jinja2Templates(directory="templates")
55
 
 
 
 
 
56
  # ==========================================================
57
- # ๐Ÿ“„ Helper: File Extraction
58
  # ==========================================================
59
-
60
  def extract_text_from_file(filepath: str) -> str:
61
  """Extract text from a PDF or DOCX file."""
62
  if filepath.endswith(".pdf"):
@@ -67,49 +57,38 @@ def extract_text_from_file(filepath: str) -> str:
67
  return ""
68
 
69
  # ==========================================================
70
- # ๐Ÿค– LangGraph Agent Setup
71
  # ==========================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
- GEMINI_KEYS = {
74
- "parser": os.getenv("GEMINI_API_KEY"),
75
- "requirements": os.getenv("GEMINI_API_KEY1"),
76
- "ui": os.getenv("GEMINI_API_KEY2"),
77
- }
78
-
79
- # Configure API keys globally
80
- genai.configure(api_key=GEMINI_KEYS["parser"]) # parser
81
- parser_model = genai.GenerativeModel("gemini-2.5-pro")
82
-
83
- genai.configure(api_key=GEMINI_KEYS["requirements"]) # requirements
84
- req_model = genai.GenerativeModel("gemini-2.5-pro")
85
-
86
- genai.configure(api_key=GEMINI_KEYS["ui"]) # UI generator
87
- ui_model = genai.GenerativeModel("gemini-2.5-pro")
88
-
89
- class AgentState(dict):
90
- """Simple state container for passing data between agents."""
91
- pass
92
-
93
- @traceable(name="DocumentParserAgent")
94
- def document_parser_agent(state: AgentState) -> AgentState:
95
- text = state.get("input_text", "")
96
- result = parser_model.generate_content(f"Extract structured sections and summaries from:\n\n{text}")
97
- state["parsed_doc"] = result.text.strip()
98
- return state
99
-
100
- @traceable(name="RequirementsExtractionAgent")
101
- def requirements_agent(state: AgentState) -> AgentState:
102
- parsed = state.get("parsed_doc", "")
103
- result = req_model.generate_content(f"Extract key functional and UI requirements for a recruitment system from this document:\n\n{parsed}")
104
- state["requirements"] = result.text.strip()
105
- return state
106
-
107
- @traceable(name="UIGeneratorAgent")
108
- def ui_generator_agent(state: AgentState) -> AgentState:
109
- requirements = state.get("requirements", "")
110
- reference_html = state.get("reference_html", "")
111
- all_text = state.get("input_text", "")
112
 
 
 
 
 
 
 
 
 
 
 
113
  prompt = f"""
114
  You are an expert PowerApps-style UI generator and business workflow designer.
115
  Your goal is to create a Recruitment Management Application HTML layout that follows **exactly the same layout, structure, and JavaScript logic** as the reference QMS app design provided below.
@@ -151,82 +130,55 @@ def ui_generator_agent(state: AgentState) -> AgentState:
151
  --------------------
152
  """
153
 
154
- result = ui_model.generate_content(prompt)
155
- state["generated_ui"] = result.text.strip()
156
- return state
157
-
158
- def build_agent_graph():
159
- """Build the sequential agent pipeline."""
160
- graph = StateGraph(AgentState)
161
- graph.add_node("parse", document_parser_agent)
162
- graph.add_node("requirements", requirements_agent)
163
- graph.add_node("ui", ui_generator_agent)
164
-
165
- graph.add_edge("parse", "requirements")
166
- graph.add_edge("requirements", "ui")
167
- graph.add_edge("ui", END)
168
-
169
- graph.set_entry_point("parse")
170
- return graph.compile()
171
-
172
- # ==========================================================
173
- # ๐Ÿงญ Routes
174
- # ==========================================================
175
-
176
- @app.get("/", response_class=HTMLResponse)
177
- async def index(request: Request):
178
- return templates.TemplateResponse("index.html", {"request": request})
179
-
180
- @app.post("/upload")
181
- async def upload_files(request: Request, files: list[UploadFile] = File(...)):
182
- all_text = ""
183
- for file in files:
184
- filename = f"{uuid.uuid4()}_{file.filename}"
185
- filepath = os.path.join(UPLOAD_FOLDER, filename)
186
- with open(filepath, "wb") as f:
187
- f.write(await file.read())
188
- all_text += extract_text_from_file(filepath) + "\n"
189
-
190
- if not all_text.strip():
191
- return JSONResponse({"error": "No readable content found in uploaded files."}, status_code=400)
192
-
193
- reference_path = os.path.join(BASE_DIR, "templates", "demo_qms_design.html")
194
- if not os.path.exists(reference_path):
195
- return JSONResponse({"error": "Reference design not found."}, status_code=500)
196
-
197
- with open(reference_path, "r", encoding="utf-8") as f:
198
- reference_html = f.read()
199
-
200
  try:
201
- graph = build_agent_graph()
202
- initial_state = AgentState({
203
- "input_text": all_text,
204
- "reference_html": reference_html
205
- })
206
- final_state = graph.invoke(initial_state)
207
  except Exception as e:
208
- return JSONResponse({"error": f"Agent pipeline failed: {str(e)}"}, status_code=500)
209
 
210
- generated_html = final_state.get("generated_ui", "").strip()
211
  if generated_html.startswith("```"):
212
- generated_html = generated_html.split("```html")[-1].split("```")[-1].strip()
 
 
 
 
 
213
 
 
 
 
214
  output_filename = f"{uuid.uuid4()}.html"
215
  output_path = os.path.join(OUTPUT_FOLDER, output_filename)
216
  with open(output_path, "w", encoding="utf-8") as f:
217
  f.write(generated_html)
218
 
 
 
 
219
  host = request.url.hostname
220
  scheme = request.url.scheme
221
- public_url = f"{scheme}://{host}/static/outputs/{output_filename}"
 
222
 
 
 
 
223
  return JSONResponse({
224
  "html": generated_html,
225
  "link": public_url
226
  })
227
 
 
 
 
 
228
  @app.post("/generate_report")
229
  async def generate_report(request: Request):
 
230
  data = await request.json()
231
  app_url = data.get("url")
232
  if not app_url:
@@ -234,4 +186,5 @@ async def generate_report(request: Request):
234
 
235
  output_pdf = os.path.join(OUTPUT_FOLDER, "UI_Report.pdf")
236
  await generate_ui_report(app_url, output_pdf)
 
237
  return FileResponse(output_pdf, filename="UI_Report.pdf", media_type="application/pdf")
 
1
  import os
2
  import uuid
3
+ import re
4
  from fastapi import FastAPI, UploadFile, File, Request
5
+ from fastapi.responses import HTMLResponse, JSONResponse
6
  from fastapi.staticfiles import StaticFiles
7
  from fastapi.templating import Jinja2Templates
8
  from fastapi.middleware.cors import CORSMiddleware
9
  from dotenv import load_dotenv
10
  from pdfminer.high_level import extract_text as pdf_extract_text
11
  from docx import Document
 
 
 
 
 
 
 
 
 
12
  import google.generativeai as genai
13
+ import fpdf
 
 
 
 
 
 
 
14
 
15
  # ==========================================================
16
  # ๐Ÿ”ง Setup
17
  # ==========================================================
 
18
  load_dotenv("settings.env")
 
19
  app = FastAPI(title="PowerApps Mockup Generator")
20
 
21
  # Enable CORS
 
27
  allow_headers=["*"],
28
  )
29
 
30
+ # Directory setup
31
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
32
  UPLOAD_FOLDER = os.path.join(BASE_DIR, "static", "uploads")
33
  OUTPUT_FOLDER = os.path.join(BASE_DIR, "static", "outputs")
34
  os.makedirs(UPLOAD_FOLDER, exist_ok=True)
35
  os.makedirs(OUTPUT_FOLDER, exist_ok=True)
36
 
37
+ # Serve static files
38
  app.mount("/static", StaticFiles(directory="static"), name="static")
39
+
40
+ # Setup templates
41
  templates = Jinja2Templates(directory="templates")
42
 
43
+ # Gemini setup
44
+ genai.configure(api_key=os.environ.get("GEMINI_API_KEY2"))
45
+ model = genai.GenerativeModel("gemini-2.5-pro")
46
+
47
  # ==========================================================
48
+ # ๐Ÿ“„ Helper Function
49
  # ==========================================================
 
50
  def extract_text_from_file(filepath: str) -> str:
51
  """Extract text from a PDF or DOCX file."""
52
  if filepath.endswith(".pdf"):
 
57
  return ""
58
 
59
  # ==========================================================
60
+ # ๐Ÿงญ Routes
61
  # ==========================================================
62
+ @app.get("/", response_class=HTMLResponse)
63
+ async def index(request: Request):
64
+ """Serve the main UI."""
65
+ return templates.TemplateResponse("index.html", {"request": request})
66
+
67
+ @app.post("/upload")
68
+ async def upload_files(request: Request, files: list[UploadFile] = File(...)):
69
+ """Handle uploaded files and generate PowerApps-style HTML."""
70
+ all_text = ""
71
+ for file in files:
72
+ filename = f"{uuid.uuid4()}_{file.filename}"
73
+ filepath = os.path.join(UPLOAD_FOLDER, filename)
74
+ with open(filepath, "wb") as f:
75
+ f.write(await file.read())
76
+ extracted_text = extract_text_from_file(filepath)
77
+ all_text += f"\n\nDocument: {file.filename}\n{extracted_text}"
78
 
79
+ if not all_text.strip():
80
+ return JSONResponse({"error": "No readable content found in files."}, status_code=400)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
+ reference_path = os.path.join(BASE_DIR, "templates", "demo_qms_design.html")
83
+ if not os.path.exists(reference_path):
84
+ return JSONResponse({"error": "Reference design not found."}, status_code=500)
85
+
86
+ with open(reference_path, "r", encoding="utf-8") as f:
87
+ reference_html = f.read()
88
+
89
+ # ==========================================================
90
+ # ๐Ÿง  Gemini Prompt
91
+ # ==========================================================
92
  prompt = f"""
93
  You are an expert PowerApps-style UI generator and business workflow designer.
94
  Your goal is to create a Recruitment Management Application HTML layout that follows **exactly the same layout, structure, and JavaScript logic** as the reference QMS app design provided below.
 
130
  --------------------
131
  """
132
 
133
+ # ==========================================================
134
+ # ๐Ÿค– Gemini Call
135
+ # ==========================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  try:
137
+ response = model.generate_content(prompt)
138
+ generated_html = response.text.strip()
 
 
 
 
139
  except Exception as e:
140
+ return JSONResponse({"error": f"Gemini API error: {str(e)}"}, status_code=500)
141
 
142
+ # Clean up possible markdown fences
143
  if generated_html.startswith("```"):
144
+ generated_html = generated_html.split("```html")[-1]
145
+ generated_html = generated_html.split("```")[-1].strip()
146
+
147
+ matches = re.findall(r"<html.*?</html>", generated_html, re.DOTALL | re.IGNORECASE)
148
+ if matches:
149
+ generated_html = matches[0]
150
 
151
+ # ==========================================================
152
+ # ๐Ÿ’พ Save output
153
+ # ==========================================================
154
  output_filename = f"{uuid.uuid4()}.html"
155
  output_path = os.path.join(OUTPUT_FOLDER, output_filename)
156
  with open(output_path, "w", encoding="utf-8") as f:
157
  f.write(generated_html)
158
 
159
+ # ==========================================================
160
+ # ๐ŸŒ Auto-detect base URL
161
+ # ==========================================================
162
  host = request.url.hostname
163
  scheme = request.url.scheme
164
+ base_url = f"{scheme}://{host}"
165
+ public_url = f"{base_url}/static/outputs/{output_filename}"
166
 
167
+ # ==========================================================
168
+ # โœ… Return result
169
+ # ==========================================================
170
  return JSONResponse({
171
  "html": generated_html,
172
  "link": public_url
173
  })
174
 
175
+
176
+ from fastapi.responses import FileResponse
177
+ from playwright_model import generate_ui_report
178
+
179
  @app.post("/generate_report")
180
  async def generate_report(request: Request):
181
+ """Generate a UI walkthrough report PDF using Playwright."""
182
  data = await request.json()
183
  app_url = data.get("url")
184
  if not app_url:
 
186
 
187
  output_pdf = os.path.join(OUTPUT_FOLDER, "UI_Report.pdf")
188
  await generate_ui_report(app_url, output_pdf)
189
+
190
  return FileResponse(output_pdf, filename="UI_Report.pdf", media_type="application/pdf")