Krrish-shetty commited on
Commit
402a682
·
verified ·
1 Parent(s): 1b2c003

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +567 -142
app.py CHANGED
@@ -1,6 +1,7 @@
1
  import os
2
  import time
3
  import hashlib
 
4
  from dotenv import load_dotenv
5
  import streamlit as st
6
  from langchain_community.vectorstores import FAISS
@@ -74,6 +75,22 @@ st.markdown("""
74
  max-width: 250px;
75
  height: auto;
76
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  </style>
78
  """, unsafe_allow_html=True)
79
 
@@ -90,9 +107,9 @@ if "authenticated" not in st.session_state:
90
 
91
  if st.session_state.role is None:
92
  st.markdown("<h2 style='text-align: center;'>Who are you?</h2>", unsafe_allow_html=True)
93
- col1, col2, col3 = st.columns([1, 2, 1])
94
  with col2:
95
- col_a, col_b = st.columns(2)
96
  with col_a:
97
  if st.button("🧑 I am a Civilian"):
98
  st.session_state.role = "civilian"
@@ -102,9 +119,13 @@ if st.session_state.role is None:
102
  if st.button("⚖️ I am a Court Stakeholder"):
103
  st.session_state.role = "stakeholder"
104
  st.rerun()
 
 
 
 
105
 
106
- if st.session_state.role == "stakeholder" and not st.session_state.authenticated:
107
- st.markdown("### 🔐 Stakeholder Login")
108
  username = st.text_input("Username")
109
  password = st.text_input("Password", type="password")
110
  if st.button("Login"):
@@ -122,16 +143,37 @@ if st.session_state.role and (st.session_state.role == "civilian" or st.session_
122
  st.rerun()
123
 
124
  tabs = ["📘 LawGPT"]
125
- if st.session_state.role == "stakeholder":
 
 
 
126
  tabs.extend(["📝 Document Signer", "🔍 Verify Document"])
127
 
128
  selected_tab = st.tabs(tabs)
129
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  if "📘 LawGPT" in tabs:
131
  with selected_tab[0]:
132
  st.markdown("## 💬 Your Legal AI Lawyer")
133
  st.markdown("### Ask any legal question related to the Indian Penal Code (IPC)")
134
- st.markdown("Questions might be of types like: Suppose a 16 year old is drinking and driving , and hit a pedestrian on the road . what are the possible case laws imposed and give any one previous court decisions on the same. ")
135
 
136
  def reset_conversation():
137
  st.session_state.messages = []
@@ -144,14 +186,6 @@ if st.session_state.role and (st.session_state.role == "civilian" or st.session_
144
  k=2, memory_key="chat_history", return_messages=True
145
  )
146
 
147
- embeddings = HuggingFaceEmbeddings(
148
- model_name="nomic-ai/nomic-embed-text-v1",
149
- model_kwargs={"trust_remote_code": True, "revision": "289f532e14dbbbd5a04753fa58739e9ba766f3c7"}
150
- )
151
-
152
- db = FAISS.load_local("ipc_vector_db", embeddings, allow_dangerous_deserialization=True)
153
- db_retriever = db.as_retriever(search_type="similarity", search_kwargs={"k": 4})
154
-
155
  prompt_template = """<s>[INST]You are a legal chatbot that answers questions about the Indian Penal Code (IPC).
156
  Provide clear, concise, and accurate responses based on context and user's question.
157
  Avoid extra details or assumptions. Focus only on legal information.
@@ -168,13 +202,6 @@ ANSWER:
168
  input_variables=["context", "question", "chat_history"]
169
  )
170
 
171
- llm = Together(
172
- model="mistralai/Mistral-7B-Instruct-v0.2",
173
- temperature=0.5,
174
- max_tokens=1024,
175
- together_api_key=os.getenv("TOGETHER_API_KEY")
176
- )
177
-
178
  qa = ConversationalRetrievalChain.from_llm(
179
  llm=llm,
180
  memory=st.session_state.memory,
@@ -206,10 +233,408 @@ ANSWER:
206
  full_response += chunk
207
  time.sleep(0.02)
208
  message_placeholder.markdown(full_response + " ▌")
 
209
  st.session_state.messages.append({"role": "assistant", "content": result["answer"]})
210
 
211
  st.button("🔄 Reset Chat", on_click=reset_conversation)
212
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  if st.session_state.role == "stakeholder":
214
  if "📝 Document Signer" in tabs:
215
  with selected_tab[1]:
@@ -249,130 +674,130 @@ ANSWER:
249
  with selected_tab[2]:
250
  st.markdown("## 🔍 Verify Document Authentication")
251
  st.markdown("Upload any document to verify its integrity and authenticity.")
252
-
253
- verify_file = st.file_uploader("Upload PDF for verification", type=["pdf"], key="verify")
254
-
255
- if verify_file:
256
- content = verify_file.read()
257
-
258
- try:
259
- # Basic PDF validation
260
- pdf = PdfReader(BytesIO(content))
261
-
262
- # Extract text to look for signature markers
263
- all_text = ""
264
- for page in pdf.pages:
265
- all_text += page.extract_text() or ""
266
-
267
- # Check for digital signature information
268
- has_signature_text = any(sig_text in all_text.lower() for sig_text in
269
- ["signed by:", "digital signature", "electronic signature"])
270
 
271
- # Create document fingerprint/hash
272
- doc_hash = hashlib.sha256(content).hexdigest()
273
 
274
- # Calculate metadata integrity
275
- metadata_valid = True
276
- if pdf.metadata:
277
- try:
278
- # Check for suspicious metadata modifications
279
- creation_date = pdf.metadata.get('/CreationDate', '')
280
- mod_date = pdf.metadata.get('/ModDate', '')
281
- if mod_date and creation_date:
282
- metadata_valid = mod_date >= creation_date
283
- except:
284
- metadata_valid = False
285
-
286
- # Check for content consistency
287
- content_consistent = True
288
-
289
- col1, col2 = st.columns(2)
290
-
291
- with col1:
292
- st.subheader("Document Analysis")
293
- st.info(f"📄 Pages: {len(pdf.pages)}")
294
- st.info(f"🔒 Contains signature markers: {'Yes' if has_signature_text else 'No'}")
295
-
296
- # Display hash for document tracking
297
- st.code(f"Document Hash: {doc_hash[:16]}...{doc_hash[-16:]}")
298
-
299
- # Document size and characteristics
300
- file_size = len(content) / 1024 # KB
301
- st.info(f"📦 File size: {file_size:.2f} KB")
302
-
303
- with col2:
304
- st.subheader("Verification Results")
305
 
306
- # Case 1: Document has signature markers
307
- if has_signature_text:
308
- if metadata_valid and content_consistent:
309
- st.success("✅ Document Status: VERIFIED AUTHENTIC")
310
- st.markdown("- Valid PDF structure")
311
- st.markdown("- Signature information detected")
312
- st.markdown("- No tampering indicators found")
313
- st.markdown("- Metadata consistency verified")
314
- else:
315
- st.warning("⚠️ Document Status: POTENTIALLY MODIFIED")
316
- st.markdown("- Valid PDF structure")
317
- st.markdown("- Signature information found")
318
- st.markdown("- ❌ Some integrity checks failed")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
 
320
- if not metadata_valid:
321
- st.markdown("- Metadata inconsistencies detected")
 
322
 
323
- # Display signature extraction if present
324
- signature_line = next((line for line in all_text.split('\n') if "signed by:" in line.lower()), "")
325
- if signature_line:
326
- st.info(f"📝 {signature_line.strip()}")
327
-
328
- # Case 2: Document without signatures
329
- else:
330
- if metadata_valid and content_consistent:
331
- st.success("✅ Document Status: VALID DOCUMENT")
332
- st.markdown("- ✓ Valid PDF structure")
333
- st.markdown("- ✓ Content integrity verified")
334
- st.markdown("- ✓ No tampering indicators found")
335
- st.markdown("- ℹ️ No signature information found (this is not an error)")
336
- else:
337
- st.warning("⚠️ Document Status: POTENTIALLY MODIFIED")
338
- st.markdown("- ✓ Valid PDF structure")
339
- st.markdown("- ❌ Some integrity checks failed")
340
 
341
- if not metadata_valid:
342
- st.markdown("- ❌ Metadata inconsistencies detected")
343
-
344
- # Advanced options
345
- with st.expander("🔬 Advanced Verification Details"):
346
- st.markdown("### Document Metadata")
347
- if pdf.metadata:
348
- for key, value in pdf.metadata.items():
349
- if key and value and key not in ('/CreationDate', '/ModDate'):
350
- st.text(f"{key}: {value}")
351
- else:
352
- st.text("No metadata available")
353
-
354
- st.markdown("### Integrity Timeline")
355
- st.text(f"Creation Date: {pdf.metadata.get('/CreationDate', 'Not available')}")
356
- st.text(f"Last Modified: {pdf.metadata.get('/ModDate', 'Not available')}")
357
-
358
- # Additional verification for content integrity
359
- st.markdown("### Content Analysis")
360
- fonts_used = set()
361
- image_count = 0
362
- for page in pdf.pages:
363
- if "/Font" in page["/Resources"]:
364
- for font in page["/Resources"]["/Font"]:
365
- fonts_used.add(str(font))
366
- if "/XObject" in page["/Resources"]:
367
- for obj in page["/Resources"]["/XObject"]:
368
- if "/Subtype" in page["/Resources"]["/XObject"][obj] and \
369
- page["/Resources"]["/XObject"][obj]["/Subtype"] == "/Image":
370
- image_count += 1
371
-
372
- st.text(f"Fonts detected: {len(fonts_used)}")
373
- st.text(f"Images detected: {image_count}")
374
-
375
- except Exception as e:
376
- st.error(f"❌ Document Status: INVALID OR CORRUPTED")
377
- st.markdown(f"Error: Could not process the document properly. The file may be corrupted or not a valid PDF.")
378
- st.markdown(f"Technical details: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
  import time
3
  import hashlib
4
+ import json
5
  from dotenv import load_dotenv
6
  import streamlit as st
7
  from langchain_community.vectorstores import FAISS
 
75
  max-width: 250px;
76
  height: auto;
77
  }
78
+ .judge-badge {
79
+ background-color: #991b1b;
80
+ color: white;
81
+ padding: 5px 10px;
82
+ border-radius: 12px;
83
+ font-weight: 600;
84
+ display: inline-block;
85
+ margin-bottom: 10px;
86
+ }
87
+ .judgment-card {
88
+ background-color: #1e293b;
89
+ border-radius: 8px;
90
+ padding: 20px;
91
+ margin-bottom: 20px;
92
+ border-left: 4px solid #991b1b;
93
+ }
94
  </style>
95
  """, unsafe_allow_html=True)
96
 
 
107
 
108
  if st.session_state.role is None:
109
  st.markdown("<h2 style='text-align: center;'>Who are you?</h2>", unsafe_allow_html=True)
110
+ col1, col2, col3 = st.columns([1, 3, 1])
111
  with col2:
112
+ col_a, col_b, col_c = st.columns(3)
113
  with col_a:
114
  if st.button("🧑 I am a Civilian"):
115
  st.session_state.role = "civilian"
 
119
  if st.button("⚖️ I am a Court Stakeholder"):
120
  st.session_state.role = "stakeholder"
121
  st.rerun()
122
+ with col_c:
123
+ if st.button("👨‍⚖️ I am a Judge"):
124
+ st.session_state.role = "judge"
125
+ st.rerun()
126
 
127
+ if (st.session_state.role == "stakeholder" or st.session_state.role == "judge") and not st.session_state.authenticated:
128
+ st.markdown(f"### 🔐 {'Judge' if st.session_state.role == 'judge' else 'Stakeholder'} Login")
129
  username = st.text_input("Username")
130
  password = st.text_input("Password", type="password")
131
  if st.button("Login"):
 
143
  st.rerun()
144
 
145
  tabs = ["📘 LawGPT"]
146
+
147
+ if st.session_state.role == "judge":
148
+ tabs.extend(["👨‍⚖️ Judge Console", "📜 Previous Judgments"])
149
+ elif st.session_state.role == "stakeholder":
150
  tabs.extend(["📝 Document Signer", "🔍 Verify Document"])
151
 
152
  selected_tab = st.tabs(tabs)
153
 
154
+ # Load embeddings and DB for all roles
155
+ embeddings = HuggingFaceEmbeddings(
156
+ model_name="nomic-ai/nomic-embed-text-v1",
157
+ model_kwargs={"trust_remote_code": True, "revision": "289f532e14dbbbd5a04753fa58739e9ba766f3c7"}
158
+ )
159
+
160
+ db = FAISS.load_local("ipc_vector_db", embeddings, allow_dangerous_deserialization=True)
161
+ db_retriever = db.as_retriever(search_type="similarity", search_kwargs={"k": 4})
162
+
163
+ # Common LLM setup
164
+ llm = Together(
165
+ model="mistralai/Mistral-7B-Instruct-v0.2",
166
+ temperature=0.5,
167
+ max_tokens=1024,
168
+ together_api_key=os.getenv("TOGETHER_API_KEY")
169
+ )
170
+
171
+ # LawGPT Tab for all roles
172
  if "📘 LawGPT" in tabs:
173
  with selected_tab[0]:
174
  st.markdown("## 💬 Your Legal AI Lawyer")
175
  st.markdown("### Ask any legal question related to the Indian Penal Code (IPC)")
176
+ st.markdown("Questions might be of types like: Suppose a 16 year old is drinking and driving, and hit a pedestrian on the road. What are the possible case laws imposed and give any one previous court decisions on the same.")
177
 
178
  def reset_conversation():
179
  st.session_state.messages = []
 
186
  k=2, memory_key="chat_history", return_messages=True
187
  )
188
 
 
 
 
 
 
 
 
 
189
  prompt_template = """<s>[INST]You are a legal chatbot that answers questions about the Indian Penal Code (IPC).
190
  Provide clear, concise, and accurate responses based on context and user's question.
191
  Avoid extra details or assumptions. Focus only on legal information.
 
202
  input_variables=["context", "question", "chat_history"]
203
  )
204
 
 
 
 
 
 
 
 
205
  qa = ConversationalRetrievalChain.from_llm(
206
  llm=llm,
207
  memory=st.session_state.memory,
 
233
  full_response += chunk
234
  time.sleep(0.02)
235
  message_placeholder.markdown(full_response + " ▌")
236
+ message_placeholder.markdown(full_response)
237
  st.session_state.messages.append({"role": "assistant", "content": result["answer"]})
238
 
239
  st.button("🔄 Reset Chat", on_click=reset_conversation)
240
 
241
+ # Judge Console Tab
242
+ if st.session_state.role == "judge":
243
+ # Initialize judgment storage
244
+ if "judgments" not in st.session_state:
245
+ st.session_state.judgments = []
246
+
247
+ # Load existing judgments if file exists
248
+ try:
249
+ with open("judgments.json", "r") as f:
250
+ st.session_state.judgments = json.load(f)
251
+ except (FileNotFoundError, json.JSONDecodeError):
252
+ pass
253
+
254
+ with selected_tab[1]:
255
+ st.markdown("## 👨‍⚖️ Judge's Decision Console")
256
+ st.markdown("### Enter case details for analysis and judgment")
257
+
258
+ # Input fields for case details
259
+ st.subheader("Case Information")
260
+ case_number = st.text_input("Case Number/ID")
261
+ case_title = st.text_input("Case Title")
262
+ plaintiff = st.text_input("Plaintiff/Prosecution")
263
+ defendant = st.text_input("Defendant/Accused")
264
+
265
+ # Case facts and context
266
+ st.subheader("Case Details")
267
+ case_facts = st.text_area("Enter detailed facts of the case:", height=200)
268
+
269
+ # Get relevant laws/sections that apply
270
+ relevant_laws = st.text_area("Relevant IPC Sections (if known):",
271
+ placeholder="e.g. Section 302, Section 376, etc.")
272
+
273
+ col1, col2 = st.columns(2)
274
+ with col1:
275
+ case_type = st.selectbox("Case Type", [
276
+ "Criminal", "Civil", "Family", "Property", "Cyber Crime",
277
+ "Corporate", "Intellectual Property", "Other"
278
+ ])
279
+ with col2:
280
+ case_priority = st.select_slider("Case Priority",
281
+ options=["Low", "Medium", "High", "Urgent"])
282
+
283
+ if st.button("Generate Judgment"):
284
+ if not case_facts:
285
+ st.error("Please enter the case facts to generate a judgment.")
286
+ else:
287
+ with st.status("Analyzing case and formulating judgment...", expanded=True):
288
+ # Create a prompt for legal judgment
289
+ judge_prompt_template = """<s>[INST]You are an experienced Indian judge making a legal judgment based on the Indian Penal Code (IPC).
290
+ Review the case details and provide a comprehensive legal judgment.
291
+
292
+ CASE NUMBER: {case_number}
293
+ CASE TITLE: {case_title}
294
+ PLAINTIFF/PROSECUTION: {plaintiff}
295
+ DEFENDANT/ACCUSED: {defendant}
296
+ CASE TYPE: {case_type}
297
+ CASE FACTS: {case_facts}
298
+ RELEVANT IPC SECTIONS: {relevant_laws}
299
+
300
+ Your judgment should follow this structure:
301
+ 1. Summary of the case
302
+ 2. Facts of the case
303
+ 3. Legal issues involved
304
+ 4. Analysis of applicable laws and precedents
305
+ 5. Reasoning and findings
306
+ 6. Final judgment and orders
307
+ 7. Any remedies or penalties imposed
308
+
309
+ Be impartial, consider only facts and relevant laws, and make a fair judgment.
310
+ </s>[INST]"""
311
+
312
+ judge_prompt = PromptTemplate(
313
+ template=judge_prompt_template,
314
+ input_variables=["case_number", "case_title", "plaintiff", "defendant",
315
+ "case_type", "case_facts", "relevant_laws"]
316
+ )
317
+
318
+ # Format prompt with case details
319
+ formatted_prompt = judge_prompt.format(
320
+ case_number=case_number if case_number else "Unassigned",
321
+ case_title=case_title if case_title else "Unnamed Case",
322
+ plaintiff=plaintiff if plaintiff else "Unspecified",
323
+ defendant=defendant if defendant else "Unspecified",
324
+ case_type=case_type,
325
+ case_facts=case_facts,
326
+ relevant_laws=relevant_laws if relevant_laws else "To be determined"
327
+ )
328
+
329
+ # Generate judgment using LLM
330
+ judgment_result = llm.invoke(formatted_prompt)
331
+
332
+ # Save judgment to session state
333
+ timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
334
+ judgment_data = {
335
+ "id": hashlib.md5(f"{case_title}{timestamp}".encode()).hexdigest()[:8],
336
+ "case_number": case_number if case_number else "Unassigned",
337
+ "case_title": case_title if case_title else "Unnamed Case",
338
+ "plaintiff": plaintiff,
339
+ "defendant": defendant,
340
+ "case_type": case_type,
341
+ "priority": case_priority,
342
+ "facts": case_facts,
343
+ "relevant_laws": relevant_laws,
344
+ "judgment": judgment_result,
345
+ "timestamp": timestamp
346
+ }
347
+
348
+ st.session_state.judgments.append(judgment_data)
349
+
350
+ # Save judgments to file
351
+ with open("judgments.json", "w") as f:
352
+ json.dump(st.session_state.judgments, f)
353
+
354
+ # Display the judgment
355
+ st.markdown("### Judgment Generated")
356
+ with st.container():
357
+ st.markdown(f"<div class='judgment-card'>", unsafe_allow_html=True)
358
+ st.markdown(f"<div class='judge-badge'>JUDGMENT #{judgment_data['id']}</div>", unsafe_allow_html=True)
359
+ st.markdown(f"**Case**: {judgment_data['case_title']}")
360
+ st.markdown(f"**Date**: {judgment_data['timestamp']}")
361
+ st.markdown("---")
362
+ st.markdown(judgment_data['judgment'])
363
+ st.markdown("</div>", unsafe_allow_html=True)
364
+
365
+ # Download judgment as PDF
366
+ if st.button("📥 Download Judgment as PDF"):
367
+ # Generate PDF with ReportLab
368
+ pdf_buffer = BytesIO()
369
+ c = canvas.Canvas(pdf_buffer, pagesize=letter)
370
+ width, height = letter
371
+
372
+ # Header
373
+ c.setFont("Helvetica-Bold", 16)
374
+ c.drawString(72, height - 72, f"JUDGMENT #{judgment_data['id']}")
375
+
376
+ # Case details
377
+ c.setFont("Helvetica-Bold", 12)
378
+ c.drawString(72, height - 100, f"Case: {judgment_data['case_title']}")
379
+ c.setFont("Helvetica", 10)
380
+ c.drawString(72, height - 115, f"Case Number: {judgment_data['case_number']}")
381
+ c.drawString(72, height - 130, f"Date: {judgment_data['timestamp']}")
382
+ c.drawString(72, height - 145, f"Plaintiff/Prosecution: {judgment_data['plaintiff']}")
383
+ c.drawString(72, height - 160, f"Defendant/Accused: {judgment_data['defendant']}")
384
+ c.drawString(72, height - 175, f"Case Type: {judgment_data['case_type']}")
385
+
386
+ # Line separator
387
+ c.line(72, height - 190, width - 72, height - 190)
388
+
389
+ # Format judgment text
390
+ judgment_text = judgment_data['judgment']
391
+ text_object = c.beginText(72, height - 210)
392
+ text_object.setFont("Times-Roman", 10)
393
+
394
+ # Wrap text to fit page
395
+ lines = []
396
+ for paragraph in judgment_text.split('\n\n'):
397
+ # Replace single newlines with spaces for proper wrapping
398
+ paragraph = paragraph.replace('\n', ' ')
399
+
400
+ # Simple word wrap
401
+ words = paragraph.split()
402
+ line = ''
403
+ for word in words:
404
+ if len(line + ' ' + word) <= 90: # character limit per line
405
+ line += ' ' + word if line else word
406
+ else:
407
+ lines.append(line)
408
+ line = word
409
+ if line:
410
+ lines.append(line)
411
+
412
+ # Add blank line between paragraphs
413
+ lines.append('')
414
+
415
+ # Add lines to text object with pagination
416
+ line_height = 12
417
+ lines_per_page = 50
418
+ current_line = 0
419
+
420
+ for line in lines:
421
+ if current_line >= lines_per_page:
422
+ c.drawText(text_object)
423
+ c.showPage()
424
+ text_object = c.beginText(72, height - 72)
425
+ text_object.setFont("Times-Roman", 10)
426
+ current_line = 0
427
+
428
+ text_object.textLine(line)
429
+ current_line += 1
430
+
431
+ c.drawText(text_object)
432
+
433
+ # Add footer with page numbers
434
+ c.saveState()
435
+ c.setFont("Helvetica", 8)
436
+ c.drawString(width/2 - 40, 30, f"Generated by LawGPT Judge")
437
+ c.restoreState()
438
+
439
+ # Final page with signature
440
+ c.showPage()
441
+ c.setFont("Helvetica-Bold", 12)
442
+ c.drawString(72, height - 100, "OFFICIAL JUDGMENT")
443
+ c.setFont("Helvetica", 10)
444
+ c.drawString(72, height - 130, f"Case #{judgment_data['id']} - {judgment_data['case_title']}")
445
+ c.drawString(72, height - 150, f"Date: {judgment_data['timestamp']}")
446
+
447
+ # Add barcode for authenticity
448
+ barcode = code128.Code128(f"JUDGMENT-{judgment_data['id']}", barHeight=10 * mm, barWidth=0.4)
449
+ barcode.drawOn(c, 72, 100)
450
+
451
+ # Add signature line
452
+ c.line(width - 200, 70, width - 72, 70)
453
+ c.drawString(width - 180, 60, "Judge's Signature")
454
+
455
+ c.save()
456
+
457
+ pdf_buffer.seek(0)
458
+
459
+ # Offer download
460
+ st.download_button(
461
+ label="📥 Download Generated PDF",
462
+ data=pdf_buffer,
463
+ file_name=f"judgment_{judgment_data['id']}_{judgment_data['case_title'].replace(' ', '_')}.pdf",
464
+ mime="application/pdf"
465
+ )
466
+
467
+ st.success("Judgment has been saved to the system.")
468
+
469
+ # Previous Judgments Tab
470
+ with selected_tab[2]:
471
+ st.markdown("## 📜 Previous Judgments")
472
+ st.markdown("### Review and search past judgments")
473
+
474
+ # Search and filter
475
+ search_term = st.text_input("Search judgments:", placeholder="Enter case title, number, plaintiff, etc.")
476
+
477
+ col1, col2 = st.columns(2)
478
+ with col1:
479
+ filter_type = st.multiselect("Filter by case type:",
480
+ options=["All"] + ["Criminal", "Civil", "Family", "Property", "Cyber Crime",
481
+ "Corporate", "Intellectual Property", "Other"],
482
+ default=["All"])
483
+ with col2:
484
+ sort_by = st.selectbox("Sort by:", options=["Most recent", "Oldest first", "Case title (A-Z)"])
485
+
486
+ # Display judgments based on filters
487
+ if len(st.session_state.judgments) == 0:
488
+ st.info("No judgments recorded yet. Use the Judge Console to create judgments.")
489
+ else:
490
+ # Filter judgments
491
+ filtered_judgments = st.session_state.judgments
492
+
493
+ # Apply search term filter
494
+ if search_term:
495
+ filtered_judgments = [j for j in filtered_judgments if
496
+ search_term.lower() in j['case_title'].lower() or
497
+ search_term.lower() in j['case_number'].lower() or
498
+ search_term.lower() in j['plaintiff'].lower() or
499
+ search_term.lower() in j['defendant'].lower() or
500
+ search_term.lower() in j.get('relevant_laws', '').lower()]
501
+
502
+ # Apply case type filter
503
+ if "All" not in filter_type:
504
+ filtered_judgments = [j for j in filtered_judgments if j['case_type'] in filter_type]
505
+
506
+ # Apply sorting
507
+ if sort_by == "Most recent":
508
+ filtered_judgments = sorted(filtered_judgments, key=lambda x: x['timestamp'], reverse=True)
509
+ elif sort_by == "Oldest first":
510
+ filtered_judgments = sorted(filtered_judgments, key=lambda x: x['timestamp'])
511
+ elif sort_by == "Case title (A-Z)":
512
+ filtered_judgments = sorted(filtered_judgments, key=lambda x: x['case_title'])
513
+
514
+ # Display judgments
515
+ for judgment in filtered_judgments:
516
+ with st.expander(f"**{judgment['case_title']}** - {judgment['timestamp']}"):
517
+ st.markdown(f"<div class='judge-badge'>JUDGMENT #{judgment['id']}</div>", unsafe_allow_html=True)
518
+ st.markdown(f"**Case Number**: {judgment['case_number']}")
519
+ st.markdown(f"**Plaintiff**: {judgment['plaintiff']}")
520
+ st.markdown(f"**Defendant**: {judgment['defendant']}")
521
+ st.markdown(f"**Case Type**: {judgment['case_type']} (Priority: {judgment['priority']})")
522
+
523
+ st.markdown("#### Case Facts")
524
+ st.markdown(judgment['facts'])
525
+
526
+ if judgment.get('relevant_laws'):
527
+ st.markdown("#### Relevant Laws Applied")
528
+ st.markdown(judgment['relevant_laws'])
529
+
530
+ st.markdown("#### Full Judgment")
531
+ st.markdown("---")
532
+ st.markdown(judgment['judgment'])
533
+
534
+ # Button to download individual judgment as PDF
535
+ if st.button(f"📥 Download PDF", key=f"download_{judgment['id']}"):
536
+ # Generate PDF with ReportLab
537
+ pdf_buffer = BytesIO()
538
+ c = canvas.Canvas(pdf_buffer, pagesize=letter)
539
+ width, height = letter
540
+
541
+ # Header
542
+ c.setFont("Helvetica-Bold", 16)
543
+ c.drawString(72, height - 72, f"JUDGMENT #{judgment['id']}")
544
+
545
+ # Case details
546
+ c.setFont("Helvetica-Bold", 12)
547
+ c.drawString(72, height - 100, f"Case: {judgment['case_title']}")
548
+ c.setFont("Helvetica", 10)
549
+ c.drawString(72, height - 115, f"Case Number: {judgment['case_number']}")
550
+ c.drawString(72, height - 130, f"Date: {judgment['timestamp']}")
551
+ c.drawString(72, height - 145, f"Plaintiff/Prosecution: {judgment['plaintiff']}")
552
+ c.drawString(72, height - 160, f"Defendant/Accused: {judgment['defendant']}")
553
+ c.drawString(72, height - 175, f"Case Type: {judgment['case_type']}")
554
+
555
+ # Line separator
556
+ c.line(72, height - 190, width - 72, height - 190)
557
+
558
+ # Format judgment text
559
+ judgment_text = judgment['judgment']
560
+ text_object = c.beginText(72, height - 210)
561
+ text_object.setFont("Times-Roman", 10)
562
+
563
+ # Wrap text to fit page
564
+ lines = []
565
+ for paragraph in judgment_text.split('\n\n'):
566
+ # Replace single newlines with spaces for proper wrapping
567
+ paragraph = paragraph.replace('\n', ' ')
568
+
569
+ # Simple word wrap
570
+ words = paragraph.split()
571
+ line = ''
572
+ for word in words:
573
+ if len(line + ' ' + word) <= 90: # character limit per line
574
+ line += ' ' + word if line else word
575
+ else:
576
+ lines.append(line)
577
+ line = word
578
+ if line:
579
+ lines.append(line)
580
+
581
+ # Add blank line between paragraphs
582
+ lines.append('')
583
+
584
+ # Add lines to text object with pagination
585
+ line_height = 12
586
+ lines_per_page = 50
587
+ current_line = 0
588
+
589
+ for line in lines:
590
+ if current_line >= lines_per_page:
591
+ c.drawText(text_object)
592
+ c.showPage()
593
+ text_object = c.beginText(72, height - 72)
594
+ text_object.setFont("Times-Roman", 10)
595
+ current_line = 0
596
+
597
+ text_object.textLine(line)
598
+ current_line += 1
599
+
600
+ c.drawText(text_object)
601
+
602
+ # Add footer with page numbers
603
+ c.saveState()
604
+ c.setFont("Helvetica", 8)
605
+ c.drawString(width/2 - 40, 30, f"Generated by LawGPT Judge")
606
+ c.restoreState()
607
+
608
+ # Final page with signature
609
+ c.showPage()
610
+ c.setFont("Helvetica-Bold", 12)
611
+ c.drawString(72, height - 100, "OFFICIAL JUDGMENT")
612
+ c.setFont("Helvetica", 10)
613
+ c.drawString(72, height - 130, f"Case #{judgment['id']} - {judgment['case_title']}")
614
+ c.drawString(72, height - 150, f"Date: {judgment['timestamp']}")
615
+
616
+ # Add barcode for authenticity
617
+ barcode = code128.Code128(f"JUDGMENT-{judgment['id']}", barHeight=10 * mm, barWidth=0.4)
618
+ barcode.drawOn(c, 72, 100)
619
+
620
+ # Add signature line
621
+ c.line(width - 200, 70, width - 72, 70)
622
+ c.drawString(width - 180, 60, "Judge's Signature")
623
+
624
+ c.save()
625
+
626
+ pdf_buffer.seek(0)
627
+
628
+ # Offer download
629
+ st.download_button(
630
+ label="📥 Download Generated PDF",
631
+ data=pdf_buffer,
632
+ file_name=f"judgment_{judgment['id']}_{judgment['case_title'].replace(' ', '_')}.pdf",
633
+ mime="application/pdf",
634
+ key=f"pdf_{judgment['id']}"
635
+ )
636
+
637
+ # Stakeholder tabs
638
  if st.session_state.role == "stakeholder":
639
  if "📝 Document Signer" in tabs:
640
  with selected_tab[1]:
 
674
  with selected_tab[2]:
675
  st.markdown("## 🔍 Verify Document Authentication")
676
  st.markdown("Upload any document to verify its integrity and authenticity.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
677
 
678
+ verify_file = st.file_uploader("Upload PDF for verification", type=["pdf"], key="verify")
 
679
 
680
+ if verify_file:
681
+ content = verify_file.read()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
682
 
683
+ try:
684
+ # Basic PDF validation
685
+ pdf = PdfReader(BytesIO(content))
686
+
687
+ # Extract text to look for signature markers
688
+ all_text = ""
689
+ for page in pdf.pages:
690
+ all_text += page.extract_text() or ""
691
+
692
+ # Check for digital signature information
693
+ has_signature_text = any(sig_text in all_text.lower() for sig_text in
694
+ ["signed by:", "digital signature", "electronic signature"])
695
+
696
+ # Create document fingerprint/hash
697
+ doc_hash = hashlib.sha256(content).hexdigest()
698
+
699
+ # Calculate metadata integrity
700
+ metadata_valid = True
701
+ if pdf.metadata:
702
+ try:
703
+ # Check for suspicious metadata modifications
704
+ creation_date = pdf.metadata.get('/CreationDate', '')
705
+ mod_date = pdf.metadata.get('/ModDate', '')
706
+ if mod_date and creation_date:
707
+ metadata_valid = mod_date >= creation_date
708
+ except:
709
+ metadata_valid = False
710
+
711
+ # Check for content consistency
712
+ content_consistent = True
713
+
714
+ col1, col2 = st.columns(2)
715
+
716
+ with col1:
717
+ st.subheader("Document Analysis")
718
+ st.info(f"📄 Pages: {len(pdf.pages)}")
719
+ st.info(f"🔒 Contains signature markers: {'Yes' if has_signature_text else 'No'}")
720
+
721
+ # Display hash for document tracking
722
+ st.code(f"Document Hash: {doc_hash[:16]}...{doc_hash[-16:]}")
723
 
724
+ # Document size and characteristics
725
+ file_size = len(content) / 1024 # KB
726
+ st.info(f"📦 File size: {file_size:.2f} KB")
727
 
728
+ with col2:
729
+ st.subheader("Verification Results")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
730
 
731
+ # Case 1: Document has signature markers
732
+ if has_signature_text:
733
+ if metadata_valid and content_consistent:
734
+ st.success("✅ Document Status: VERIFIED AUTHENTIC")
735
+ st.markdown("- Valid PDF structure")
736
+ st.markdown("- Signature information detected")
737
+ st.markdown("- ✓ No tampering indicators found")
738
+ st.markdown("- Metadata consistency verified")
739
+ else:
740
+ st.warning("⚠️ Document Status: POTENTIALLY MODIFIED")
741
+ st.markdown("- ✓ Valid PDF structure")
742
+ st.markdown("- Signature information found")
743
+ st.markdown("- ❌ Some integrity checks failed")
744
+
745
+ if not metadata_valid:
746
+ st.markdown("- Metadata inconsistencies detected")
747
+
748
+ # Display signature extraction if present
749
+ signature_line = next((line for line in all_text.split('\n') if "signed by:" in line.lower()), "")
750
+ if signature_line:
751
+ st.info(f"📝 {signature_line.strip()}")
752
+
753
+ # Case 2: Document without signatures
754
+ else:
755
+ if metadata_valid and content_consistent:
756
+ st.success(" Document Status: VALID DOCUMENT")
757
+ st.markdown("- Valid PDF structure")
758
+ st.markdown("- Content integrity verified")
759
+ st.markdown("- No tampering indicators found")
760
+ st.markdown("- ℹ️ No signature information found (this is not an error)")
761
+ else:
762
+ st.warning("⚠️ Document Status: POTENTIALLY MODIFIED")
763
+ st.markdown("- Valid PDF structure")
764
+ st.markdown("- ❌ Some integrity checks failed")
765
+
766
+ if not metadata_valid:
767
+ st.markdown("- Metadata inconsistencies detected")
768
+
769
+ # Advanced options
770
+ with st.expander("🔬 Advanced Verification Details"):
771
+ st.markdown("### Document Metadata")
772
+ if pdf.metadata:
773
+ for key, value in pdf.metadata.items():
774
+ if key and value and key not in ('/CreationDate', '/ModDate'):
775
+ st.text(f"{key}: {value}")
776
+ else:
777
+ st.text("No metadata available")
778
+
779
+ st.markdown("### Integrity Timeline")
780
+ st.text(f"Creation Date: {pdf.metadata.get('/CreationDate', 'Not available')}")
781
+ st.text(f"Last Modified: {pdf.metadata.get('/ModDate', 'Not available')}")
782
+
783
+ # Additional verification for content integrity
784
+ st.markdown("### Content Analysis")
785
+ fonts_used = set()
786
+ image_count = 0
787
+ for page in pdf.pages:
788
+ if "/Font" in page["/Resources"]:
789
+ for font in page["/Resources"]["/Font"]:
790
+ fonts_used.add(str(font))
791
+ if "/XObject" in page["/Resources"]:
792
+ for obj in page["/Resources"]["/XObject"]:
793
+ if "/Subtype" in page["/Resources"]["/XObject"][obj] and \
794
+ page["/Resources"]["/XObject"][obj]["/Subtype"] == "/Image":
795
+ image_count += 1
796
+
797
+ st.text(f"Fonts detected: {len(fonts_used)}")
798
+ st.text(f"Images detected: {image_count}")
799
+
800
+ except Exception as e:
801
+ st.error(f"❌ Document Status: INVALID OR CORRUPTED")
802
+ st.markdown(f"Error: Could not process the document properly. The file may be corrupted or not a valid PDF.")
803
+ st.markdown(f"Technical details: {str(e)}")