mbuuck commited on
Commit
102f4ad
1 Parent(s): bccf193

User sees default projects in addition to theirs

Browse files
Files changed (3) hide show
  1. app.py +12 -3
  2. utils/duckdb_queries.py +1 -1
  3. utils/indicators.py +31 -24
app.py CHANGED
@@ -22,22 +22,31 @@ with gr.Blocks() as demo:
22
  headers=["Year", "Project Name", "Score"],
23
  datatype=["number", "str", "number"],
24
  label="Biodiversity scores by year",
 
25
  )
26
  calc_btn.click(
27
  indexgenerator.calculate_biodiversity_score,
28
- inputs=[start_year, end_year, project_name],
29
  outputs=results_df,
30
  )
31
  view_btn.click(
32
  fn=indexgenerator.show_project_map,
33
- inputs=[project_name],
34
  outputs=[m1],
35
  )
36
 
37
  def update_project_dropdown_list(url_params):
38
  username = url_params.get("username", "default")
39
  projects = dq.list_projects_by_author(author_id=username)
40
- return gr.Dropdown.update(choices=projects["name"].tolist())
 
 
 
 
 
 
 
 
 
41
 
42
  # Get url params
43
  url_params = gr.JSON({"username": "default"}, visible=False, label="URL Params")
 
22
  headers=["Year", "Project Name", "Score"],
23
  datatype=["number", "str", "number"],
24
  label="Biodiversity scores by year",
25
+ row_count=0,
26
  )
27
  calc_btn.click(
28
  indexgenerator.calculate_biodiversity_score,
29
+ inputs=[start_year, end_year],
30
  outputs=results_df,
31
  )
32
  view_btn.click(
33
  fn=indexgenerator.show_project_map,
 
34
  outputs=[m1],
35
  )
36
 
37
  def update_project_dropdown_list(url_params):
38
  username = url_params.get("username", "default")
39
  projects = dq.list_projects_by_author(author_id=username)
40
+ # Initialize the first project in the list
41
+ project_names = projects['name'].tolist()
42
+ return gr.Dropdown.update(choices=project_names)
43
+
44
+ # Change the project name in the index generator object when the
45
+ # user selects a new project
46
+ project_name.change(
47
+ indexgenerator.set_project,
48
+ inputs=project_name
49
+ )
50
 
51
  # Get url params
52
  url_params = gr.JSON({"username": "default"}, visible=False, label="URL Params")
utils/duckdb_queries.py CHANGED
@@ -18,7 +18,7 @@ else:
18
  # to-do: pass con through decorator
19
  def list_projects_by_author(author_id):
20
  return con.execute(
21
- "SELECT DISTINCT name FROM project WHERE authorId = ? AND geometry != 'null'",
22
  [author_id],
23
  ).df()
24
 
 
18
  # to-do: pass con through decorator
19
  def list_projects_by_author(author_id):
20
  return con.execute(
21
+ "SELECT DISTINCT name FROM project WHERE (authorId = ? OR authorId = 'default') AND (geometry IS NOT NULL)",
22
  [author_id],
23
  ).df()
24
 
utils/indicators.py CHANGED
@@ -34,10 +34,26 @@ class IndexGenerator:
34
  # Authenticate to GEE & DuckDB
35
  self._authenticate_ee(GEE_SERVICE_ACCOUNT)
36
 
 
 
 
 
37
  # Use defined subset of indices
38
  all_indices = self._load_indices(INDICES_FILE)
39
  self.indices = {k: all_indices[k] for k in indices}
40
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  def _cloudfree(self, gee_path, daterange):
42
  """
43
  Internal method to generate a cloud-free composite.
@@ -146,7 +162,7 @@ class IndexGenerator:
146
  logging.info(f"Calculated zonal mean for {index_key}.")
147
  return out
148
 
149
- def generate_composite_index_df(self, year, project_geometry, indices=[]):
150
  data = {
151
  "metric": indices,
152
  "year": year,
@@ -175,36 +191,28 @@ class IndexGenerator:
175
  ee.Initialize(credentials)
176
  logging.info("Authenticated to Google Earth Engine.")
177
 
178
- def _calculate_yearly_index(self, years, project_name):
179
  dfs = []
180
  logging.info(years)
181
- project_geometry = dq.get_project_geometry(project_name)
182
- project_centroid = dq.get_project_centroid(project_name)
183
- # to-do: refactor to involve less transformations
184
- _polygon = json.dumps(
185
- json.loads(project_geometry[0][0])["features"][0]["geometry"]
186
- )
187
- # to-do: don't use self.roi and instead pass patameter strategically
188
- self.roi = ee.Geometry.Polygon(json.loads(_polygon)["coordinates"])
189
 
190
  # to-do: pararelize?
191
  for year in years:
192
  logging.info(year)
193
- self.project_name = project_name
194
  df = self.generate_composite_index_df(
195
- year, project_geometry, list(self.indices.keys())
196
  )
197
  dfs.append(df)
198
 
199
  # Concatenate all dataframes
200
  df_concat = pd.concat(dfs)
201
- df_concat["centroid"] = str(project_centroid)
202
- df_concat["project_name"] = project_name
203
- df_concat["geojson"] = str(project_geometry)
204
  return df_concat.round(2)
205
 
206
- # h/t: https://community.plotly.com/t/dynamic-zoom-for-mapbox/32658/12
207
- def _latlon_to_config(self, longitudes=None, latitudes=None):
 
208
  """Function documentation:\n
209
  Basic framework adopted from Krichardson under the following thread:
210
  https://community.plotly.com/t/dynamic-zoom-for-mapbox/32658/7
@@ -249,9 +257,8 @@ class IndexGenerator:
249
  # Finally, return the zoom level and the associated boundary-box center coordinates
250
  return zoom, b_box["center"]
251
 
252
- def show_project_map(self, project_name):
253
- project_geometry = dq.get_project_geometry(project_name)
254
- features = json.loads(project_geometry[0][0].replace("'", '"'))["features"]
255
  geometry = features[0]["geometry"]
256
  longitudes = np.array(geometry["coordinates"])[0, :, 0]
257
  latitudes = np.array(geometry["coordinates"])[0, :, 1]
@@ -287,15 +294,15 @@ class IndexGenerator:
287
 
288
  return fig
289
 
290
- def calculate_biodiversity_score(self, start_year, end_year, project_name):
291
  years = []
292
  for year in range(start_year, end_year):
293
- row_exists = dq.check_if_project_exists_for_year(project_name, year)
294
  if not row_exists:
295
  years.append(year)
296
 
297
  if len(years) > 0:
298
- df = self._calculate_yearly_index(years, project_name)
299
 
300
  # Write score table to `_temptable`
301
  dq.write_score_to_temptable(df)
@@ -306,5 +313,5 @@ class IndexGenerator:
306
  # UPSERT project record
307
  dq.upsert_project_record()
308
  logging.info("upserted records into motherduck")
309
- scores = dq.get_project_scores(project_name, start_year, end_year)
310
  return scores
 
34
  # Authenticate to GEE & DuckDB
35
  self._authenticate_ee(GEE_SERVICE_ACCOUNT)
36
 
37
+ self.project_name = None
38
+ self.project_geometry = None
39
+ self.project_centroid = None
40
+
41
  # Use defined subset of indices
42
  all_indices = self._load_indices(INDICES_FILE)
43
  self.indices = {k: all_indices[k] for k in indices}
44
 
45
+ def set_project(self, project_name):
46
+ self.project_name = project_name
47
+ self.project_geometry = dq.get_project_geometry(self.project_name)
48
+ self.project_centroid = dq.get_project_centroid(self.project_name)
49
+
50
+ # to-do: refactor to involve fewer transformations
51
+ _polygon = json.dumps(
52
+ json.loads(self.project_geometry[0][0])["features"][0]["geometry"]
53
+ )
54
+ # to-do: don't use self.roi and instead pass patameter strategically
55
+ self.roi = ee.Geometry.Polygon(json.loads(_polygon)["coordinates"])
56
+
57
  def _cloudfree(self, gee_path, daterange):
58
  """
59
  Internal method to generate a cloud-free composite.
 
162
  logging.info(f"Calculated zonal mean for {index_key}.")
163
  return out
164
 
165
+ def generate_composite_index_df(self, year, indices=[]):
166
  data = {
167
  "metric": indices,
168
  "year": year,
 
191
  ee.Initialize(credentials)
192
  logging.info("Authenticated to Google Earth Engine.")
193
 
194
+ def _calculate_yearly_index(self, years):
195
  dfs = []
196
  logging.info(years)
 
 
 
 
 
 
 
 
197
 
198
  # to-do: pararelize?
199
  for year in years:
200
  logging.info(year)
 
201
  df = self.generate_composite_index_df(
202
+ year, self.project_geometry, list(self.indices.keys())
203
  )
204
  dfs.append(df)
205
 
206
  # Concatenate all dataframes
207
  df_concat = pd.concat(dfs)
208
+ df_concat["centroid"] = str(self.project_centroid)
209
+ df_concat["project_name"] = self.project_name
210
+ df_concat["geojson"] = str(self.project_geometry)
211
  return df_concat.round(2)
212
 
213
+ # h/t: https://community.plotly.com/t/dynamic-zoom-for-mapbox/32658/12\
214
+ @staticmethod
215
+ def _latlon_to_config(longitudes=None, latitudes=None):
216
  """Function documentation:\n
217
  Basic framework adopted from Krichardson under the following thread:
218
  https://community.plotly.com/t/dynamic-zoom-for-mapbox/32658/7
 
257
  # Finally, return the zoom level and the associated boundary-box center coordinates
258
  return zoom, b_box["center"]
259
 
260
+ def show_project_map(self):
261
+ features = json.loads(self.project_geometry[0][0].replace("'", '"'))["features"]
 
262
  geometry = features[0]["geometry"]
263
  longitudes = np.array(geometry["coordinates"])[0, :, 0]
264
  latitudes = np.array(geometry["coordinates"])[0, :, 1]
 
294
 
295
  return fig
296
 
297
+ def calculate_biodiversity_score(self, start_year, end_year):
298
  years = []
299
  for year in range(start_year, end_year):
300
+ row_exists = dq.check_if_project_exists_for_year(self.project_name, year)
301
  if not row_exists:
302
  years.append(year)
303
 
304
  if len(years) > 0:
305
+ df = self._calculate_yearly_index(years)
306
 
307
  # Write score table to `_temptable`
308
  dq.write_score_to_temptable(df)
 
313
  # UPSERT project record
314
  dq.upsert_project_record()
315
  logging.info("upserted records into motherduck")
316
+ scores = dq.get_project_scores(self.project_name, start_year, end_year)
317
  return scores