varunka commited on
Commit
c31d423
·
1 Parent(s): 97e7973

announcement route added

Browse files
Files changed (4) hide show
  1. database.py +5 -0
  2. main.py +3 -1
  3. models/announcement.py +23 -0
  4. routers/announcements.py +171 -0
database.py CHANGED
@@ -74,6 +74,11 @@ def _ensure_complaint_workflow_columns():
74
  return
75
 
76
  required_columns = {
 
 
 
 
 
77
  "citizen_user_id": "INTEGER",
78
  "citizen_language": "VARCHAR",
79
  "image_path": "VARCHAR",
 
74
  return
75
 
76
  required_columns = {
77
+ "ai_risk_score": "FLOAT",
78
+ "ai_risk_level": "VARCHAR",
79
+ "ai_risk_factors": "TEXT",
80
+ "ai_risk_reasoning": "TEXT",
81
+ "ai_leader_brief": "TEXT",
82
  "citizen_user_id": "INTEGER",
83
  "citizen_language": "VARCHAR",
84
  "image_path": "VARCHAR",
main.py CHANGED
@@ -60,7 +60,7 @@ app.add_middleware(
60
  app.mount("/uploads", StaticFiles(directory=UPLOAD_DIR), name="uploads")
61
 
62
  # Import and register routers
63
- from routers import complaints, nlp, priority, sentiment, vision, speech, reports, dashboard, whatsapp, auth
64
 
65
  app.include_router(complaints.router, prefix=API_PREFIX)
66
  app.include_router(auth.router, prefix=API_PREFIX)
@@ -72,6 +72,7 @@ app.include_router(speech.router, prefix=API_PREFIX)
72
  app.include_router(reports.router, prefix=API_PREFIX)
73
  app.include_router(dashboard.router, prefix=API_PREFIX)
74
  app.include_router(whatsapp.router, prefix=API_PREFIX)
 
75
 
76
 
77
  @app.on_event("startup")
@@ -108,6 +109,7 @@ def root():
108
  "speech_transcribe": f"{API_PREFIX}/speech/transcribe",
109
  "report_generate": f"{API_PREFIX}/reports/generate",
110
  "dashboard_stats": f"{API_PREFIX}/dashboard/stats",
 
111
  "whatsapp_webhook": f"{API_PREFIX}/whatsapp/webhook",
112
  "whatsapp_test": f"{API_PREFIX}/whatsapp/test",
113
  },
 
60
  app.mount("/uploads", StaticFiles(directory=UPLOAD_DIR), name="uploads")
61
 
62
  # Import and register routers
63
+ from routers import complaints, nlp, priority, sentiment, vision, speech, reports, dashboard, whatsapp, auth, announcements
64
 
65
  app.include_router(complaints.router, prefix=API_PREFIX)
66
  app.include_router(auth.router, prefix=API_PREFIX)
 
72
  app.include_router(reports.router, prefix=API_PREFIX)
73
  app.include_router(dashboard.router, prefix=API_PREFIX)
74
  app.include_router(whatsapp.router, prefix=API_PREFIX)
75
+ app.include_router(announcements.router, prefix=API_PREFIX)
76
 
77
 
78
  @app.on_event("startup")
 
109
  "speech_transcribe": f"{API_PREFIX}/speech/transcribe",
110
  "report_generate": f"{API_PREFIX}/reports/generate",
111
  "dashboard_stats": f"{API_PREFIX}/dashboard/stats",
112
+ "announcements_public": f"{API_PREFIX}/announcements/public",
113
  "whatsapp_webhook": f"{API_PREFIX}/whatsapp/webhook",
114
  "whatsapp_test": f"{API_PREFIX}/whatsapp/test",
115
  },
models/announcement.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy import Boolean, Column, DateTime, Integer, String, Text
2
+ from sqlalchemy.sql import func
3
+
4
+ from database import Base
5
+
6
+
7
+ class Announcement(Base):
8
+ __tablename__ = "announcements"
9
+
10
+ id = Column(Integer, primary_key=True, index=True, autoincrement=True)
11
+ title = Column(String, nullable=False)
12
+ message = Column(Text, nullable=False)
13
+ advisory_type = Column(String, nullable=False, default="public_notice")
14
+ priority = Column(String, nullable=False, default="medium")
15
+ ward = Column(String, nullable=True)
16
+ image_url = Column(String, nullable=True)
17
+ cta_text = Column(String, nullable=True)
18
+ cta_link = Column(String, nullable=True)
19
+ is_published = Column(Boolean, nullable=False, default=True)
20
+ created_by_user_id = Column(Integer, nullable=True)
21
+ created_by_name = Column(String, nullable=True)
22
+ created_at = Column(DateTime(timezone=True), server_default=func.now())
23
+ updated_at = Column(DateTime(timezone=True), onupdate=func.now())
routers/announcements.py ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Announcements Router — Leader advisories and public notices for homepage display.
3
+ """
4
+
5
+ from typing import Optional
6
+
7
+ from fastapi import APIRouter, Depends
8
+ from pydantic import BaseModel
9
+ from sqlalchemy.orm import Session
10
+
11
+ from database import get_db
12
+ from models.announcement import Announcement
13
+ from models.user import User
14
+ from routers.auth import get_current_leader
15
+
16
+ router = APIRouter(prefix="/announcements", tags=["Announcements"])
17
+
18
+
19
+ class AnnouncementCreateRequest(BaseModel):
20
+ title: str
21
+ message: str
22
+ advisory_type: str = "public_notice"
23
+ priority: str = "medium"
24
+ ward: Optional[str] = None
25
+ image_url: Optional[str] = None
26
+ cta_text: Optional[str] = None
27
+ cta_link: Optional[str] = None
28
+ publish_now: bool = True
29
+
30
+
31
+ def _default_announcements():
32
+ return [
33
+ {
34
+ "title": "Heatwave Advisory: Stay Hydrated",
35
+ "message": "Temperatures are expected to cross 42C this week. Avoid afternoon outdoor exposure,"
36
+ " use ORS, and check in on elderly neighbors.",
37
+ "advisory_type": "public_health",
38
+ "priority": "high",
39
+ "ward": "All Wards",
40
+ "image_url": "https://images.unsplash.com/photo-1469474968028-56623f02e42e?auto=format&fit=crop&w=1200&q=80",
41
+ "cta_text": "View Heat Safety Tips",
42
+ "cta_link": "https://www.ndma.gov.in/Natural-Hazards/Heat-Wave",
43
+ },
44
+ {
45
+ "title": "Monsoon Drain Clearance Drive",
46
+ "message": "Pre-monsoon desilting and drain cleaning starts from Monday. Please avoid dumping solid waste in roadside drains.",
47
+ "advisory_type": "infrastructure",
48
+ "priority": "medium",
49
+ "ward": "All Wards",
50
+ "image_url": "https://images.unsplash.com/photo-1502303756762-a8d7dca6f96d?auto=format&fit=crop&w=1200&q=80",
51
+ "cta_text": "See Ward Schedule",
52
+ "cta_link": "https://urbanindia.gov.in/",
53
+ },
54
+ {
55
+ "title": "Rumor Alert: Water Supply Not Discontinued",
56
+ "message": "A viral message claiming complete water shutdown is false. Routine supply remains active."
57
+ " Report misinformation through the citizen portal.",
58
+ "advisory_type": "fact_check",
59
+ "priority": "high",
60
+ "ward": "All Wards",
61
+ "image_url": "https://images.unsplash.com/photo-1473448912268-2022ce9509d8?auto=format&fit=crop&w=1200&q=80",
62
+ "cta_text": "Report Misinformation",
63
+ "cta_link": "/citizen",
64
+ },
65
+ {
66
+ "title": "Citizen Advice: Emergency Escalation",
67
+ "message": "If an issue involves fire, gas leak, electrocution, or flooding risk, mention 'emergency' in the complaint title for immediate triage.",
68
+ "advisory_type": "advice",
69
+ "priority": "medium",
70
+ "ward": "All Wards",
71
+ "image_url": "https://images.unsplash.com/photo-1582213782179-e0d53f98f2ca?auto=format&fit=crop&w=1200&q=80",
72
+ "cta_text": "File Emergency Complaint",
73
+ "cta_link": "/citizen",
74
+ },
75
+ ]
76
+
77
+
78
+ def _serialize(row: Announcement):
79
+ return {
80
+ "id": row.id,
81
+ "title": row.title,
82
+ "message": row.message,
83
+ "advisory_type": row.advisory_type,
84
+ "priority": row.priority,
85
+ "ward": row.ward,
86
+ "image_url": row.image_url,
87
+ "cta_text": row.cta_text,
88
+ "cta_link": row.cta_link,
89
+ "is_published": bool(row.is_published),
90
+ "created_by_name": row.created_by_name,
91
+ "created_at": row.created_at.isoformat() if row.created_at else None,
92
+ }
93
+
94
+
95
+ def _seed_defaults_if_empty(db: Session):
96
+ existing = db.query(Announcement).count()
97
+ if existing > 0:
98
+ return
99
+
100
+ for item in _default_announcements():
101
+ db.add(
102
+ Announcement(
103
+ title=item["title"],
104
+ message=item["message"],
105
+ advisory_type=item["advisory_type"],
106
+ priority=item["priority"],
107
+ ward=item.get("ward"),
108
+ image_url=item.get("image_url"),
109
+ cta_text=item.get("cta_text"),
110
+ cta_link=item.get("cta_link"),
111
+ is_published=True,
112
+ created_by_name="System Advisory Desk",
113
+ )
114
+ )
115
+ db.commit()
116
+
117
+
118
+ @router.get("/public")
119
+ def list_public_announcements(limit: int = 6, db: Session = Depends(get_db)):
120
+ _seed_defaults_if_empty(db)
121
+
122
+ rows = (
123
+ db.query(Announcement)
124
+ .filter(Announcement.is_published.is_(True))
125
+ .order_by(Announcement.created_at.desc())
126
+ .limit(max(1, min(limit, 20)))
127
+ .all()
128
+ )
129
+ return {"announcements": [_serialize(row) for row in rows]}
130
+
131
+
132
+ @router.get("/leader/manage")
133
+ def list_leader_announcements(
134
+ limit: int = 30,
135
+ db: Session = Depends(get_db),
136
+ _: User = Depends(get_current_leader),
137
+ ):
138
+ _seed_defaults_if_empty(db)
139
+
140
+ rows = (
141
+ db.query(Announcement)
142
+ .order_by(Announcement.created_at.desc())
143
+ .limit(max(1, min(limit, 100)))
144
+ .all()
145
+ )
146
+ return {"announcements": [_serialize(row) for row in rows]}
147
+
148
+
149
+ @router.post("")
150
+ def create_announcement(
151
+ req: AnnouncementCreateRequest,
152
+ db: Session = Depends(get_db),
153
+ current_user: User = Depends(get_current_leader),
154
+ ):
155
+ row = Announcement(
156
+ title=req.title.strip(),
157
+ message=req.message.strip(),
158
+ advisory_type=req.advisory_type.strip() or "public_notice",
159
+ priority=req.priority.strip() or "medium",
160
+ ward=req.ward.strip() if req.ward else None,
161
+ image_url=req.image_url.strip() if req.image_url else None,
162
+ cta_text=req.cta_text.strip() if req.cta_text else None,
163
+ cta_link=req.cta_link.strip() if req.cta_link else None,
164
+ is_published=bool(req.publish_now),
165
+ created_by_user_id=current_user.id,
166
+ created_by_name=current_user.name,
167
+ )
168
+ db.add(row)
169
+ db.commit()
170
+ db.refresh(row)
171
+ return _serialize(row)