Skip to content

Commit 5cd85fe

Browse files
committed
Merge branch 'feature/secondary-country' into dev
2 parents c58a539 + 501a20f commit 5cd85fe

File tree

4 files changed

+53
-15
lines changed

4 files changed

+53
-15
lines changed

docker-compose.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ services:
2727
- ./sql/create_tables.sql:/docker-entrypoint-initdb.d/1-create_tables.sql
2828
- ./sql/import_data.sql:/docker-entrypoint-initdb.d/2-import_data.sql
2929
- ./sql/init_test_data.sql:/docker-entrypoint-initdb.d/3-init_test_data.sql
30+
- ./sql/add_secondary_location.sql:/app/sql/add_secondary_location.sql
3031
- ./data:/docker-entrypoint-initdb.d/data
3132
healthcheck:
3233
test: ["CMD-SHELL", "pg_isready -U postgres"]

sql/add_secondary_location.sql

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
Migration script to add secondary_location column to signals table.
3+
Run this script to update the database schema.
4+
*/
5+
6+
-- Add secondary_location column to signals table
7+
ALTER TABLE signals ADD COLUMN IF NOT EXISTS secondary_location TEXT[];
8+
9+
-- Update the index to include the new column
10+
DROP INDEX IF EXISTS signals_idx;
11+
CREATE INDEX ON signals (
12+
status,
13+
created_by,
14+
created_for,
15+
created_unit,
16+
steep_primary,
17+
steep_secondary,
18+
signature_primary,
19+
signature_secondary,
20+
sdgs,
21+
location,
22+
secondary_location,
23+
score
24+
);

src/database/signals.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ async def search_signals(cursor: AsyncCursor, filters: SignalFilters) -> SignalP
3434
page : SignalPage
3535
Paginated search results for signals.
3636
"""
37-
query = """
37+
query = f"""
3838
SELECT
3939
*, COUNT(*) OVER() AS total_count
4040
FROM
@@ -84,17 +84,13 @@ async def search_signals(cursor: AsyncCursor, filters: SignalFilters) -> SignalP
8484
AND (%(unit)s IS NULL OR unit_region = %(unit)s OR unit_name = %(unit)s)
8585
AND (%(query)s IS NULL OR text_search_field @@ websearch_to_tsquery('english', %(query)s))
8686
ORDER BY
87-
{} {}
87+
{filters.order_by} {filters.direction}
8888
OFFSET
8989
%(offset)s
9090
LIMIT
9191
%(limit)s
9292
;
9393
"""
94-
query = sql.SQL(query).format(
95-
sql.Identifier(filters.order_by),
96-
sql.SQL(filters.direction),
97-
)
9894
await cursor.execute(query, filters.model_dump())
9995
rows = await cursor.fetchall()
10096
# extract total count of rows matching the WHERE clause
@@ -112,7 +108,8 @@ async def create_signal(cursor: AsyncCursor, signal: Signal) -> int:
112108
cursor : AsyncCursor
113109
An async database cursor.
114110
signal : Signal
115-
A signal object to insert.
111+
A signal object to insert. The following fields are supported:
112+
- secondary_location: list[str] | None
116113
117114
Returns
118115
-------
@@ -137,6 +134,7 @@ async def create_signal(cursor: AsyncCursor, signal: Signal) -> int:
137134
relevance,
138135
keywords,
139136
location,
137+
secondary_location,
140138
score
141139
)
142140
VALUES (
@@ -156,6 +154,7 @@ async def create_signal(cursor: AsyncCursor, signal: Signal) -> int:
156154
%(relevance)s,
157155
%(keywords)s,
158156
%(location)s,
157+
%(secondary_location)s,
159158
%(score)s
160159
)
161160
RETURNING
@@ -221,7 +220,8 @@ async def read_signal(cursor: AsyncCursor, uid: int) -> Signal | None:
221220
;
222221
"""
223222
await cursor.execute(query, (uid,))
224-
if (row := await cursor.fetchone()) is None:
223+
row = await cursor.fetchone()
224+
if row is None:
225225
return None
226226
return Signal(**row)
227227

@@ -236,7 +236,8 @@ async def update_signal(cursor: AsyncCursor, signal: Signal) -> int | None:
236236
cursor : AsyncCursor
237237
An async database cursor.
238238
signal : Signal
239-
A signal object to update.
239+
A signal object to update. The following fields are supported:
240+
- secondary_location: list[str] | None
240241
241242
Returns
242243
-------
@@ -263,6 +264,7 @@ async def update_signal(cursor: AsyncCursor, signal: Signal) -> int | None:
263264
relevance = COALESCE(%(relevance)s, relevance),
264265
keywords = COALESCE(%(keywords)s, keywords),
265266
location = COALESCE(%(location)s, location),
267+
secondary_location = COALESCE(%(secondary_location)s, secondary_location),
266268
score = COALESCE(%(score)s, score)
267269
WHERE
268270
id = %(id)s
@@ -271,7 +273,8 @@ async def update_signal(cursor: AsyncCursor, signal: Signal) -> int | None:
271273
;
272274
"""
273275
await cursor.execute(query, signal.model_dump())
274-
if (row := await cursor.fetchone()) is None:
276+
row = await cursor.fetchone()
277+
if row is None:
275278
return None
276279
signal_id = row["id"]
277280

@@ -308,7 +311,8 @@ async def delete_signal(cursor: AsyncCursor, uid: int) -> Signal | None:
308311
"""
309312
query = "DELETE FROM signals WHERE id = %s RETURNING *;"
310313
await cursor.execute(query, (uid,))
311-
if (row := await cursor.fetchone()) is None:
314+
row = await cursor.fetchone()
315+
if row is None:
312316
return None
313317
signal = Signal(**row)
314318
if signal.attachment is not None:
@@ -358,7 +362,8 @@ async def read_user_signals(
358362
;
359363
"""
360364
await cursor.execute(query, (user_email, status))
361-
return [Signal(**row) async for row in cursor]
365+
rows = await cursor.fetchall()
366+
return [Signal(**row) for row in rows]
362367

363368

364369
async def is_signal_favorited(cursor: AsyncCursor, user_email: str, signal_id: int) -> bool:

src/entities/signal.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ class Signal(BaseEntity):
2424
default=None,
2525
description="Region and/or country for which this signal has greatest relevance.",
2626
)
27+
secondary_location: list[str] | None = Field(
28+
default=None,
29+
description="Additional regions and/or countries for which this signal has relevance.",
30+
)
2731
score: utils.Score | None = Field(default=None)
2832
connected_trends: list[int] | None = Field(
2933
default=None,
@@ -36,13 +40,17 @@ class Signal(BaseEntity):
3640

3741
model_config = ConfigDict(
3842
json_schema_extra={
39-
"example": BaseEntity.model_config["json_schema_extra"]["example"]
40-
| {
43+
"example": {
44+
"id": 1,
45+
"created_unit": "HQ",
4146
"url": "https://undp.medium.com/the-cost-of-corruption-a827306696fb",
4247
"relevance": "Of the approximately US$13 trillion that governments spend on public spending, up to 25 percent is lost to corruption.",
4348
"keywords": ["economy", "governance"],
4449
"location": "Global",
45-
"favorite": False,
50+
"secondary_location": ["Africa", "Asia"],
51+
"score": None,
52+
"connected_trends": [101, 102],
53+
"favorite": False
4654
}
4755
}
4856
)

0 commit comments

Comments
 (0)