GodsDevProject commited on
Commit
f768e52
ยท
verified ยท
1 Parent(s): f9878e1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +73 -86
app.py CHANGED
@@ -27,9 +27,7 @@ except Exception:
27
  # CONFIG / FEATURE GATES
28
  # ======================================================
29
 
30
- ENABLE_SEMANTIC = False # Opt-in only
31
- ENABLE_PDF_EXPORT = True
32
- ENABLE_HEATMAP = True
33
  ENABLE_PDF_THUMBNAILS = True
34
  ENABLE_ENTITY_GRAPHS = True
35
  ENABLE_TIMELINES = True
@@ -47,7 +45,6 @@ class FOIAAdapter:
47
  start = time.time()
48
  url = self.search_url.format(q=quote_plus(query))
49
  latency = round(time.time() - start, 3)
50
-
51
  return [{
52
  "agency": self.agency,
53
  "title": f"{self.agency} FOIA Search Results",
@@ -100,7 +97,6 @@ class StubAdapter(FOIAAdapter):
100
  def __init__(self, agency):
101
  self.agency = agency
102
  self.search_url = ""
103
-
104
  def search(self, query):
105
  return [{
106
  "agency": self.agency,
@@ -158,7 +154,6 @@ def run_search(query, include_stubs, semantic_mode):
158
  coverage[r["agency"]] += 1
159
  if r["is_live"]:
160
  LAST_LIVE_RECORDS.append(r)
161
-
162
  rows.append([
163
  r["agency"],
164
  "LIVE" if r["is_live"] else "STUB",
@@ -181,17 +176,12 @@ def run_search(query, include_stubs, semantic_mode):
181
  # ======================================================
182
 
183
  def build_pdf_thumbnail_gallery():
184
- if not ENABLE_PDF_THUMBNAILS:
185
- return "<i>PDF previews disabled</i>"
186
-
187
  cards = []
188
-
189
  for r in LAST_LIVE_RECORDS:
190
  url = r["url"]
191
- if not url or not url.lower().endswith(".pdf"):
192
  continue
193
-
194
- card = f"""
195
  <div style="border:1px solid #ddd;border-radius:8px;padding:12px;margin-bottom:16px;">
196
  <b>{r['agency']} โ€” {r['title']}</b><br><br>
197
  <iframe src="{url}" width="100%" height="220"></iframe>
@@ -202,13 +192,74 @@ def build_pdf_thumbnail_gallery():
202
  <a href="#" onclick="askAI('{r['agency']}', '{r['title']}', '{url}')">Ask AI</a>
203
  </div>
204
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  """
206
- cards.append(card)
207
 
208
- return "".join(cards) if cards else "<i>No PDF documents found.</i>"
 
 
 
209
 
210
  # ======================================================
211
- # CLIENT JS
212
  # ======================================================
213
 
214
  JS_HELPERS = """
@@ -221,71 +272,16 @@ function shareDoc(url) {
221
  alert("Link copied to clipboard");
222
  }
223
  }
224
-
225
- function askAI(agency, title, url) {
226
- alert(
227
- "AI Summary (Preview)\\n\\n" +
228
- "Agency: " + agency + "\\n" +
229
- "Title: " + title + "\\n\\n" +
230
- "Semantic analysis, entities, and citations can be enabled in opt-in AI mode."
231
- );
232
  }
233
  </script>
234
  """
235
 
236
- # ======================================================
237
- # ENTITY GRAPH + TIMELINE
238
- # ======================================================
239
-
240
- def build_entity_graph():
241
- domains = Counter(urlparse(r["url"]).netloc for r in LAST_LIVE_RECORDS)
242
- return go.Figure([go.Bar(x=list(domains.keys()), y=list(domains.values()))])
243
-
244
- def build_timeline():
245
- dates = Counter(r["timestamp"][:10] for r in LAST_LIVE_RECORDS)
246
- return go.Figure([go.Bar(x=list(dates.keys()), y=list(dates.values()))])
247
-
248
- # ======================================================
249
- # FOIA REQUEST GENERATOR
250
- # ======================================================
251
-
252
- def generate_foia_request(requester, description):
253
- buffer = io.BytesIO()
254
- doc = SimpleDocTemplate(buffer)
255
- styles = getSampleStyleSheet()
256
- story = []
257
-
258
- story.append(Paragraph("<b>Freedom of Information Act Request</b>", styles["Title"]))
259
- story.append(Spacer(1, 12))
260
- story.append(Paragraph(f"<b>Requester:</b> {requester}", styles["Normal"]))
261
- story.append(Spacer(1, 8))
262
- story.append(Paragraph("<b>Description:</b>", styles["Normal"]))
263
- story.append(Paragraph(description, styles["Normal"]))
264
- story.append(Spacer(1, 12))
265
-
266
- agencies = ", ".join(sorted({r["agency"] for r in LAST_LIVE_RECORDS}))
267
- story.append(Paragraph(f"<b>Agencies:</b> {agencies}", styles["Normal"]))
268
-
269
- doc.build(story)
270
- buffer.seek(0)
271
- return buffer
272
-
273
- # ======================================================
274
- # UI
275
- # ======================================================
276
-
277
  with gr.Blocks(title="Federal FOIA Intelligence Search") as app:
278
  gr.HTML(JS_HELPERS)
279
 
280
- gr.Markdown(
281
- """
282
- # ๐Ÿ›๏ธ Federal FOIA Intelligence Search
283
- **Public FOIA Electronic Reading Rooms Only**
284
-
285
- โœ” LIVE = court-ready
286
- โš  STUB = informational only
287
- """
288
- )
289
 
290
  query = gr.Textbox(label="Search FOIA Libraries")
291
  include_stubs = gr.Checkbox(label="Include Extended Coverage (STUB)", value=False)
@@ -310,17 +306,8 @@ with gr.Blocks(title="Federal FOIA Intelligence Search") as app:
310
  gr.Markdown("## ๐Ÿ“„ PDF Document Previews")
311
  pdf_gallery.render()
312
 
313
- gr.Markdown("## FOIA Request Generator")
314
- requester = gr.Textbox(label="Your Name / Organization")
315
- description = gr.Textbox(label="Describe the records requested", lines=4)
316
-
317
- gr.Button("Generate FOIA Request PDF").click(
318
- generate_foia_request,
319
- inputs=[requester, description],
320
- outputs=gr.File()
321
- )
322
-
323
- gr.Button("Show Entity Graph").click(build_entity_graph, outputs=gr.Plot())
324
- gr.Button("Show Timeline").click(build_timeline, outputs=gr.Plot())
325
 
326
  app.launch()
 
27
  # CONFIG / FEATURE GATES
28
  # ======================================================
29
 
30
+ ENABLE_SEMANTIC = False
 
 
31
  ENABLE_PDF_THUMBNAILS = True
32
  ENABLE_ENTITY_GRAPHS = True
33
  ENABLE_TIMELINES = True
 
45
  start = time.time()
46
  url = self.search_url.format(q=quote_plus(query))
47
  latency = round(time.time() - start, 3)
 
48
  return [{
49
  "agency": self.agency,
50
  "title": f"{self.agency} FOIA Search Results",
 
97
  def __init__(self, agency):
98
  self.agency = agency
99
  self.search_url = ""
 
100
  def search(self, query):
101
  return [{
102
  "agency": self.agency,
 
154
  coverage[r["agency"]] += 1
155
  if r["is_live"]:
156
  LAST_LIVE_RECORDS.append(r)
 
157
  rows.append([
158
  r["agency"],
159
  "LIVE" if r["is_live"] else "STUB",
 
176
  # ======================================================
177
 
178
  def build_pdf_thumbnail_gallery():
 
 
 
179
  cards = []
 
180
  for r in LAST_LIVE_RECORDS:
181
  url = r["url"]
182
+ if not url.lower().endswith(".pdf"):
183
  continue
184
+ cards.append(f"""
 
185
  <div style="border:1px solid #ddd;border-radius:8px;padding:12px;margin-bottom:16px;">
186
  <b>{r['agency']} โ€” {r['title']}</b><br><br>
187
  <iframe src="{url}" width="100%" height="220"></iframe>
 
192
  <a href="#" onclick="askAI('{r['agency']}', '{r['title']}', '{url}')">Ask AI</a>
193
  </div>
194
  </div>
195
+ """)
196
+ return "".join(cards) if cards else "<i>No PDF documents found.</i>"
197
+
198
+ # ======================================================
199
+ # JOURNALIST ZIP EXPORT (LINKS + CITATIONS ONLY)
200
+ # ======================================================
201
+
202
+ def generate_journalist_zip():
203
+ if not LAST_LIVE_RECORDS:
204
+ return None
205
+
206
+ mem = io.BytesIO()
207
+ with zipfile.ZipFile(mem, "w", zipfile.ZIP_DEFLATED) as z:
208
+ z.writestr(
209
+ "README.txt",
210
+ "Public FOIA sources only.\n"
211
+ "This ZIP contains links and citations only.\n"
212
+ "No documents are hosted or redistributed.\n"
213
+ )
214
+
215
+ z.writestr(
216
+ "citations.txt",
217
+ "\n\n".join(bluebook_full(r) for r in LAST_LIVE_RECORDS)
218
+ )
219
+
220
+ csv = "agency,title,url,retrieved\n"
221
+ for r in LAST_LIVE_RECORDS:
222
+ csv += f"{r['agency']},{r['title']},{r['url']},{r['timestamp']}\n"
223
+ z.writestr("links.csv", csv)
224
+
225
+ pdf_links = "\n".join(
226
+ r["url"] for r in LAST_LIVE_RECORDS if r["url"].lower().endswith(".pdf")
227
+ )
228
+ z.writestr("pdf_links.txt", pdf_links)
229
+
230
+ mem.seek(0)
231
+ return mem
232
+
233
+ # ======================================================
234
+ # PUBLIC SHAREABLE RESULT PAGE (STATIC HTML)
235
+ # ======================================================
236
+
237
+ def generate_share_page():
238
+ if not LAST_LIVE_RECORDS:
239
+ return None
240
+
241
+ html = """
242
+ <html><head><title>FOIA Search Results</title></head><body>
243
+ <h1>Federal FOIA Search Results</h1>
244
+ <p>Public electronic reading rooms only.</p><hr>
245
+ """
246
+
247
+ for r in LAST_LIVE_RECORDS:
248
+ html += f"""
249
+ <h3>{r['agency']} โ€” {r['title']}</h3>
250
+ <p><a href="{r['url']}" target="_blank">{r['url']}</a></p>
251
+ <p><b>Citation:</b> {bluebook_full(r)}</p>
252
+ <p><b>Hash:</b> {citation_hash(r)}</p>
253
+ <hr>
254
  """
 
255
 
256
+ html += "</body></html>"
257
+
258
+ buf = io.BytesIO(html.encode("utf-8"))
259
+ return buf
260
 
261
  # ======================================================
262
+ # UI
263
  # ======================================================
264
 
265
  JS_HELPERS = """
 
272
  alert("Link copied to clipboard");
273
  }
274
  }
275
+ function askAI(a,t,u){
276
+ alert("AI analysis placeholder for: " + t);
 
 
 
 
 
 
277
  }
278
  </script>
279
  """
280
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
  with gr.Blocks(title="Federal FOIA Intelligence Search") as app:
282
  gr.HTML(JS_HELPERS)
283
 
284
+ gr.Markdown("# ๐Ÿ›๏ธ Federal FOIA Intelligence Search")
 
 
 
 
 
 
 
 
285
 
286
  query = gr.Textbox(label="Search FOIA Libraries")
287
  include_stubs = gr.Checkbox(label="Include Extended Coverage (STUB)", value=False)
 
306
  gr.Markdown("## ๐Ÿ“„ PDF Document Previews")
307
  pdf_gallery.render()
308
 
309
+ gr.Markdown("## ๐Ÿ—‚ Journalist Tools")
310
+ gr.Button("Download Journalist ZIP").click(generate_journalist_zip, outputs=gr.File())
311
+ gr.Button("Generate Shareable Result Page").click(generate_share_page, outputs=gr.File())
 
 
 
 
 
 
 
 
 
312
 
313
  app.launch()