ammarnasr commited on
Commit
ab52166
·
1 Parent(s): a006bef

Milestone 1

Browse files
Files changed (7) hide show
  1. fields_asim.parquet +0 -0
  2. history_asim.csv +3 -0
  3. pag/monitor.py +174 -121
  4. process.py +3 -3
  5. tokens.txt +11 -0
  6. tokens_expired.txt +6 -0
  7. utils.py +95 -0
fields_asim.parquet ADDED
Binary file (6.15 kB). View file
 
history_asim.csv ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ field_name,start_date,end_date,crop,irrigation_method
2
+ square_one,2024-05-01,2024-07-26,Wheat,Rainfed
3
+ uk_farm,2024-08-01,2024-10-31,Corn,Irrigated
pag/monitor.py CHANGED
@@ -32,12 +32,16 @@ def check_authentication():
32
 
33
 
34
  config = SHConfig()
35
- config.instance_id = '352670fb-2edf-4abd-90c8-437485a2403e'
36
- config.sh_client_id = 'ca95f10f-443c-4c60-9a36-98950292bb9b'
37
- config.sh_client_secret = 'rNFGRxGNiNFrXJfGyHIkVRyGOrdWNsfI'
38
  config.sh_timesfm_IP = "34.121.141.161"
39
- OpenAI_key = os.getenv('OPENAI_KEY')
40
- client = OpenAI(api_key= OpenAI_key)
 
 
 
 
41
 
42
  def select_field(gdf):
43
  st.markdown("""
@@ -137,7 +141,7 @@ def download_date_data(df, field, dates, metric, clientName,):
137
 
138
 
139
  def track(metric, field_name, src_df, client_name):
140
- st.title(":green[Select Date and Start Monitoring]")
141
  dates = []
142
  date = -1
143
  if 'dates' not in st.session_state:
@@ -170,9 +174,10 @@ def track(metric, field_name, src_df, client_name):
170
  .stSelectbox > div > div {cursor: pointer;}
171
  </style>
172
  """, unsafe_allow_html=True)
 
173
  date = st.selectbox('Select Observation Date: ', dates, index=len(dates)-1, key=f'Select Date Dropdown Menu - {metric}')
174
  if date != -1:
175
- st.success(f'You selected: {date}')
176
  #Add the date to the session state
177
  st.session_state['date'] = date
178
  else:
@@ -182,7 +187,7 @@ def track(metric, field_name, src_df, client_name):
182
 
183
 
184
  st.markdown('---')
185
- st.header('Show Field Data')
186
 
187
  # If a field and a date are selected, display the field data
188
  if date != -1:
@@ -232,9 +237,10 @@ def track(metric, field_name, src_df, client_name):
232
  )
233
 
234
  # Add the base map
235
- token = open("token.mapbox_token").read()
236
- fig.update_layout(mapbox_style="satellite", mapbox_accesstoken=token)
237
- st.plotly_chart(fig)
 
238
 
239
  #Dwonload Links
240
 
@@ -287,10 +293,9 @@ def track(metric, field_name, src_df, client_name):
287
 
288
 
289
  def monitor_fields():
 
290
  row1,row2 = st.columns([1,2])
291
  with row1:
292
- st.title(":orange[Field Monitoring]")
293
-
294
  current_user = greeting("Let's take a look how these fields are doing")
295
  if os.path.exists(f"fields_{current_user}.parquet"):
296
  gdf = gpd.read_parquet(f"fields_{current_user}.parquet")
@@ -299,125 +304,173 @@ def monitor_fields():
299
  st.info("No Field Selected Yet!")
300
  else:
301
  metric = st.radio("Select Metric to Monitor", ["NDVI", "LAI", "CAB"], key="metric", index=0, help="Select the metric to monitor")
302
- st.success(f"Monitoring {metric} for {field_name}")
303
  with st.expander("Metrics Explanation", expanded=False):
304
  st.write("NDVI: Normalized Difference Vegetation Index, Mainly used to monitor the health of vegetation")
305
  st.write("LAI: Leaf Area Index, Mainly used to monitor the productivity of vegetation")
306
  st.write("CAB: Chlorophyll Absorption in the Blue band, Mainly used to monitor the chlorophyll content in vegetation")
307
  # st.write("NDMI: Normalized Difference Moisture Index, Mainly used to monitor the moisture content in vegetation")
308
- st.info("More metrics and analysis features will be added soon")
309
  else:
310
  st.info("No Fields Added Yet!")
311
  return
312
-
313
  if field_name != "Select Field":
314
- st.title(":orange[Predict Metrics for Next Month]")
315
- st.write(f"Press the button below to predict {metric} for the next 30 weeks")
316
- subcol1, subcol2, subcol3 = st.columns(3)
317
- if subcol2.button(f'Predict & Recommend'):
318
-
319
- start_date = '2024-01-01'
320
- today = datetime.today()
321
- end_date = today.strftime('%Y-%m-%d')
322
- year = '2024'
323
-
324
- dates = get_and_cache_available_dates(gdf, field_name, year, start_date, end_date)
325
- my_bar = st.progress(0, text= f"Downloading Data for the last {len(dates)//6} months ...")
326
- counter = 0
327
- downloaded_prev_metrics = []
328
- for index, date in enumerate(dates):
329
- # time.sleep(0.1)
330
- metric_data = get_cuarted_df_for_field(gdf, field_name, date, metric, current_user, dates = None)
331
- cloud_cover_data = get_cuarted_df_for_field(gdf, field_name, date, 'CLP', current_user, dates = None)
332
- field_data = metric_data.merge(cloud_cover_data, on='geometry')
333
- avg_metric = field_data[f'{metric}_{date}'].mean()
334
- downloaded_prev_metrics.append((date, avg_metric))
335
- counter = counter + 100/(len(dates))
336
- my_bar.progress(round(counter), text=f"Downloading Data for the last {len(dates)//6} months: {round(counter)}%")
337
-
338
- st.subheader('Predictions:')
339
- # chart_data = pd.DataFrame(
340
- # {
341
- # "date": [metric[0] for metric in downloaded_prev_metrics],
342
- # f"{metric}": [metric[1] for metric in downloaded_prev_metrics],
343
- # }
344
- # )
345
-
346
- # st.area_chart(chart_data, x="date", y=f"{metric}")
347
- channel = grpc.insecure_channel(f"{config.sh_timesfm_IP}:50051")
348
- print("runing client request")
349
- stub = pb.timesfm_pb2_grpc.PredictAgriStub(channel)
350
- features = stub.predict_metric(iter([pb.timesfm_pb2.prev_values(value=metric[1], date=metric[0]) for metric in downloaded_prev_metrics]))
351
- print("server streaming:")
352
- predictions = []
353
- for feature in features:
354
- predictions.append(feature.value)
355
- # do something with the returned output
356
- # print(predictions)
357
- future_dates = []
358
- # print(dates[0])
359
- curr_date = datetime.today()
360
- for pred in predictions:
361
- curr_date = curr_date + timedelta(days=7)
362
- future_dates.append(curr_date.strftime('%Y-%m-%d'))
363
-
364
- prev_dates = [metric[0] for metric in downloaded_prev_metrics]
365
- history_metric_data = [metric[1] for metric in downloaded_prev_metrics]
366
- future_metric_data = predictions
367
- interval_dates = prev_dates
368
- interval_dates.extend(future_dates)
369
- history_metric_data.extend([0 for i in range(len(predictions))])
370
- masked_future_metric_data = [0 for i in range(len([metric[1] for metric in downloaded_prev_metrics]))]
371
- masked_future_metric_data.extend(future_metric_data)
372
- # print(f"interval_dates:{len(interval_dates)}")
373
- # print(f"history_metric_data:{len(history_metric_data)}")
374
- # print(f"masked_future_metric_data:{len(masked_future_metric_data)}")
375
- # print(predictions)
376
-
377
- # print(interval_dates)
378
- prediction_chart_data = pd.DataFrame(
379
- {
380
- f"history_{metric}_values": history_metric_data,
381
- f"predicted_{metric}_values":masked_future_metric_data,
382
- f"date": interval_dates,
383
- }
384
- )
385
-
386
- # print(prediction_chart_data)
387
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
388
  st.area_chart(prediction_chart_data, x="date", y=[f"history_{metric}_values", f"predicted_{metric}_values"])
389
-
390
  st.subheader('Recommendation:')
391
- crop = "Wheat"
392
-
393
- try:
394
- prompt = f"given the {metric} values weekly for the next 30 weeks, comment if they are appropriate to grow {crop} (write one paragraph showing your conclusion): {metric} values:{predictions}"
395
- response = client.chat.completions.create(
396
- model="gpt-4o",
397
- messages=[
398
- {
399
- "role": "user",
400
- "content": prompt
401
- }
402
- ],
403
- temperature=1,
404
- max_tokens=256,
405
- top_p=1,
406
- frequency_penalty=0,
407
- presence_penalty=0
408
- )
409
- st.caption(response.choices[0].message.content)
410
- except:
411
- st.code("Server Error: Could't generate recommendation!")
412
-
413
-
414
-
415
-
416
- with row2:
417
- if field_name != "Select Field":
418
- track(metric, field_name, gdf, current_user)
419
-
420
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
421
 
422
 
423
  if __name__ == '__main__':
 
32
 
33
 
34
  config = SHConfig()
35
+ config.instance_id = '44e79764-8b9d-43b0-a4bf-15799db2899d'
36
+ config.sh_client_id = '4ae34b53-3f81-4ba0-9c7d-b6fb0606dac3'
37
+ config.sh_client_secret = '3IPSSqE75fqK38vP85hxttR9PJEs5OxX'
38
  config.sh_timesfm_IP = "34.121.141.161"
39
+ try:
40
+ OpenAI_key = os.getenv('OPENAI_KEY')
41
+ client = OpenAI(api_key= OpenAI_key)
42
+ except:
43
+ OpenAI_key = "sk-"
44
+ client = OpenAI(api_key= OpenAI_key)
45
 
46
  def select_field(gdf):
47
  st.markdown("""
 
141
 
142
 
143
  def track(metric, field_name, src_df, client_name):
144
+ st.subheader(":green[Select Date and Start Monitoring]")
145
  dates = []
146
  date = -1
147
  if 'dates' not in st.session_state:
 
174
  .stSelectbox > div > div {cursor: pointer;}
175
  </style>
176
  """, unsafe_allow_html=True)
177
+ dates.append(-1)
178
  date = st.selectbox('Select Observation Date: ', dates, index=len(dates)-1, key=f'Select Date Dropdown Menu - {metric}')
179
  if date != -1:
180
+ st.write(f'You selected: {date}')
181
  #Add the date to the session state
182
  st.session_state['date'] = date
183
  else:
 
187
 
188
 
189
  st.markdown('---')
190
+ st.subheader('Show Field Data')
191
 
192
  # If a field and a date are selected, display the field data
193
  if date != -1:
 
237
  )
238
 
239
  # Add the base map
240
+ with st.expander("Show Map", expanded=False):
241
+ token = open("token.mapbox_token").read()
242
+ fig.update_layout(mapbox_style="satellite", mapbox_accesstoken=token)
243
+ st.plotly_chart(fig, use_container_width=True)
244
 
245
  #Dwonload Links
246
 
 
293
 
294
 
295
  def monitor_fields():
296
+ st.title(":orange[Field Monitoring]")
297
  row1,row2 = st.columns([1,2])
298
  with row1:
 
 
299
  current_user = greeting("Let's take a look how these fields are doing")
300
  if os.path.exists(f"fields_{current_user}.parquet"):
301
  gdf = gpd.read_parquet(f"fields_{current_user}.parquet")
 
304
  st.info("No Field Selected Yet!")
305
  else:
306
  metric = st.radio("Select Metric to Monitor", ["NDVI", "LAI", "CAB"], key="metric", index=0, help="Select the metric to monitor")
307
+ st.write(f"Monitoring {metric} for {field_name}")
308
  with st.expander("Metrics Explanation", expanded=False):
309
  st.write("NDVI: Normalized Difference Vegetation Index, Mainly used to monitor the health of vegetation")
310
  st.write("LAI: Leaf Area Index, Mainly used to monitor the productivity of vegetation")
311
  st.write("CAB: Chlorophyll Absorption in the Blue band, Mainly used to monitor the chlorophyll content in vegetation")
312
  # st.write("NDMI: Normalized Difference Moisture Index, Mainly used to monitor the moisture content in vegetation")
313
+
314
  else:
315
  st.info("No Fields Added Yet!")
316
  return
317
+ with row2:
318
  if field_name != "Select Field":
319
+ track(metric, field_name, gdf, current_user)
320
+
321
+ if field_name != "Select Field":
322
+ st.title(":orange[Field Health Forecast]")
323
+ st.write(f"Press the button below to predict {metric} for the next 30 weeks")
324
+ if 'api_token_confirmed' not in st.session_state:
325
+ st.session_state['api_token'] = ''
326
+ st.session_state['api_token_confirmed'] = False
327
+ if not st.session_state['api_token_confirmed']:
328
+ with st.empty():
329
+ st.warning("No Valid API Token Found")
330
+ st.info("You can get the API Token from the Service Page on the SNET Platform (link to the service page)")
331
+ st.info("For Testing purposes, you can use: TEST_TOKEN")
332
+ api_token = st.text_input("API Token", key="api_token_input", help="Enter the API Token From SNET")
333
+ if st.button("submit API Token", key="confirm_api_token"):
334
+ if utils.confirm_api_token(api_token):
335
+ st.session_state['api_token'] = api_token
336
+ st.session_state['api_token_confirmed'] = True
337
+ st.session_state['valid_until'] = utils.load_token_expiration(api_token).strftime('%Y-%m-%d %H:%M:%S')
338
+ st.rerun()
339
+ else:
340
+ st.error("Invalid API Token")
341
+ else:
342
+ st.success(f"API Token Confirmed valid until {st.session_state['valid_until']}")
343
+ lookback_days = st.slider("Select Lookback Days", 30, 365, 60, step=30,key="lookback_days", help="Large lookback days may take longer to load")
344
+ subcol1, subcol2, subcol3 = st.columns(3)
345
+
346
+ if subcol2.button(f'Predict & Recommend', key="predict_button", disabled=not st.session_state['api_token_confirmed']):
347
+ # start_date = '2024-01-01'
348
+ today = datetime.today()
349
+ end_date = today.strftime('%Y-%m-%d')
350
+ start_date = today - timedelta(days=lookback_days)
351
+ start_date = start_date.strftime('%Y-%m-%d')
352
+ year = '2024'
353
+
354
+ dates = get_and_cache_available_dates(gdf, field_name, year, start_date, end_date)
355
+ newest_date, oldest_date = dates[0], dates[-1]
356
+ number_of_months = (datetime.strptime(newest_date, '%Y-%m-%d') - datetime.strptime(oldest_date, '%Y-%m-%d')).days//30
357
+ my_bar = st.progress(0, text= f"Downloading Data for the last {number_of_months+1} months ...")
358
+ counter = 0
359
+ downloaded_prev_metrics = []
360
+ for index, date in enumerate(dates):
361
+ # time.sleep(0.1)
362
+ metric_data = get_cuarted_df_for_field(gdf, field_name, date, metric, current_user, dates = None)
363
+ # cloud_cover_data = get_cuarted_df_for_field(gdf, field_name, date, 'CLP', current_user, dates = None)
364
+ # field_data = metric_data.merge(cloud_cover_data, on='geometry')
365
+ avg_metric = metric_data[f'{metric}_{date}'].mean()
366
+ downloaded_prev_metrics.append((date, avg_metric))
367
+ counter = counter + 100/(len(dates))
368
+ my_bar.progress(round(counter), text=f"Downloading Data for the last {len(dates)//6} months: {round(counter)}%")
369
+
370
+ st.subheader('Predictions:')
371
+ # chart_data = pd.DataFrame(
372
+ # {
373
+ # "date": [metric[0] for metric in downloaded_prev_metrics],
374
+ # f"{metric}": [metric[1] for metric in downloaded_prev_metrics],
375
+ # }
376
+ # )
377
+
378
+ # st.area_chart(chart_data, x="date", y=f"{metric}")
379
+ channel = grpc.insecure_channel(f"{config.sh_timesfm_IP}:50051")
380
+ print("runing client request")
381
+ stub = pb.timesfm_pb2_grpc.PredictAgriStub(channel)
382
+ features = stub.predict_metric(iter([pb.timesfm_pb2.prev_values(value=metric[1], date=metric[0]) for metric in downloaded_prev_metrics]))
383
+ print("server streaming:")
384
+ predictions = []
385
+ for feature in features:
386
+ predictions.append(feature.value)
387
+ # do something with the returned output
388
+ # print(predictions)
389
+ future_dates = []
390
+ # print(dates[0])
391
+ curr_date = datetime.today()
392
+ for pred in predictions:
393
+ curr_date = curr_date + timedelta(days=7)
394
+ future_dates.append(curr_date.strftime('%Y-%m-%d'))
395
+
396
+ prev_dates = [metric[0] for metric in downloaded_prev_metrics]
397
+ history_metric_data = [metric[1] for metric in downloaded_prev_metrics]
398
+ future_metric_data = predictions
399
+ interval_dates = prev_dates
400
+ interval_dates.extend(future_dates)
401
+ history_metric_data.extend([0 for i in range(len(predictions))])
402
+ masked_future_metric_data = [0 for i in range(len([metric[1] for metric in downloaded_prev_metrics]))]
403
+ masked_future_metric_data.extend(future_metric_data)
404
+ # print(f"interval_dates:{len(interval_dates)}")
405
+ # print(f"history_metric_data:{len(history_metric_data)}")
406
+ # print(f"masked_future_metric_data:{len(masked_future_metric_data)}")
407
+ # print(predictions)
408
+
409
+ # print(interval_dates)
410
+ prediction_chart_data = pd.DataFrame(
411
+ {
412
+ f"history_{metric}_values": history_metric_data,
413
+ f"predicted_{metric}_values":masked_future_metric_data,
414
+ f"date": interval_dates,
415
+ }
416
+ )
417
+
418
+ # print(prediction_chart_data)
419
+ graph_col, recommendation_col = st.columns([1,1])
420
+ with graph_col:
421
  st.area_chart(prediction_chart_data, x="date", y=[f"history_{metric}_values", f"predicted_{metric}_values"])
422
+ with recommendation_col:
423
  st.subheader('Recommendation:')
424
+ with st.spinner("Generating Recommendation..."):
425
+ crop = gdf.loc[gdf['name'] == field_name].crop if 'crop' in gdf.columns else "Wheat"
426
+
427
+
428
+ try:
429
+ weeks = future_dates
430
+ gdf_loc = gdf.loc[gdf['name'] == field_name].reset_index(drop=True)
431
+ location = utils.get_region_from_coordinates(gdf_loc.geometry[0].centroid.y, gdf_loc.geometry[0].centroid.x)
432
+ prompt = f"""The Field Name is {field_name} and is located in {location}.
433
+ Analyze {crop} growth conditions for the next {len(weeks)} weeks starting from {weeks[0]} to {weeks[-1]} based on the Forecatsed {metric} values weekly.
434
+ {metric}: {predictions}
435
+
436
+ Provide a concise Short report:
437
+
438
+ 1. Field Status (use format "Category: Status - One sentence comment", (e.g. Overall Health: Low - The NDVI values consistently below 0.2, indicating weak vegetative growth.)
439
+ - Overall Health:
440
+ - Growth Stage:
441
+ - Pest Risk:
442
+ - Disease Risk:
443
+ - Stress Level:
444
+
445
+ 2. Yield Forecast:
446
+ [look online for the expected yield for the crop in the region based {metric} values]
447
+
448
+ 3. Recommendation:
449
+ [one actionable advice reasoned based on the forecasted {metric} values, season, crop, and region]
450
+
451
+ """
452
+ # prompt = f"given the {metric} values weekly for the next 30 weeks, comment if they are appropriate to grow {crop} (write one paragraph showing your conclusion): {metric} values:{predictions}"
453
+ response = client.chat.completions.create(
454
+ model="gpt-4o",
455
+ messages=[
456
+ {
457
+ "role": "user",
458
+ "content": prompt
459
+ }
460
+ ],
461
+ temperature=1,
462
+ max_tokens=256,
463
+ top_p=1,
464
+ frequency_penalty=0,
465
+ presence_penalty=0
466
+ )
467
+ st.markdown(response.choices[0].message.content)
468
+ except Exception as e:
469
+ st.code("Server Error: Could't generate recommendation!")
470
+ st.error(e)
471
+
472
+
473
+
474
 
475
 
476
  if __name__ == '__main__':
process.py CHANGED
@@ -6,9 +6,9 @@ from sentinelhub import SHConfig, MimeType
6
 
7
 
8
  config = SHConfig()
9
- config.instance_id = '352670fb-2edf-4abd-90c8-437485a2403e'
10
- config.sh_client_id = 'ca95f10f-443c-4c60-9a36-98950292bb9b'
11
- config.sh_client_secret = 'rNFGRxGNiNFrXJfGyHIkVRyGOrdWNsfI'
12
  config.sh_timesfm_IP = "34.121.141.161"
13
 
14
  def Download_image_in_given_date(clientName, metric, df, field, date, mime_type = MimeType.TIFF):
 
6
 
7
 
8
  config = SHConfig()
9
+ config.instance_id = '44e79764-8b9d-43b0-a4bf-15799db2899d'
10
+ config.sh_client_id = '4ae34b53-3f81-4ba0-9c7d-b6fb0606dac3'
11
+ config.sh_client_secret = '3IPSSqE75fqK38vP85hxttR9PJEs5OxX'
12
  config.sh_timesfm_IP = "34.121.141.161"
13
 
14
  def Download_image_in_given_date(clientName, metric, df, field, date, mime_type = MimeType.TIFF):
tokens.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ k20rjxb2muforgz4eoh5b3rw8oaizsia
2
+ pbmhjy7e8943e65i6cfwk8sixfwnj7ky
3
+ zx57n4wyv34gd0unfs4y0kpjkfdgshx7
4
+ ox5680uleqj9ea0jhqcl35a20qhlr3zm
5
+ 9mfg7acflhkbif70bnrn2lq3ult5t7l5
6
+ gof83vvcxu6lrwqmfjnp3v0048rpbzid
7
+ klhbf2c2o7l0g7d9z13kkp2z4q2r9v6i
8
+ oijpgewezt9d39886829szmmryfr2k9y
9
+ c4selw18hwft2mloqe9uv1ddozspmjbo
10
+ kfpo6khkcuirmkshjufdob6k1dn9a2oo
11
+ TEST_TOKEN
tokens_expired.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ 9mfg7acflhkbif70bnrn2lq3ult5t7l5,2024-07-26T20:38:48.882401
2
+ 3swbgq8p1x6wo4kt8vn9apus3y1trlii,2024-07-26T20:08:55.102588
3
+ 9mfg7acflhkbif70bnrn2lq3ult5t7l5,2024-07-26T20:09:19.569408
4
+ 9mfg7acflhkbif70bnrn2lq3ult5t7l5,2024-07-26T19:39:42.164519
5
+ kfpo6khkcuirmkshjufdob6k1dn9a2oo,2025-07-26T20:45:07.000759
6
+ TEST_TOKEN,2025-07-26T20:45:07.000759
utils.py CHANGED
@@ -7,6 +7,101 @@ import geopandas as gpd
7
  from shapely.geometry import Point
8
  from PIL import Image
9
  from tqdm import tqdm
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  # Initialzie custom basemaps for folium
12
  basemaps = {
 
7
  from shapely.geometry import Point
8
  from PIL import Image
9
  from tqdm import tqdm
10
+ import geopy
11
+ from geopy.geocoders import Nominatim
12
+ from geopy.exc import GeocoderTimedOut, GeocoderUnavailable
13
+
14
+ import random
15
+ import string
16
+ import os
17
+ from datetime import datetime, timedelta
18
+
19
+ TOKEN_FILE = "tokens.txt"
20
+ EXPIRED_FILE = "tokens_expired.txt"
21
+
22
+ def generate_random_unique_tokens(num_tokens=10, token_file=TOKEN_FILE):
23
+ '''Generates a list of random unique tokens and saves them to a file.'''
24
+ if not os.path.exists(token_file):
25
+ with open(token_file, 'w') as f:
26
+ tokens = set()
27
+ while len(tokens) < num_tokens:
28
+ token = ''.join(random.choices(string.ascii_lowercase + string.digits, k=32))
29
+ tokens.add(token)
30
+ for token in tokens:
31
+ f.write(token + '\n')
32
+ else:
33
+ with open(token_file, 'r') as f:
34
+ tokens = set(f.read().splitlines())
35
+ with open(token_file, 'a') as f:
36
+ while len(tokens) < num_tokens:
37
+ token = ''.join(random.choices(string.ascii_lowercase + string.digits, k=32))
38
+ if token not in tokens:
39
+ tokens.add(token)
40
+ f.write(token + '\n')
41
+ return tokens
42
+
43
+ def confirm_api_token(token, token_file=TOKEN_FILE, expired_file=EXPIRED_FILE):
44
+ '''Checks if the given token is valid and not expired.'''
45
+ with open(token_file, 'r') as f:
46
+ tokens = set(f.read().splitlines())
47
+ if token in tokens:
48
+ now = datetime.now()
49
+ if token in load_expired_tokens(expired_file):
50
+ if now < load_token_expiration(token, expired_file):
51
+ return True
52
+ else:
53
+ expiry_date = now + timedelta(hours=1)
54
+ save_expired_token(token, expiry_date, expired_file)
55
+ return True
56
+ return False
57
+
58
+ def load_expired_tokens(expired_file=EXPIRED_FILE):
59
+ '''Loads expired tokens from the file.'''
60
+ expired_tokens = {}
61
+ if os.path.exists(expired_file):
62
+ with open(expired_file, 'r') as f:
63
+ for line in f:
64
+ token, expiry_date = line.strip().split(',')
65
+ expired_tokens[token] = datetime.fromisoformat(expiry_date)
66
+ return expired_tokens
67
+
68
+ def load_token_expiration(token, expired_file=EXPIRED_FILE):
69
+ '''Loads the expiration date for a given token.'''
70
+ expired_tokens = load_expired_tokens(expired_file)
71
+ return expired_tokens.get(token)
72
+
73
+ def save_expired_token(token, expiry_date, expired_file=EXPIRED_FILE):
74
+ '''Saves expired tokens to the file.'''
75
+ if not os.path.exists(expired_file):
76
+ with open(expired_file, 'w') as f:
77
+ f.write(f"{token},{expiry_date.isoformat()}\n")
78
+ else:
79
+ with open(expired_file, 'a') as f:
80
+ f.write(f"{token},{expiry_date.isoformat()}\n")
81
+
82
+
83
+ def get_region_from_coordinates(latitude, longitude, max_retries=3):
84
+ geolocator = Nominatim(user_agent="my_agent")
85
+
86
+ for attempt in range(max_retries):
87
+ try:
88
+ location = geolocator.reverse(f"{latitude}, {longitude}")
89
+ if location and location.raw.get('address'):
90
+ address = location.raw['address']
91
+ # Try to get the most relevant administrative level
92
+ for level in ['state', 'county', 'region', 'province', 'district']:
93
+ if level in address:
94
+ return address[level]
95
+ # If no specific region is found, return the country
96
+ if 'country' in address:
97
+ return address['country']
98
+ return "Region not found"
99
+ except (GeocoderTimedOut, GeocoderUnavailable):
100
+ if attempt == max_retries - 1:
101
+ return "Geocoding service unavailable"
102
+
103
+ return "Failed to retrieve region information"
104
+
105
 
106
  # Initialzie custom basemaps for folium
107
  basemaps = {