File size: 16,938 Bytes
61d29fc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
"""
Comprehensive demo: AI Summarization + Keyword Alerts + Batch Processing

Shows the complete workflow from:
1. Loading discovered jurisdictions
2. AI summarization of meeting content
3. Keyword alert generation
4. Quality metrics tracking
"""
import asyncio
from datetime import datetime
from pathlib import Path

# Check for OpenAI API key
from config.settings import settings

# Import our new capabilities
from extraction.summarizer import MeetingSummarizer, summarize_meeting_simple
from alerts.keyword_monitor import KeywordAlertSystem, generate_alert_email
from discovery.batch_processor import BatchProcessor, JurisdictionQuality
from models.meeting_event import MeetingEvent, Classification, Location


def print_section(title: str):
    """Print a section header."""
    print("\n" + "=" * 80)
    print(f"  {title}")
    print("=" * 80 + "\n")


def demo_ai_summarization():
    """Demo: AI-powered meeting summarization (OpenTowns pattern)."""
    print_section("πŸ€– AI SUMMARIZATION (OpenTowns Pattern)")
    
    # Example meeting
    event = MeetingEvent(
        title="City Council Regular Meeting",
        description="Discussion of community water fluoridation program",
        classification=Classification.COUNCIL,
        start=datetime(2026, 4, 15, 18, 0),
        location=Location(
            name="City Hall Council Chambers",
            address="710 N 20th Street",
            city="Birmingham",
            state="AL"
        ),
        jurisdiction_name="Birmingham",
        state_code="AL",
        source="https://birminghamal.gov/meetings/2026-04-15"
    )
    
    # Example meeting transcript
    transcript = """
    BIRMINGHAM CITY COUNCIL REGULAR MEETING
    April 15, 2026 - 6:00 PM
    City Hall Council Chambers
    
    PRESENT: Mayor Woodfin, Council President Scales, Councilors Smith,
    Johnson, Martinez, Davis, Brown, Wilson, Thompson, Lee
    
    AGENDA ITEM 3: RESOLUTION 2026-045
    Community Water Fluoridation Program Implementation
    
    Mayor Woodfin: "Tonight we are considering Resolution 2026-045, which
    would implement a community water fluoridation program for Birmingham's
    municipal water system. Dr. Sarah Johnson from the Alabama Department
    of Public Health is here to present."
    
    Dr. Johnson: "Thank you, Mayor. Community water fluoridation is recognized
    by the CDC as one of the ten great public health achievements of the 20th
    century. It's a safe, effective, and equitable way to prevent tooth decay
    across all age groups and socioeconomic levels.
    
    The proposed program would adjust fluoride levels in Birmingham's water
    to 0.7 mg/L, consistent with CDC and American Dental Association guidelines.
    Research shows this reduces tooth decay by 25% in children and adults.
    
    We estimate the program would cost $120,000 annually in operation and
    maintenance. However, the economic benefits are substantial - for every
    dollar invested, we save approximately $10 in dental treatment costs.
    That's $1.2 million in prevented costs annually for Birmingham."
    
    Councilor Smith: "I've reviewed the financial analysis and support this
    initiative. Tooth decay is a significant problem in our community,
    particularly among children. This is a cost-effective prevention measure."
    
    Councilor Johnson: "I appreciate the presentation, but I'd like to hear
    from our residents before we vote. I propose we schedule a public hearing
    to gather community input."
    
    Mayor Woodfin: "That's a reasonable request. Let's schedule a public
    hearing for May 6th at 6:00 PM."
    
    MOTION by Councilor Smith, seconded by Councilor Martinez, to schedule
    a public hearing on Resolution 2026-045 for May 6, 2026 at 6:00 PM.
    
    VOTE: 9 Ayes, 1 No (Councilor Thompson)
    Motion carried.
    
    AGENDA ITEM 4: UPDATE ON MEDICAID DENTAL EXPANSION
    
    Health Director Martinez: "The state has approved expanded Medicaid dental
    coverage for adults. We're working with local dental clinics to ensure
    capacity. We expect 5,000 newly eligible residents in Birmingham."
    
    MEETING ADJOURNED at 8:15 PM
    Next meeting: April 29, 2026 at 6:00 PM
    """
    
    # Check if API key is configured
    if not settings.openai_api_key:
        print("⚠️  OpenAI API key not configured.")
        print("\nTo enable AI summarization:")
        print("  1. Set OPENAI_API_KEY in your .env file")
        print("  2. Or export OPENAI_API_KEY='sk-...'")
        print("\nπŸ“ Showing what the output would look like:\n")
        
        # Show mock summary
        print("Executive Summary:")
        print("  The Birmingham City Council voted 9-1 to schedule a public hearing")
        print("  on a community water fluoridation program. The program would cost")
        print("  $120,000 annually but could prevent $1.2M in dental costs.")
        print("\nKey Decisions:")
        print("  β€’ Public hearing scheduled for May 6, 2026")
        print("  β€’ Resolution 2026-045 moved to public comment phase")
        print("\nHealth Policy Items:")
        print("  β€’ Community water fluoridation program (0.7 mg/L CDC standard)")
        print("  β€’ Medicaid dental expansion for 5,000 Birmingham residents")
        print("\nNext Actions:")
        print("  β€’ Public hearing: May 6, 2026 at 6:00 PM")
        print("  β€’ Next council meeting: April 29, 2026")
        return
    
    # Generate real summary
    try:
        summarizer = MeetingSummarizer()
        summary = summarizer.summarize(event, transcript, focus_on_health=True)
        
        print(f"πŸ“‹ Meeting: {event.title}")
        print(f"πŸ“ Location: {event.jurisdiction_name}, {event.state_code}")
        print(f"πŸ“… Date: {event.start.strftime('%B %d, %Y')}")
        print(f"\n✨ Executive Summary:")
        print(f"  {summary.executive_summary}")
        
        if summary.key_decisions:
            print(f"\nβœ… Key Decisions ({len(summary.key_decisions)}):")
            for decision in summary.key_decisions:
                print(f"  β€’ {decision}")
        
        if summary.health_policy_items:
            print(f"\nπŸ₯ Health Policy Items ({len(summary.health_policy_items)}):")
            for item in summary.health_policy_items:
                print(f"  β€’ {item}")
        
        if summary.next_actions:
            print(f"\n⏭️  Next Actions ({len(summary.next_actions)}):")
            for action in summary.next_actions:
                print(f"  β€’ {action}")
        
        print(f"\nπŸ“Š Quality Metrics:")
        print(f"  Confidence: {summary.confidence_score:.0%}")
        print(f"  Source length: {summary.source_length:,} chars")
        print(f"  Summary length: {summary.summary_length:,} chars")
        print(f"  Compression ratio: {(summary.summary_length/summary.source_length):.1%}")
        print(f"  Model: {summary.model_used}")
        print(f"  Tokens used: {summary.tokens_used:,}")
        
    except Exception as e:
        print(f"❌ Error generating summary: {e}")


def demo_keyword_alerts():
    """Demo: Keyword-based alert system (OpenTowns pattern)."""
    print_section("πŸ”” KEYWORD ALERTS (OpenTowns Pattern)")
    
    # Same event and transcript as above
    event = MeetingEvent(
        title="City Council Regular Meeting",
        classification=Classification.COUNCIL,
        start=datetime(2026, 4, 15, 18, 0),
        jurisdiction_name="Birmingham",
        state_code="AL",
        source="https://birminghamal.gov/meetings/2026-04-15"
    )
    
    transcript = """
    Birmingham City Council - April 15, 2026
    
    Resolution 2026-045: Community Water Fluoridation Program
    
    The council voted to schedule a public hearing on implementing community
    water fluoridation. Dr. Johnson from the Alabama Department of Public Health
    presented data showing fluoridation reduces tooth decay by 25%. The program
    would adjust fluoride levels to 0.7 mg/L per CDC guidelines.
    
    Cost-benefit analysis: $120,000 annual cost, $1.2 million in prevented
    dental treatment costs. Vote: 9-1 to schedule public hearing May 6.
    
    Also discussed: Medicaid dental expansion for adults, 5,000 newly eligible
    Birmingham residents. Health Director Martinez coordinating with dental
    clinics to ensure capacity.
    """
    
    # Scan for keywords
    alert_system = KeywordAlertSystem()
    alerts = alert_system.scan_meeting(event, transcript)
    
    if alerts:
        alert = alerts[0]
        
        print(f"🚨 ALERT GENERATED!")
        print(f"\nAlert ID: {alert.alert_id}")
        print(f"Priority: {alert.priority.value.upper()} ({'πŸ”΄' if alert.priority.value == 'critical' else '🟠' if alert.priority.value == 'high' else '🟑'})")
        print(f"\nπŸ“ Meeting Details:")
        print(f"  Jurisdiction: {alert.jurisdiction_name}, {alert.state_code}")
        print(f"  Title: {alert.meeting_title}")
        print(f"  Date: {alert.meeting_date.strftime('%B %d, %Y at %I:%M %p')}")
        
        print(f"\n🎯 Match Details:")
        print(f"  Total matches: {alert.total_matches}")
        print(f"  Categories: {', '.join(alert.categories_matched)}")
        print(f"  Confidence: {alert.confidence_score:.0%}")
        
        print(f"\nπŸ”‘ Keywords Found ({len(alert.keywords_found)}):")
        for i, keyword in enumerate(alert.keywords_found[:12], 1):
            print(f"  {i:2d}. {keyword}")
        if len(alert.keywords_found) > 12:
            print(f"  ... and {len(alert.keywords_found) - 12} more")
        
        print(f"\nπŸ“„ Relevant Excerpt:")
        print(f"  \"{alert.snippet[:250]}...\"")
        
        print(f"\nπŸ“§ Email Alert:")
        print(f"  Generated HTML email ready to send to subscribers")
        print(f"  Preview: 'CRITICAL Priority Alert: {alert.meeting_title}'")
        
        # Show first few lines of HTML email
        email_html = generate_alert_email(alert)
        print(f"  Length: {len(email_html):,} chars")
        
    else:
        print("ℹ️  No alerts generated (insufficient keyword matches)")


def demo_batch_processing():
    """Demo: Batch processing with quality metrics (LocalView pattern)."""
    print_section("πŸ“Š BATCH PROCESSING & QUALITY METRICS (LocalView Pattern)")
    
    print("This system handles large-scale processing of 1,000+ jurisdictions:\n")
    
    # Show quality metric example
    print("πŸ“ˆ Quality Tracking Per Jurisdiction:\n")
    
    example_metrics = JurisdictionQuality(
        jurisdiction_name="Birmingham",
        state_code="AL",
        fips_code="0107000",
        url="https://birminghamal.gov",
        platform="legistar",
        total_meetings_expected=24,  # Biweekly meetings
        total_meetings_found=20,
        meetings_with_agendas=20,
        meetings_with_minutes=15,
        meetings_with_videos=10,
        meetings_with_transcripts=8,
        last_scraped=datetime.utcnow(),
        last_meeting_found=datetime(2026, 4, 15),
        scraping_frequency="biweekly",
        consecutive_successes=5,
        consecutive_failures=0,
        total_scrapes=10,
        successful_scrapes=10,
        last_success=datetime.utcnow(),
        last_error=None,
        completeness_score=85.0,
        reliability_score=100.0,
        freshness_score=100.0,
        overall_quality=90.0,
        health_status="healthy",
        created_at=datetime(2026, 1, 1),
        updated_at=datetime.utcnow()
    )
    
    print(f"Jurisdiction: {example_metrics.jurisdiction_name}, {example_metrics.state_code}")
    print(f"Platform: {example_metrics.platform}")
    print(f"\nData Completeness:")
    print(f"  Expected meetings: {example_metrics.total_meetings_expected}")
    print(f"  Found meetings: {example_metrics.total_meetings_found}")
    print(f"  With agendas: {example_metrics.meetings_with_agendas}")
    print(f"  With minutes: {example_metrics.meetings_with_minutes}")
    print(f"  With videos: {example_metrics.meetings_with_videos}")
    
    print(f"\nReliability:")
    print(f"  Total scrapes: {example_metrics.total_scrapes}")
    print(f"  Successful: {example_metrics.successful_scrapes}")
    print(f"  Success rate: {(example_metrics.successful_scrapes/example_metrics.total_scrapes)*100:.0f}%")
    print(f"  Consecutive successes: {example_metrics.consecutive_successes}")
    
    print(f"\nQuality Scores:")
    print(f"  Completeness: {example_metrics.completeness_score:.1f}/100")
    print(f"  Reliability: {example_metrics.reliability_score:.1f}/100")
    print(f"  Freshness: {example_metrics.freshness_score:.1f}/100")
    print(f"  Overall: {example_metrics.overall_quality:.1f}/100")
    
    print(f"\nHealth Status: {example_metrics.health_status.upper()} βœ…")
    
    print("\nπŸ”„ Batch Processing Features:")
    print("  β€’ Process 100 jurisdictions at a time")
    print("  β€’ Track success/failure per batch")
    print("  β€’ Automatic retry with exponential backoff")
    print("  β€’ Resume from interruption")
    print("  β€’ Quality metrics per jurisdiction")
    print("  β€’ System-wide health reporting")
    
    print("\nπŸ’‘ Example Usage:")
    print("""
  from discovery.batch_processor import BatchProcessor
  
  processor = BatchProcessor(batch_size=100)
  
  # Process all high-priority jurisdictions
  for batch_result in processor.process_all_jurisdictions(priority_filter='high'):
      print(f"Batch {batch_result.batch_number}: "
            f"{batch_result.success_rate:.1f}% success")
      print(f"  Meetings found: {batch_result.meetings_found}")
      print(f"  Duration: {batch_result.duration_seconds:.0f}s")
  
  # Get system health report
  health = processor.get_system_health_report()
  print(f"System health: {health['health_percentage']:.1f}% healthy jurisdictions")
    """)


def demo_integration_summary():
    """Show summary of all integrated capabilities."""
    print_section("🎯 COMPLETE INTEGRATION SUMMARY")
    
    print("βœ… Integrated Patterns from 11 Civic Tech Projects:\n")
    
    capabilities = [
        ("Platform Detection", "Civic Scraper", "βœ… discovery/platform_detector.py"),
        ("Event Schema", "City Scrapers", "βœ… models/meeting_event.py"),
        ("Matter Tracking", "Engagic", "βœ… models/meeting_event.py"),
        ("AI Summarization", "OpenTowns", "βœ… extraction/summarizer.py"),
        ("Keyword Alerts", "OpenTowns", "βœ… alerts/keyword_monitor.py"),
        ("Batch Processing", "LocalView", "βœ… discovery/batch_processor.py"),
        ("Quality Metrics", "LocalView", "βœ… discovery/batch_processor.py"),
        ("Summary Validation", "MeetingBank", "βœ… extraction/summarizer.py"),
        ("Video Ingestion", "CDP", "πŸ“‹ Roadmapped"),
        ("Cross-Jurisdiction Search", "CivicBand", "πŸ“‹ Architecture designed"),
        ("Person/Vote Tracking", "Councilmatic", "πŸ“‹ Planned"),
    ]
    
    for capability, source, status in capabilities:
        icon = "βœ…" if "βœ…" in status else "πŸ“‹"
        print(f"{icon} {capability:25s} ({source:15s}) β†’ {status.replace('βœ…', '').replace('πŸ“‹', '').strip()}")
    
    print("\nπŸ“š Documentation:")
    print("  β€’ docs/INTEGRATION_GUIDE.md - First 5 projects (Civic Scraper, City Scrapers, CDP, Engagic, Councilmatic)")
    print("  β€’ docs/SCALE_AND_SEARCH_PATTERNS.md - Next 6 projects (OpenTowns, LocalView, etc.)")
    
    print("\n🎬 Demo Scripts:")
    print("  β€’ examples/integration_demo.py - Platform detection & event models")
    print("  β€’ examples/full_demo.py (this file) - AI summarization, alerts, batch processing")
    
    print("\nπŸš€ Ready for Production:")
    print("  1. βœ… Jurisdiction discovery (85,302 records from Census)")
    print("  2. βœ… URL matching (76 .gov domains found)")
    print("  3. βœ… Platform detection (8 platforms supported)")
    print("  4. βœ… AI summarization (GPT-4o-mini)")
    print("  5. βœ… Keyword alerts (6 categories, 4 priority levels)")
    print("  6. βœ… Batch processing (100 at a time with quality tracking)")
    print("  7. πŸ“‹ Next: Implement actual scrapers (Legistar, Granicus, etc.)")


def main():
    """Run all demos."""
    print("\n" + "🦷" * 40)
    print("  ORAL HEALTH POLICY PULSE")
    print("  Full Integration Demo: AI + Alerts + Scale")
    print("🦷" * 40)
    
    # Run demos
    demo_ai_summarization()
    demo_keyword_alerts()
    demo_batch_processing()
    demo_integration_summary()
    
    print("\n" + "=" * 80)
    print("  βœ… Demo Complete!")
    print("=" * 80)
    
    print("\nπŸ’‘ Try it yourself:")
    print("  python examples/full_demo.py")
    print("\n  Or explore individual capabilities:")
    print("  β€’ python extraction/summarizer.py")
    print("  β€’ python alerts/keyword_monitor.py")
    print("  β€’ python discovery/batch_processor.py")


if __name__ == "__main__":
    main()