Skip to content

Commit 821263e

Browse files
committed
Compute by both TCL by driver and year
1 parent 7c587c8 commit 821263e

File tree

3 files changed

+77
-22
lines changed

3 files changed

+77
-22
lines changed

app/models/pydantic/datamart.py

+44-5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import Dict, Optional, Union
66
from uuid import UUID
77

8+
import pandas as pd
89
from pydantic import Field
910

1011
from app.models.pydantic.responses import Response
@@ -69,8 +70,36 @@ class TreeCoverLossByDriverMetadata(DataMartMetadata):
6970
canopy_cover: int
7071

7172

73+
class TreeCoverLossByDriverResult(StrictBaseModel):
74+
tree_cover_loss_by_driver: Dict[str, float]
75+
yearly_tree_cover_loss_by_driver: Dict[str, Dict[str, float]]
76+
77+
@staticmethod
78+
def from_rows(rows):
79+
df = pd.DataFrame(rows)
80+
by_year = (
81+
df.groupby(["umd_tree_cover_loss__year"])
82+
.apply(
83+
lambda x: dict(
84+
zip(x["tsc_tree_cover_loss_drivers__driver"], x["area__ha"])
85+
)
86+
)
87+
.to_dict()
88+
)
89+
by_driver = (
90+
df.drop(["umd_tree_cover_loss__year"], axis=1)
91+
.groupby(["tsc_tree_cover_loss_drivers__driver"])
92+
.sum()
93+
.to_dict()["area__ha"]
94+
)
95+
return TreeCoverLossByDriverResult(
96+
tree_cover_loss_by_driver=by_driver,
97+
yearly_tree_cover_loss_by_driver=by_year,
98+
)
99+
100+
72101
class TreeCoverLossByDriver(StrictBaseModel):
73-
result: Optional[Dict[str, float]] = Field(None, alias="tree_cover_loss_by_driver")
102+
result: Optional[TreeCoverLossByDriverResult] = None
74103
metadata: Optional[TreeCoverLossByDriverMetadata] = None
75104
message: Optional[str] = None
76105
status: AnalysisStatus
@@ -81,7 +110,7 @@ class Config:
81110

82111

83112
class TreeCoverLossByDriverUpdate(StrictBaseModel):
84-
result: Optional[Dict[str, float]] = Field(None, alias="tree_cover_loss_by_driver")
113+
result: Optional[TreeCoverLossByDriverResult] = None
85114
metadata: Optional[TreeCoverLossByDriverMetadata] = None
86115
status: Optional[AnalysisStatus] = AnalysisStatus.saved
87116
message: Optional[str] = None
@@ -101,11 +130,21 @@ def to_csv(
101130
return a temporary redirect to download URL."""
102131
csv_file = StringIO()
103132
wr = csv.writer(csv_file, quoting=csv.QUOTE_NONNUMERIC)
104-
wr.writerow(["tsc_tree_cover_loss_drivers__driver", "area__ha"])
133+
wr.writerow(
134+
[
135+
"umd_tree_cover_loss__year",
136+
"tsc_tree_cover_loss_drivers__driver",
137+
"area__ha",
138+
]
139+
)
105140

106141
if self.data.status == "saved":
107-
for driver, year in self.data.result.items():
108-
wr.writerow([driver, year])
142+
for (
143+
year,
144+
tcl_by_driver,
145+
) in self.data.result.yearly_tree_cover_loss_by_driver.items():
146+
for driver, area in tcl_by_driver.items():
147+
wr.writerow([year, driver, area])
109148

110149
csv_file.seek(0)
111150
return csv_file

app/tasks/datamart/land.py

+3-7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
AnalysisStatus,
1010
DataMartSource,
1111
TreeCoverLossByDriverMetadata,
12+
TreeCoverLossByDriverResult,
1213
TreeCoverLossByDriverUpdate,
1314
)
1415
from app.models.pydantic.geostore import GeostoreCommon
@@ -37,7 +38,7 @@ async def compute_tree_cover_loss_by_driver(
3738
f"Computing tree cover loss by driver for resource {resource_id} with geostore {geostore_id} and canopy cover {canopy_cover}"
3839
)
3940
geostore: GeostoreCommon = await get_geostore(geostore_id, GeostoreOrigin.rw)
40-
query = f"SELECT SUM(area__ha) FROM data WHERE umd_tree_cover_density_2000__threshold >= {canopy_cover} GROUP BY tsc_tree_cover_loss_drivers__driver"
41+
query = f"SELECT SUM(area__ha) FROM data WHERE umd_tree_cover_density_2000__threshold >= {canopy_cover} GROUP BY umd_tree_cover_loss__year, tsc_tree_cover_loss_drivers__driver"
4142

4243
results = await _query_dataset_json(
4344
"umd_tree_cover_loss",
@@ -47,12 +48,7 @@ async def compute_tree_cover_loss_by_driver(
4748
dataset_version,
4849
)
4950

50-
tcl_by_driver = {
51-
row["tsc_tree_cover_loss_drivers__driver"]: row["area__ha"]
52-
for row in results
53-
}
54-
55-
resource.result = tcl_by_driver
51+
resource.result = TreeCoverLossByDriverResult.from_rows(results)
5652
resource.status = AnalysisStatus.saved
5753
await datamart_crud.update_result(resource_id, resource)
5854

tests_v2/unit/app/routes/datamart/test_land.py

+30-10
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ async def test_get_tree_cover_loss_by_drivers_as_csv(
306306

307307
assert (
308308
response.content
309-
== b'"tsc_tree_cover_loss_drivers__driver","area__ha"\r\n"Permanent agriculture",10.0\r\n"Hard commodities",12.0\r\n"Shifting cultivation",7.0\r\n"Forest management",93.4\r\n"Wildfires",42.0\r\n"Settlements and infrastructure",13.562\r\n"Other natural disturbances",6.0\r\n'
309+
== b'"umd_tree_cover_loss__year","tsc_tree_cover_loss_drivers__driver","area__ha"\r\n"2001","Permanent agriculture",10.0\r\n"2001","Hard commodities",12.0\r\n"2001","Shifting cultivation",7.0\r\n"2001","Forest management",93.4\r\n"2001","Wildfires",42.0\r\n"2001","Settlements and infrastructure",13.562\r\n"2001","Other natural disturbances",6.0\r\n'
310310
)
311311

312312

@@ -333,7 +333,7 @@ async def test_compute_tree_cover_loss_by_driver(geostore):
333333
mock_query_dataset_json.assert_awaited_once_with(
334334
"umd_tree_cover_loss",
335335
"v1.8",
336-
"SELECT SUM(area__ha) FROM data WHERE umd_tree_cover_density_2000__threshold >= 30 GROUP BY tsc_tree_cover_loss_drivers__driver",
336+
"SELECT SUM(area__ha) FROM data WHERE umd_tree_cover_density_2000__threshold >= 30 GROUP BY umd_tree_cover_loss__year, tsc_tree_cover_loss_drivers__driver",
337337
geostore_common,
338338
DEFAULT_LAND_DATASET_VERSIONS | {"umd_tree_cover_loss": "v1.8"},
339339
)
@@ -376,30 +376,37 @@ async def test_compute_tree_cover_loss_by_driver_error(geostore):
376376

377377
MOCK_RESULT = [
378378
{
379+
"umd_tree_cover_loss__year": "2001",
379380
"tsc_tree_cover_loss_drivers__driver": "Permanent agriculture",
380381
"area__ha": 10,
381382
},
382383
{
384+
"umd_tree_cover_loss__year": "2001",
383385
"tsc_tree_cover_loss_drivers__driver": "Hard commodities",
384386
"area__ha": 12,
385387
},
386388
{
389+
"umd_tree_cover_loss__year": "2001",
387390
"tsc_tree_cover_loss_drivers__driver": "Shifting cultivation",
388391
"area__ha": 7,
389392
},
390393
{
394+
"umd_tree_cover_loss__year": "2001",
391395
"tsc_tree_cover_loss_drivers__driver": "Forest management",
392396
"area__ha": 93.4,
393397
},
394398
{
399+
"umd_tree_cover_loss__year": "2001",
395400
"tsc_tree_cover_loss_drivers__driver": "Wildfires",
396401
"area__ha": 42,
397402
},
398403
{
404+
"umd_tree_cover_loss__year": "2001",
399405
"tsc_tree_cover_loss_drivers__driver": "Settlements and infrastructure",
400406
"area__ha": 13.562,
401407
},
402408
{
409+
"umd_tree_cover_loss__year": "2001",
403410
"tsc_tree_cover_loss_drivers__driver": "Other natural disturbances",
404411
"area__ha": 6,
405412
},
@@ -409,14 +416,27 @@ async def test_compute_tree_cover_loss_by_driver_error(geostore):
409416
MOCK_RESOURCE = {
410417
"status": "saved",
411418
"message": None,
412-
"tree_cover_loss_by_driver": {
413-
"Permanent agriculture": 10.0,
414-
"Hard commodities": 12.0,
415-
"Shifting cultivation": 7.0,
416-
"Forest management": 93.4,
417-
"Wildfires": 42.0,
418-
"Settlements and infrastructure": 13.562,
419-
"Other natural disturbances": 6.0,
419+
"result": {
420+
"tree_cover_loss_by_driver": {
421+
"Permanent agriculture": 10.0,
422+
"Hard commodities": 12.0,
423+
"Shifting cultivation": 7.0,
424+
"Forest management": 93.4,
425+
"Wildfires": 42.0,
426+
"Settlements and infrastructure": 13.562,
427+
"Other natural disturbances": 6.0,
428+
},
429+
"yearly_tree_cover_loss_by_driver": {
430+
"2001": {
431+
"Permanent agriculture": 10.0,
432+
"Hard commodities": 12.0,
433+
"Shifting cultivation": 7.0,
434+
"Forest management": 93.4,
435+
"Wildfires": 42.0,
436+
"Settlements and infrastructure": 13.562,
437+
"Other natural disturbances": 6.0,
438+
}
439+
},
420440
},
421441
"metadata": {
422442
"geostore_id": "",

0 commit comments

Comments
 (0)