Signe22 commited on
Commit
d961738
·
verified ·
1 Parent(s): cb6ca89

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +13 -0
  2. app.py +157 -0
  3. requirements.txt +3 -0
Dockerfile ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY api/requirements.txt /app/requirements.txt
6
+ RUN pip install --no-cache-dir -r /app/requirements.txt
7
+
8
+ COPY api /app/api
9
+ COPY data /app/data
10
+
11
+ EXPOSE 7860
12
+
13
+ CMD ["uvicorn", "api.app:app", "--host", "0.0.0.0", "--port", "7860"]
app.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+ import sqlite3
3
+ from typing import Optional
4
+
5
+ import pandas as pd
6
+ from fastapi import FastAPI, Query
7
+ from fastapi.middleware.cors import CORSMiddleware
8
+
9
+ app = FastAPI(title="Green Energy News API", version="1.0.0")
10
+
11
+ # SQLite stays in the same repo position
12
+ BASE_DIR = Path(__file__).resolve().parent.parent
13
+ DB_PATH = BASE_DIR / "data" / "news.db"
14
+
15
+ app.add_middleware(
16
+ CORSMiddleware,
17
+ allow_origins=["*"], # tighten later if needed
18
+ allow_credentials=False,
19
+ allow_methods=["*"],
20
+ allow_headers=["*"],
21
+ )
22
+
23
+
24
+ def get_connection() -> sqlite3.Connection:
25
+ return sqlite3.connect(DB_PATH)
26
+
27
+
28
+ @app.get("/health")
29
+ def health():
30
+ return {
31
+ "status": "ok",
32
+ "db_exists": DB_PATH.exists(),
33
+ "db_path": str(DB_PATH),
34
+ }
35
+
36
+
37
+ @app.get("/labels")
38
+ def get_labels():
39
+ conn = get_connection()
40
+ query = """
41
+ SELECT DISTINCT label
42
+ FROM classified_articles
43
+ WHERE label IS NOT NULL
44
+ ORDER BY label
45
+ """
46
+ df = pd.read_sql_query(query, conn)
47
+ conn.close()
48
+ return df["label"].dropna().tolist()
49
+
50
+
51
+ @app.get("/sources")
52
+ def get_sources():
53
+ conn = get_connection()
54
+ query = """
55
+ SELECT DISTINCT source
56
+ FROM classified_articles
57
+ WHERE source IS NOT NULL
58
+ ORDER BY source
59
+ """
60
+ df = pd.read_sql_query(query, conn)
61
+ conn.close()
62
+ return df["source"].dropna().tolist()
63
+
64
+
65
+ @app.get("/summary/daily-actions")
66
+ def daily_actions(
67
+ start_date: Optional[str] = None,
68
+ end_date: Optional[str] = None,
69
+ ):
70
+ conn = get_connection()
71
+
72
+ query = """
73
+ SELECT
74
+ date(published_at) AS day,
75
+ label,
76
+ COUNT(*) AS count
77
+ FROM classified_articles
78
+ WHERE 1=1
79
+ """
80
+ params = []
81
+
82
+ if start_date:
83
+ query += " AND date(published_at) >= date(?)"
84
+ params.append(start_date)
85
+
86
+ if end_date:
87
+ query += " AND date(published_at) <= date(?)"
88
+ params.append(end_date)
89
+
90
+ query += """
91
+ GROUP BY date(published_at), label
92
+ ORDER BY day ASC, label ASC
93
+ """
94
+
95
+ df = pd.read_sql_query(query, conn, params=params)
96
+ conn.close()
97
+
98
+ return df.to_dict(orient="records")
99
+
100
+
101
+ @app.get("/articles")
102
+ def get_articles(
103
+ label: Optional[str] = None,
104
+ source: Optional[str] = None,
105
+ start_date: Optional[str] = None,
106
+ end_date: Optional[str] = None,
107
+ search: Optional[str] = None,
108
+ limit: int = Query(50, ge=1, le=500),
109
+ offset: int = Query(0, ge=0),
110
+ ):
111
+ conn = get_connection()
112
+
113
+ query = """
114
+ SELECT
115
+ article_id,
116
+ title,
117
+ description,
118
+ clean_text,
119
+ label,
120
+ raw_label,
121
+ source,
122
+ url,
123
+ published_at,
124
+ classified_at
125
+ FROM classified_articles
126
+ WHERE 1=1
127
+ """
128
+ params = []
129
+
130
+ if label:
131
+ query += " AND label = ?"
132
+ params.append(label)
133
+
134
+ if source:
135
+ query += " AND source = ?"
136
+ params.append(source)
137
+
138
+ if start_date:
139
+ query += " AND date(published_at) >= date(?)"
140
+ params.append(start_date)
141
+
142
+ if end_date:
143
+ query += " AND date(published_at) <= date(?)"
144
+ params.append(end_date)
145
+
146
+ if search:
147
+ query += " AND (lower(title) LIKE ? OR lower(description) LIKE ?)"
148
+ pattern = f"%{search.lower()}%"
149
+ params.extend([pattern, pattern])
150
+
151
+ query += " ORDER BY published_at DESC LIMIT ? OFFSET ?"
152
+ params.extend([limit, offset])
153
+
154
+ df = pd.read_sql_query(query, conn, params=params)
155
+ conn.close()
156
+
157
+ return df.to_dict(orient="records")
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ fastapi==0.110.0
2
+ uvicorn==0.29.0
3
+ pandas==2.2.2