Skip to content

Commit 06e07a0

Browse files
committed
Add new parsers for greon, usgs, and iwqis
1 parent 0d0ed5a commit 06e07a0

File tree

9 files changed

+5706
-0
lines changed

9 files changed

+5706
-0
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
3535
[IMLCZO-263](https://opensource.ncsa.illinois.edu/jira/browse/IMLCZO-263)
3636
- Add Scripts for Hourly Parsing of Datapoints
3737
[IMLCZO-267](https://opensource.ncsa.illinois.edu/jira/browse/IMLCZO-267)
38+
- Add updated parsers for USGS, IWQIS, and GREON
39+
[GLGVO-671](https://opensource.ncsa.illinois.edu/jira/browse/GLGVO-671)
3840

3941
### Removed
4042
- Removed huc_finder and dependencies

GREON/config.yml

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
host: https://<host>/geostreams
2+
username: <username for geostreams>
3+
password: <password for geostreams>
4+
5+
send_email: "false"
6+
email_recipients: ["[email protected]"]
7+
8+
binning_path: /home/parsers/binning/binning_args.py
9+
# Add sleep in seconds for each binning thread for systems with less resources
10+
binning_wait: 0
11+
# Use lower number of threads for systems with less resources
12+
n_binning_threads: 4
13+
14+
log_filename: /home/parsers/parsers/gltg-dev/log-gltg-dev.log
15+
loggernet_dir: /home/parsers/greon-data
16+
17+
gltg-sites:
18+
- 'GREON-07'
19+
- 'GREON-06'
20+
- 'GREON-05'
21+
- 'GREON-04'
22+
- 'GREON-03'
23+
- 'GREON-02'
24+
- 'GREON-01'
25+
ilnlrs-sites:
26+
- 'GREON-07'
27+
- 'GREON-06'
28+
- 'GREON-04'
29+
- 'GREON-02'
30+
- 'GREON-01'
31+
32+
geocodes:
33+
'GREON-01': [-90.17608055555556,38.86943333333333,0]
34+
'GREON-02': [-90.17775555555556,38.876333333333335,0]
35+
'GREON-03': [-91.227473,43.667321,0]
36+
'GREON-04': [-89.3452361111111,38.619175,0]
37+
'GREON-05': [-91.280801,43.833298, 0]
38+
'GREON-06': [-89.51394166666667,37.29493333333333,0]
39+
'GREON-07': [-88.85931111111111,39.88906111111111,0]
40+
41+
popup_text:
42+
'GREON-01': 'Mississippi River Backwater, Ellis Bay UMR 202'
43+
'GREON-02': 'Mississippi River Main Channel at Alton, IL UMR 202'
44+
'GREON-03': 'Mississippi River Backwater, Stoddard, WI, UMR 686'
45+
'GREON-04': 'Kaskaskia River at Carlyle Lake, IL'
46+
'GREON-05': 'Mississippi River Main Channel at La Crosse, WI UMR 700'
47+
'GREON-06': 'Main Channel at Cape Girardeau, MO'
48+
'GREON-07': 'Upper Sangamon River at Lake Decatur'
49+
50+
map_parameters:
51+
"YSI_Temp_C": "water-temperature-c"
52+
# "YSI_Cond_uScm": "conductivity-uscm"
53+
"YSI_SpCond_uScm": "specific-conductivity-uscm"
54+
"YSI_Sal_PSU": "salinity-psu"
55+
# "YSI_ODO_Sat": "dissolved-oxygen-saturation-pct"
56+
"YSI_ODO_mgL": "dissolved-oxygen-mgl"
57+
"YSI_Turb_FNU": "turbidity-fnu"
58+
"YSI_TSS_mgL": "suspended-solids-mgl"
59+
"YSI_Chl_RFU": "chlorophyll-rfu"
60+
"YSI_Chl_ugL": "chlorophyll-mgl"
61+
# "YSI_fDOM_RFU": "flourescent-dissolved-organic-matter-rfu"
62+
"YSI_fDOM_QSU": "flourescent-dissolved-organic-matter-qsu"
63+
"YSI_NO3_mgL": "nitrate-mgl"
64+
"YSI_NH3_mgL": "ammonia-mgl"
65+
"YSI_NH4_mgL": "ammonium-mgl"
66+
"PAR_Density_Avg": "par-density-avg"
67+
"WindSpd_mph": "wind-speed-mph"
68+
"BaroPress_inHg_Avg": "barometric-pressure-24hr-avg-hg"
69+
"WindDir": "wind-direction"
70+
"RainIn_Tot": "rain-accumulation-24hr-total-inch"
71+
"RhPct_Avg": "relative-humidity-24hr-avg"
72+
"CompassAve": "compass-heading-24hr-avg"
73+
# "AirTempF_Avg": "air-temperature-24hr-avg-f"
74+
"WindSpd_mph_Max": "wind-speed-24hr-max-mph"
75+
# "PAR_Raw_mV_Avg": "par-avg-mv"
76+
"HailHits_Tot": "hail-hits-total-24hr"
77+
"YSI_BGAPC_RFU": "blue-green-algae-pct-rfu"
78+
"YSI_BGAPC_ugL": "blue-green-algae-pct-ugl"
79+
"Nitrate": "nitrate-mgl"
80+
"Ammonia/ Ammonium": "ammonia-ammonium-mgl"
81+
"Total Nitrogen": "total-nitrogen-mgl"
82+
"Sample Below detection limit": "sample-below-detection-limit"
83+
# "YSI_Batt_V": "YSI_Batt_V"
84+
# "SUNA_DarkAve": "SUNA_DarkAve"
85+
# "SUNA_LightAve": "SUNA_LightAve"
86+
# "SUNA_Nitrate_uM": "SUNA_Nitrate_uM"
87+
# "YSI_CablePwr_V": "YSI_CablePwr_V"
88+
# "YSI_WiperPos_V": "YSI_WiperPos_V"
89+
# "SUNA_Nitrate_mgL": "SUNA_Nitrate_mgL"
90+
# "RECORD": "record"

GREON/greon.py

+225
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
from pygeotemporal.sensors import SensorsApi
2+
from pygeotemporal.streams import StreamsApi
3+
from pygeotemporal.datapoints import DatapointsApi
4+
from pygeotemporal.cache import CacheApi
5+
from pygeotemporal.time_transformers import time2utc
6+
from datetime import date, datetime
7+
from dateutil.relativedelta import relativedelta
8+
import smtplib
9+
from email.mime.text import MIMEText
10+
import yaml
11+
import sys
12+
import copy
13+
import os
14+
import csv
15+
import logging
16+
import subprocess
17+
import json
18+
from pprint import pprint
19+
import time
20+
21+
timer = time.time()
22+
23+
# Append path to hucfinder as needed
24+
# TODO add to config.yml
25+
sys.path.append('../../..')
26+
from huc_finder.huc import HucFinder
27+
hucfinder = HucFinder('../../../huc_finder/huc-all.shp')
28+
29+
# get configuration
30+
config = yaml.load(open("config.yml", 'r'), Loader=yaml.FullLoader)
31+
32+
# configure logging
33+
logging.getLogger("requests").setLevel(logging.WARNING)
34+
logging.getLogger("urllib3").setLevel(logging.WARNING)
35+
logging.getLogger("fiona").setLevel(logging.WARNING)
36+
logging.getLogger("geopandas").setLevel(logging.ERROR)
37+
logging.getLogger("pyproj").setLevel(logging.ERROR)
38+
logging.basicConfig(filename=config['log_filename'], level=logging.DEBUG)
39+
40+
host = config['host']
41+
username = config['username']
42+
password = config['password']
43+
44+
logging.info(" ")
45+
logging.info("[greon.py] Starting GREON Parser for %s" % host)
46+
47+
binning_sensor_id_list = []
48+
49+
# Authenticate
50+
sensorclient = SensorsApi(host=host, username=username, password=password)
51+
streamclient = StreamsApi(host=host, username=username, password=password)
52+
datapointclient = DatapointsApi(host=host, username=username, password=password)
53+
cacheclient = CacheApi(host=host, username=username, password=password)
54+
55+
def create_sensor_stream(site):
56+
# get or create sensor
57+
geocode = config['geocodes'][site]
58+
huc_json = hucfinder.getHuc(lat=geocode[1], lon=geocode[0])
59+
logging.debug("[greon.py] Getting sensor")
60+
sensor_json = sensorclient.sensor_create_json(site,
61+
geocode[0],
62+
geocode[1],
63+
0,
64+
site,
65+
huc_json['huc4']['code'],
66+
huc=huc_json,
67+
network='greon',
68+
organization_id='greon',
69+
title="Great Rivers Ecological Observation Network")
70+
71+
sensor = sensorclient.sensor_post_json(sensor_json)
72+
73+
latest_datapoint = sensor['max_end_time']
74+
75+
logging.debug("[greon.py] Latest datapoint:" + str(latest_datapoint))
76+
77+
# create or get stream - need to add _WQ to name of sensor for stream
78+
# then romove before returning
79+
sensor_name = copy.copy(sensor['name'])
80+
sensor['name'] = sensor_name + "_WQ"
81+
sensor['properties']["name"] = sensor_name + "_WQ"
82+
stream_json = streamclient.stream_create_json_from_sensor(sensor)
83+
stream_wq = streamclient.stream_post_json(stream_json)
84+
sensor['name'] = sensor_name
85+
sensor['properties']['name'] = sensor_name
86+
87+
return sensor, stream_wq, geocode, latest_datapoint
88+
89+
for site in config['gltg-sites']:
90+
91+
logging.debug("[greon.py] ------ Parsing %s" % site)
92+
93+
sensor, stream_wq, geocode, latest_datapoint = create_sensor_stream(site)
94+
95+
# create list of paths for site and data type
96+
file_list = []
97+
98+
logging.debug("[greon.py] Getting data files for %s" % site)
99+
for path, subdirs, files in os.walk("%s/%s%s" % (config['loggernet_dir'], str(site), "/WQData")):
100+
# don't add files to list that are previous to latest_datapoint
101+
for name in files:
102+
if name[-3:] == "dat":
103+
file_list.append(os.path.join(path, name))
104+
logging.debug("[greon.py] Done getting data files for %s" % site)
105+
106+
count = 0
107+
greon1_to_greon2 = False
108+
datapoints_bulk_list = []
109+
good_datapoint_count = 0
110+
rows_total = 0
111+
rows_count = 0
112+
113+
while len(file_list) > 0:
114+
file_rows = open(file_list[0]).read().splitlines()
115+
file_rows.pop(0)
116+
file_rows.pop(1)
117+
file_rows.pop(1)
118+
data = csv.DictReader(file_rows)
119+
120+
for row in data:
121+
rows_count += 1
122+
123+
if site == "GREON-01" and row['TIMESTAMP'] > "2017-07-02 02:52:00" and not greon1_to_greon2:
124+
if greon1_to_greon2 is False:
125+
logging.debug("[greon.py] Switching GREON-01 to GREON-02")
126+
sensorclient.sensor_statistics_post(sensor['id'])
127+
# sensor, stream_wq, geocode = create_sensor_stream("GREON-02")
128+
sensor, stream_wq, geocode, latest_datapoint = create_sensor_stream("GREON-02")
129+
greon1_to_greon2 = True
130+
continue
131+
132+
if latest_datapoint is None or time2utc(row['TIMESTAMP']) > str(latest_datapoint):
133+
134+
count += 1
135+
properties = {}
136+
for key in row.keys():
137+
if key in config['map_parameters'] and row[key] not in ['NAN']:
138+
properties[config['map_parameters'][key]] = float(row[key])
139+
if properties:
140+
141+
datapoint_json = {
142+
'start_time': time2utc(row['TIMESTAMP']),
143+
'end_time': time2utc(row['TIMESTAMP']),
144+
'type': 'Feature',
145+
'geometry': {
146+
'type': "Point",
147+
'coordinates': [
148+
geocode[0],
149+
geocode[1],
150+
0
151+
]
152+
},
153+
'stream_id': stream_wq['id'],
154+
'sensor_id': sensor['id'],
155+
'sensor_name': str(stream_wq['name']),
156+
'properties': properties
157+
}
158+
159+
good_datapoint_count += 1
160+
161+
datapoints_bulk_list.append(datapoint_json)
162+
163+
if good_datapoint_count % 100 == 0 or len(file_list) == 1:
164+
logging.debug("[greon.py] Posting datapoints up to: " + str(count)+" of sensor " + str(site))
165+
166+
response = datapointclient.datapoint_create_bulk(datapoints_bulk_list)
167+
logging.debug("[greon.py] %s" % response)
168+
169+
170+
datapoints_bulk_list = []
171+
if str(sensor['id']) not in binning_sensor_id_list:
172+
binning_sensor_id_list.append(str(sensor['id']))
173+
else:
174+
logging.debug("[greon.py] Not posting data for row - all NANs ")
175+
file_list.pop(0)
176+
177+
if len(datapoints_bulk_list) > 0:
178+
logging.debug("[greon.py] creating datapoints up to (end of sensor loop) of sensor " + str(site))
179+
response = datapointclient.datapoint_create_bulk(datapoints_bulk_list)
180+
logging.debug(response)
181+
datapoints_bulk_list = []
182+
if str(sensor['id']) not in binning_sensor_id_list:
183+
binning_sensor_id_list.append(str(sensor['id']))
184+
185+
logging.debug("[greon.py] updating sensor with id " + str(sensor['id']))
186+
sensorclient.sensor_statistics_post(sensor['id'])
187+
188+
sensor_for_update = sensorclient.sensor_get(sensor['id']) #.json()['sensor']
189+
190+
logging.debug("[greon.py] Setting online status for sensor " + str(sensor['id']))
191+
192+
if sensor_for_update["max_end_time"][:10] > str(date.today() - relativedelta(days=5)):
193+
if "online_status" not in sensor_for_update["properties"] or sensor_for_update["properties"][
194+
"online_status"] != "online":
195+
logging.debug("changing online status to online for " + str(site))
196+
sensor_for_update["properties"]["online_status"] = "online"
197+
response = sensorclient.update_sensor_metadata(sensor_for_update)
198+
199+
else:
200+
if "online_status" not in sensor_for_update["properties"] or sensor_for_update["properties"][
201+
"online_status"] != "offline":
202+
logging.debug("changing online status to offline for " + str(site))
203+
sensor_for_update["properties"]["online_status"] = "offline"
204+
response = sensorclient.update_sensor_metadata(sensor_for_update)
205+
206+
logging.debug("[greon.py] Done with sensor " + str(sensor['id']))
207+
208+
if len(binning_sensor_id_list) > 0:
209+
logging.info("[greon.py] Start binning")
210+
subprocess.Popen(
211+
["python", config['binning_path'],
212+
"--host", host,
213+
"-u", username,
214+
"-p", password,
215+
"-s", " ".join(binning_sensor_id_list),
216+
"-l", config['log_filename'],
217+
"-w", str(config['binning_wait']),
218+
"-n", str(config['n_binning_threads'])
219+
]
220+
).wait()
221+
else:
222+
logging.info("[greon.py] There are no new datapoints to parser - skipping binning")
223+
224+
logging.debug("[greon.py] Done Parsing GREON. Total time %sm" % ((time.time()-timer)/60))
225+
logging.debug(" ")

IWQIS/config.yml

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
host: https://<host>/geostreams
2+
username: <username for geostreams>
3+
password: <password for geostreams>
4+
5+
log_filename: /home/parsers/parsers/gltg-dev/log-gltg-dev.log
6+
7+
binning_path: /home/parsers/binning/binning_args.py
8+
# Add sleep in seconds for each binning thread for systems with less resources
9+
binning_wait: 0
10+
# Use lower number of threads for systems with less resources
11+
n_binning_threads: 4
12+
13+
new_sensor_start_time: '2010'
14+
15+
ilnlrs_sites:
16+
- WQS0010
17+
- WQS0020
18+
- WQS0023
19+
- WQS0025
20+
- WQS0026
21+
22+
map_parameters:
23+
nitrate_con-iwqis: nitrate-nitrite-as-n-mgl
24+
discharge-iwqis: discharge-ft3s
25+
diss_oxy_con-iwqis: dissolved-oxygen-mgl
26+
turbi_mean-iwqis: water-turbidity-ntu
27+
temp_water-iwqis: water-temperature-c
28+
ph-iwqis: pH
29+
spec_cond-iwqis: specific-conductivity-uscm
30+
load-iwqis: nitrate-load-lb-day
31+
yield-iwqis: nitrate-yield-lb-acre-day
32+
phosphate_con-iwqis: phosphorus-in-total-orthophosphate-as-p-mgl
33+
p_yield-iwqis: phosphate-yield-lb-acre-day
34+
p_load-iwqis: phosphate-load-lb-day

0 commit comments

Comments
 (0)