Lilymutinda commited on
Commit
89f576f
·
verified ·
1 Parent(s): 202f09b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +35 -203
app.py CHANGED
@@ -1,221 +1,53 @@
1
- import os
2
  import gradio as gr
3
- from pypdf import PdfReader
4
 
5
- from langchain_text_splitters import RecursiveCharacterTextSplitter
6
- from langchain_community.vectorstores import FAISS
7
- from langchain_openai import OpenAIEmbeddings, ChatOpenAI
8
- from langchain_core.documents import Document
9
-
10
- APP_TITLE = "Election Archive"
11
- APP_TAGLINE = "What Happened Last Elections."
12
-
13
-
14
- CATEGORIES = ["PRESIDENTIAL VOTE", "POLITICANS", "POLITICAL PARTIES", "WHO VIED", "WOMEN IN POLITICS", "CAMPAIGN SLOGANS"]
15
-
16
- # ---------- Core logic ----------
17
- def read_pdf(file_path: str) -> str:
18
- reader = PdfReader(file_path)
19
- pages = []
20
- for p in reader.pages:
21
- pages.append(p.extract_text() or "")
22
- return "\n".join(pages)
23
-
24
- def build_index(files):
25
- if not files:
26
- return None, "Upload at least one PDF."
27
-
28
- docs = []
29
- for f in files:
30
- text = read_pdf(f.name)
31
- source_name = os.path.basename(f.name)
32
- docs.append(Document(page_content=text, metadata={"source": source_name}))
33
-
34
- splitter = RecursiveCharacterTextSplitter(chunk_size=900, chunk_overlap=120)
35
- chunks = splitter.split_documents(docs)
36
-
37
- embeddings = OpenAIEmbeddings()
38
- index = FAISS.from_documents(chunks, embeddings)
39
- return index, f"✅ Memory built: {len(files)} file(s), {len(chunks)} chunks."
40
-
41
- def answer_question(index, question):
42
- if index is None:
43
- return "Upload documents and click **Build Memory** first.", ""
44
-
45
- retriever = index.as_retriever(search_kwargs={"k": 4})
46
- hits = retriever.get_relevant_documents(question)
47
-
48
- context_blocks, citations = [], []
49
- for i, d in enumerate(hits, start=1):
50
- src = d.metadata.get("source", "unknown")
51
- snippet = " ".join(d.page_content.split())[:420]
52
- context_blocks.append(f"[Source {i}: {src}] {d.page_content[:900]}")
53
- citations.append(f"**{i}. {src}** — {snippet}…")
54
-
55
- llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.2)
56
-
57
- prompt = f"""
58
- You are an archive assistant for an election newsroom.
59
- Answer using ONLY the context below. If insufficient, say what is missing.
60
- Give a crisp, newsroom-ready response (bullets where useful).
61
- End with "Sources used: 1,2,3..." referencing source numbers.
62
-
63
- Question: {question}
64
-
65
- Context:
66
- {chr(10).join(context_blocks)}
67
- """.strip()
68
-
69
- response = llm.invoke(prompt).content
70
- return response, "\n\n".join(citations)
71
-
72
- # ---------- UI styling ----------
73
  CUSTOM_CSS = """
74
- :root{
75
- --bg:#ffffff;
76
- --text:#111827;
77
- --muted:#6b7280;
78
- --accent:#4f46e5;
79
- --line:#e5e7eb;
80
- --radius:16px;
81
- }
82
-
83
- .gradio-container{
84
- background: var(--bg) !important;
85
- color: var(--text) !important;
86
- font-family: Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial !important;
87
  }
88
 
89
- h1{
90
- font-size: 48px !important;
91
- font-weight: 800 !important;
92
- line-height: 1.1 !important;
93
- letter-spacing: -1px;
94
  }
95
 
96
- h3{
97
- font-size: 18px !important;
98
- color: var(--muted) !important;
99
- font-weight: 400 !important;
100
  }
101
 
102
- button{
103
- border-radius: 12px !important;
104
- padding: 12px 20px !important;
105
- font-weight: 600 !important;
106
  }
107
-
108
- .primary button{
109
- background: var(--accent) !important;
110
- color: white !important;
111
- border: none !important;
112
- }
113
-
114
- .soft button{
115
- background: white !important;
116
- border: 1px solid var(--line) !important;
117
- color: var(--text) !important;
118
- }
119
-
120
- .card{
121
- border: 1px solid var(--line);
122
- border-radius: var(--radius);
123
- padding: 24px;
124
- background: white;
125
- }
126
- """
127
- ARCHIVE_SVG = """
128
- <div id="archiveArt">
129
- <svg width="360" height="220" viewBox="0 0 360 220" fill="none" xmlns="http://www.w3.org/2000/svg" style="opacity:.95">
130
- <path d="M85 78L170 45L300 90L215 125L85 78Z" stroke="rgba(233,238,246,.65)" stroke-width="3"/>
131
- <path d="M85 78V170L215 215V125L85 78Z" stroke="rgba(233,238,246,.65)" stroke-width="3"/>
132
- <path d="M215 125L300 90V180L215 215V125Z" stroke="rgba(233,238,246,.65)" stroke-width="3"/>
133
- <path d="M120 90V150" stroke="rgba(233,238,246,.35)" stroke-width="3"/>
134
- <path d="M140 98V160" stroke="rgba(233,238,246,.28)" stroke-width="3"/>
135
- <path d="M160 105V168" stroke="rgba(233,238,246,.22)" stroke-width="3"/>
136
- <rect x="122" y="126" width="48" height="22" rx="6" stroke="rgba(233,238,246,.55)" stroke-width="3"/>
137
- <text x="26" y="205" fill="rgba(157,176,198,.95)" font-size="12" font-family="ui-sans-serif, system-ui">Archive box</text>
138
- </svg>
139
- </div>
140
- """
141
-
142
- HEADER_HTML = f"""
143
- <div id="topbar">
144
- <div>
145
- <div id="brand">{APP_TITLE}</div>
146
- <div id="tagline">{APP_TAGLINE}</div>
147
- </div>
148
- <div class="pill">Shielding the Storyteller • Archive Mode</div>
149
- </div>
150
  """
151
 
152
- # ---------- App ----------
153
- with gr.Blocks(css=CUSTOM_CSS, title=APP_TITLE) as demo:
154
 
155
- index_state = gr.State(None)
156
- last_category = gr.State("")
 
 
 
 
157
 
158
- with gr.Tabs():
159
- with gr.Tab("Explore"):
160
- with gr.Row(equal_height=True):
161
- with gr.Column(scale=6):
162
- with gr.Group(elem_classes=["card"]):
163
- gr.HTML(ARCHIVE_SVG)
164
- gr.Markdown(
165
- "Type a question, or pick a category to start exploring.",
166
- elem_classes=["small"],
167
- )
168
 
169
- with gr.Column(scale=8):
170
- with gr.Group(elem_classes=["card"]):
171
- query = gr.Textbox(
172
- label="",
173
- placeholder="Explore the archive… e.g. 'What shaped youth voter turnout in 2022?'",
174
- )
175
- with gr.Row():
176
- ask_btn = gr.Button("Search", elem_classes=["primary"])
177
- clear_btn = gr.Button("Clear", elem_classes=["soft"])
178
-
179
- answer = gr.Markdown(elem_id="results")
180
- sources = gr.Markdown(label="Sources (snippets)")
181
-
182
- with gr.Group(elem_classes=["card"]):
183
- gr.Markdown("**Or pick a category**")
184
- cat_buttons = []
185
- for c in CATEGORIES:
186
- b = gr.Button(c, elem_classes=["catBtn"])
187
- cat_buttons.append(b)
188
-
189
- def on_category_click(cat):
190
- # This just turns category into a query prompt (simple + effective)
191
- return f"Give me the most useful archive context on {cat.lower()} during Kenyan elections. Provide 5 bullet themes."
192
-
193
- for c, b in zip(CATEGORIES, cat_buttons):
194
- b.click(fn=on_category_click, inputs=[gr.State(c)], outputs=[query])
195
-
196
- def on_clear():
197
- return "", "", ""
198
-
199
- clear_btn.click(on_clear, outputs=[query, answer, sources])
200
- ask_btn.click(answer_question, inputs=[index_state, query], outputs=[answer, sources])
201
-
202
- with gr.Tab("Upload & Build Memory"):
203
  with gr.Row():
204
- with gr.Column(scale=7):
205
- with gr.Group(elem_classes=["card"]):
206
- uploader = gr.File(
207
- label="Upload PDFs (archive pack)",
208
- file_count="multiple",
209
- file_types=[".pdf"],
210
- )
211
- build_btn = gr.Button("Build Memory", elem_classes=["primary"])
212
- status = gr.Markdown("Upload documents, then click **Build Memory**.", elem_classes=["small"])
213
-
214
- with gr.Column(scale=7):
215
- with gr.Group(elem_classes=["card"]):
216
- gr.Markdown("**How to use**\n- Upload PDFs\n- Build Memory\n- Go to **Explore** and ask questions\n\nTip: Start with 10–30 key docs for fast demos.")
217
- gr.Markdown("**Example questions**\n- What issues dominated 2022 coverage?\n- Summarize history of petitions in a constituency\n- What patterns repeat across election cycles?")
218
-
219
- build_btn.click(build_index, inputs=[uploader], outputs=[index_state, status])
220
 
221
  demo.launch()
 
 
1
  import gradio as gr
 
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  CUSTOM_CSS = """
4
+ .gradio-container {
5
+ background: white !important;
6
+ font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto !important;
 
 
 
 
 
 
 
 
 
 
7
  }
8
 
9
+ h1 {
10
+ font-size: 52px !important;
11
+ font-weight: 800 !important;
12
+ line-height: 1.1 !important;
13
+ letter-spacing: -1px;
14
  }
15
 
16
+ h3 {
17
+ color: #6b7280 !important;
18
+ font-weight: 400 !important;
 
19
  }
20
 
21
+ button {
22
+ border-radius: 12px !important;
23
+ padding: 12px 24px !important;
24
+ font-weight: 600 !important;
25
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  """
27
 
28
+ with gr.Blocks(css=CUSTOM_CSS) as demo:
 
29
 
30
+ with gr.Row():
31
+ with gr.Column(scale=6):
32
+ gr.Markdown("""
33
+ # How To Keep
34
+ # Your Files
35
+ # Organized
36
 
37
+ ### Turn your newsroom archive into a searchable memory engine.
38
+ ### Upload documents. Ask questions. Get answers with sources.
39
+ """)
 
 
 
 
 
 
 
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  with gr.Row():
42
+ gr.Button("Get Started", variant="primary")
43
+ gr.Button("Upload Archive")
44
+
45
+ with gr.Column(scale=6):
46
+ gr.HTML(
47
+ """
48
+ <img src="https://undraw.co/api/illustrations/organizing_files.svg"
49
+ style="width:100%; max-width:500px; display:block; margin:auto;">
50
+ """
51
+ )
 
 
 
 
 
 
52
 
53
  demo.launch()