Skip to content

Commit ff92c19

Browse files
authored
Merge pull request #25 from DataIntegrationGroup/dev/jab
Version 0.4.0
2 parents 2cf8ac3 + 244d381 commit ff92c19

File tree

17 files changed

+433
-253
lines changed

17 files changed

+433
-253
lines changed

UNIT_CONVERSIONS.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Successful Unit Conversions
2+
The conversion factor is applied to the reported value from the source to obtain a value with standardized units.
3+
4+
| SOURCE PARAMETER NAME | SOURCE UNITS | DIE PARAMETER NAME | DIE PARAMETER UNITS | CONVERSION FACTOR | NOTES |
5+
| :-------------------- | :----------- | :----------------- | :------------------ | :---------------- | :---- |
6+
| - | m | - | ft | 3.28084 | Applies to all records where source and die parameter names are equivalent |
7+
| - | ft | - | m | 0.3048 | Applies to all records where source and die parameter names are equivalent |
8+
| - | ppm | - | mg/L | 1 | Applies to all records where source and die parameter names are equivalent |
9+
| - | ug/L | - | mg/L | 0.001 | Applies to all records where source and die parameter names are equivalent |
10+
| - | ton/ac-ft | - | mg/L | 735.47 | Applies to all records where source and die parameter names are equivalent |
11+
| bicarbonate | mg/L as CaCO3 | bicarbonate | mg/L | 1.22 | equivalent mass HCO3 - = 61 |
12+
| calcium | mg/L as CaCO3 | calcium | mg/L | 0.4 | equivalent mass Ca 2+ = 20 |
13+
| carbonate | mg/L as CaCO3 | carbonate | mg/L | 0.6 | equivalent mass CO3 2- = 30 |
14+
| nitrate as n | mg/L as N | nitrate | mg/L | 4.4268 | - |
15+
| nitrate | mg/L as N | nitrate | mg/L | 4.4268 | - |
16+
| nitrate | ug/L as N | nitrate | mg/L | 0.0044268 | - |
17+
| nitrate | mg/L as NO3 | nitrate | mg/L | 1 | - |
18+
| sulfate as SO4 | mg/L | sulfate | mg/L | 1 | - |
19+
| sulfur sulfate | mg/L | sulfate | mg/L | 1 | - |
20+
| uranium | pCi/L | uranium | mg/L | 0.00149 | [conversion factor source](https://www.epa.gov/sites/default/files/2015-09/documents/qa_rad_webcast.pdf) |
21+
22+
## Converting from mg/L as CaCO3 to mg/L
23+
24+
```
25+
mg/L as CaCO3 = mg/L * (equivalent mass CaCO3/equivalent mass analyte)
26+
27+
mg/L = mg/L as CaCO3 * (equivalent mass analyte/equivalent mass CaCO3)
28+
```
29+
30+
where **equivalent mass analyte = atomic mass analyte/valency analyte**
31+
32+
so **equivalent mass CaCO3 = (equivalent mass Ca 2+) + (equivalent mass CO3 2-) = 40/2 + (12+3*16)/2 = 50**
33+
34+
35+
## Converting from mg/L as N to mg/L
36+
37+
```
38+
mg/L * (molecular weight N/molecular weight NO3) = mg/L as N
39+
mg/L = mg/L as N * (molecuar weight NO3/molecular weight N)
40+
```
41+
42+
where **molecular weight N = 14.007 g/mol** and **molecular weight NO3 = (14.007+15.999*3) = 62.004 g/mol**
43+
44+
## Converting from pCi/L to mg/L
45+
46+
```
47+
ug/L = pCi/L * 1.49
48+
```
49+
50+
# Skipped Unit Conversions
51+
52+
Records with the following source parameter names, source units, die parameter names, and die parameter units are not included in the output.
53+
54+
| SOURCE PARAMETER NAME | SOURCE UNITS | NOTES |
55+
| :-------------------- | :----------- | :---- |
56+
| - | tons/day | This is not a unit of concentration |
57+
| - | mg/kg | This is not a unit of aqueous concentration |
58+
| sodium | % | This is "Sodium fraction of cations, water, percent in equivalents of major cations" - see USGS pcode 00932 |

backend/config.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,11 @@ def _make_output_path(self):
421421
if not os.path.exists(self.output_path):
422422
os.mkdir(self.output_path)
423423

424+
def _update_output_units(self):
425+
parameter = self.parameter.lower()
426+
if parameter == "ph":
427+
self.analyte_output_units = ""
428+
424429
@property
425430
def start_dt(self):
426431
return self._extract_date(self.start_date)

backend/connectors/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ The following methods need to be defined for Faux. See `BaseAnalyteSource` for d
3333

3434
- `get_records`
3535
- `_extract_site_records`
36-
- `_extract_parameter_units`
36+
- `_extract_source_parameter_units`
3737
- `_extract_most_recent`
3838
- `_extract_parameter_result`
3939
- `_extract_parameter_record`
@@ -48,7 +48,7 @@ The following methods need to be defined for Faux. See `BaseWaterLevelSource` fo
4848

4949
- `get_records`
5050
- `_extract_site_records`
51-
- `_extract_parameter_units`
51+
- `_extract_source_parameter_units`
5252
- `_extract_most_recent`
5353
- `_extract_parameter_result`
5454
- `_extract_parameter_record`

backend/connectors/bor/source.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,11 @@
2121
from backend.connectors.bor.transformer import BORSiteTransformer, BORAnalyteTransformer
2222
from backend.connectors.mappings import BOR_ANALYTE_MAPPING
2323
from backend.constants import (
24-
TDS,
25-
URANIUM,
26-
ARSENIC,
27-
SULFATE,
28-
FLUORIDE,
29-
CHLORIDE,
24+
PARAMETER_NAME,
3025
PARAMETER_VALUE,
3126
PARAMETER_UNITS,
27+
SOURCE_PARAMETER_NAME,
28+
SOURCE_PARAMETER_UNITS,
3229
DT_MEASURED,
3330
)
3431

@@ -68,32 +65,42 @@ def parse_dt(dt):
6865
class BORAnalyteSource(BaseAnalyteSource):
6966
transformer_klass = BORAnalyteTransformer
7067
_catalog_item_idx = None
68+
_source_parameter_name = None
7169

7270
def __repr__(self):
7371
return "BORAnalyteSource"
7472

7573
def _extract_parameter_record(self, record):
74+
record[PARAMETER_NAME] = self.config.parameter
7675
record[PARAMETER_VALUE] = record["attributes"]["result"]
77-
record[PARAMETER_UNITS] = record["attributes"]["resultAttributes"]["units"]
76+
record[PARAMETER_UNITS] = self.config.analyte_output_units
7877
record[DT_MEASURED] = parse_dt(record["attributes"]["dateTime"])
78+
record[SOURCE_PARAMETER_NAME] = self._source_parameter_name
79+
record[SOURCE_PARAMETER_UNITS] = record["attributes"]["resultAttributes"][
80+
"units"
81+
]
7982
return record
8083

81-
def _extract_parameter_results(self, rs):
84+
def _extract_source_parameter_results(self, rs):
8285
return [ri["attributes"]["result"] for ri in rs]
8386

84-
def _extract_parameter_units(self, records):
87+
def _extract_source_parameter_units(self, records):
8588
return [ri["attributes"]["resultAttributes"]["units"] for ri in records]
8689

8790
def _extract_parameter_dates(self, records):
8891
return [parse_dt(ri["attributes"]["dateTime"]) for ri in records]
8992

93+
def _extract_source_parameter_names(self, records):
94+
return [self._source_parameter_name for ri in records]
95+
9096
def _extract_most_recent(self, rs):
9197

9298
record = get_most_recent(rs, "attributes.dateTime")
9399
return {
94100
"value": record["attributes"]["result"],
95101
"datetime": parse_dt(record["attributes"]["dateTime"]),
96-
"units": record["attributes"]["resultAttributes"]["units"],
102+
"source_parameter_units": record["attributes"]["resultAttributes"]["units"],
103+
"source_parameter_name": self._source_parameter_name,
97104
}
98105

99106
def _extract_site_records(self, records, site_record):
@@ -126,6 +133,11 @@ def get_records(self, site_record):
126133
if not self._catalog_item_idx:
127134
self._catalog_item_idx = i
128135

136+
if self._source_parameter_name is None:
137+
self._source_parameter_name = data["attributes"][
138+
"parameterSourceCode"
139+
]
140+
129141
return self._execute_json_request(
130142
"https://data.usbr.gov/rise/api/result",
131143
params={"itemId": data["attributes"]["_id"]},

backend/connectors/ckan/source.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@
3636
DTW,
3737
DTW_UNITS,
3838
DT_MEASURED,
39-
PARAMETER,
39+
PARAMETER_NAME,
4040
PARAMETER_UNITS,
4141
PARAMETER_VALUE,
42+
SOURCE_PARAMETER_NAME,
43+
SOURCE_PARAMETER_UNITS,
4244
)
4345
from backend.source import (
4446
BaseSource,
@@ -133,21 +135,31 @@ def _parse_response(self, site_record, resp):
133135
records = resp.json()["result"]["records"]
134136
return [record for record in records if record["Site_ID"] == site_record.id]
135137

136-
def _extract_parameter_results(self, records):
138+
def _extract_source_parameter_results(self, records):
137139
return [float(r["DTWGS"]) for r in records]
138140

139141
def _extract_most_recent(self, records):
140142
record = get_most_recent(records, tag="Date")
141-
return {"value": record["DTWGS"], "datetime": record["Date"], "units": FEET}
143+
return {
144+
"value": record["DTWGS"],
145+
"datetime": record["Date"],
146+
"source_parameter_units": FEET,
147+
"source_parameter_name": "DTWGS",
148+
}
142149

143150
def _extract_parameter_dates(self, records: list) -> list:
144151
return [r["Date"] for r in records]
145152

153+
def _extract_source_parameter_names(self, records):
154+
return ["" for r in records]
155+
146156
def _extract_parameter_record(self, record):
147-
record[PARAMETER] = DTW
157+
record[PARAMETER_NAME] = DTW
148158
record[PARAMETER_VALUE] = float(record["DTWGS"])
149-
record[PARAMETER_UNITS] = FEET
159+
record[PARAMETER_UNITS] = self.config.waterlevel_output_units
150160
record[DT_MEASURED] = record["Date"]
161+
record[SOURCE_PARAMETER_NAME] = "DTWGS"
162+
record[SOURCE_PARAMETER_UNITS] = FEET
151163
return record
152164

153165
def _clean_records(self, records: list) -> list:

backend/connectors/isc_seven_rivers/source.py

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,14 @@
2020
from backend.connectors import ISC_SEVEN_RIVERS_BOUNDING_POLYGON
2121
from backend.connectors.mappings import ISC_SEVEN_RIVERS_ANALYTE_MAPPING
2222
from backend.constants import (
23-
TDS,
2423
FEET,
25-
URANIUM,
26-
SULFATE,
27-
FLUORIDE,
28-
CHLORIDE,
2924
DT_MEASURED,
30-
DTW_UNITS,
3125
DTW,
32-
PARAMETER,
26+
PARAMETER_NAME,
3327
PARAMETER_VALUE,
3428
PARAMETER_UNITS,
29+
SOURCE_PARAMETER_NAME,
30+
SOURCE_PARAMETER_UNITS,
3531
)
3632
from backend.connectors.isc_seven_rivers.transformer import (
3733
ISCSevenRiversSiteTransformer,
@@ -93,11 +89,12 @@ def get_records(self):
9389
class ISCSevenRiversAnalyteSource(BaseAnalyteSource):
9490
transformer_klass = ISCSevenRiversAnalyteTransformer
9591
_analyte_ids = None
92+
_source_parameter_name = None
9693

9794
def __repr__(self):
9895
return "ISCSevenRiversAnalyteSource"
9996

100-
def _get_analyte_id(self, analyte):
97+
def _get_analyte_id_and_name(self, analyte):
10198
""" """
10299
if self._analyte_ids is None:
103100

@@ -107,13 +104,20 @@ def _get_analyte_id(self, analyte):
107104

108105
analyte = get_analyte_search_param(analyte, ISC_SEVEN_RIVERS_ANALYTE_MAPPING)
109106
if analyte:
110-
return self._analyte_ids.get(analyte)
107+
id_and_name = {
108+
"id": self._analyte_ids.get(analyte),
109+
"name": analyte,
110+
}
111+
return id_and_name
111112

112113
def _extract_parameter_record(self, record):
113-
record[PARAMETER] = self.config.parameter
114+
record[PARAMETER_NAME] = self.config.parameter
114115
record[PARAMETER_VALUE] = record["result"]
115-
record[PARAMETER_UNITS] = record["units"]
116+
record[PARAMETER_UNITS] = self.config.analyte_output_units
116117
record[DT_MEASURED] = get_datetime(record)
118+
record[SOURCE_PARAMETER_NAME] = self._source_parameter_name
119+
record[SOURCE_PARAMETER_UNITS] = record["units"]
120+
117121
return record
118122

119123
def _extract_most_recent(self, records):
@@ -122,25 +126,30 @@ def _extract_most_recent(self, records):
122126
return {
123127
"value": record["result"],
124128
"datetime": get_datetime(record),
125-
"units": record["units"],
129+
"source_parameter_units": record["units"],
130+
"source_parameter_name": self._source_parameter_name,
126131
}
127132

128133
def _clean_records(self, records):
129134
return [r for r in records if r["result"] is not None]
130135

131-
def _extract_parameter_results(self, records):
136+
def _extract_source_parameter_results(self, records):
132137
return [r["result"] for r in records]
133138

134-
def _extract_parameter_units(self, records):
139+
def _extract_source_parameter_units(self, records):
135140
return [r["units"] for r in records]
136141

137142
def _extract_parameter_dates(self, records: list) -> list:
138143
return [get_datetime(r) for r in records]
139144

145+
def _extract_source_parameter_names(self, records: list) -> list:
146+
return [self._source_parameter_name for r in records]
147+
140148
def get_records(self, site_record):
141149
config = self.config
142-
analyte_id = self._get_analyte_id(config.parameter)
143-
if analyte_id:
150+
analyte_id_and_name = self._get_analyte_id_and_name(config.parameter)
151+
if analyte_id_and_name:
152+
analyte_id = analyte_id_and_name["id"]
144153
params = {
145154
"monitoringPointId": site_record.id,
146155
"analyteId": analyte_id,
@@ -149,13 +158,18 @@ def get_records(self, site_record):
149158
}
150159
params.update(get_date_range(config))
151160

161+
if self._source_parameter_name is None:
162+
self._source_parameter_name = analyte_id_and_name["name"]
163+
152164
return self._execute_json_request(
153165
_make_url("getReadings.ashx"), params=params
154166
)
155167

156168

157169
class ISCSevenRiversWaterLevelSource(BaseWaterLevelSource):
158170
transformer_klass = ISCSevenRiversWaterLevelTransformer
171+
_source_parameter_name = "depthToWaterFeet"
172+
_source_parameter_units = FEET
159173

160174
def get_records(self, site_record):
161175
params = {
@@ -174,24 +188,37 @@ def _clean_records(self, records):
174188
return [r for r in records if r["depthToWaterFeet"] is not None]
175189

176190
def _extract_parameter_record(self, record):
177-
record[PARAMETER] = DTW
191+
record[PARAMETER_NAME] = DTW
178192
record[PARAMETER_VALUE] = record["depthToWaterFeet"]
179-
record[PARAMETER_UNITS] = FEET
193+
record[PARAMETER_UNITS] = self.config.waterlevel_output_units
180194
record[DT_MEASURED] = get_datetime(record)
195+
record[SOURCE_PARAMETER_NAME] = self._source_parameter_name
196+
record[SOURCE_PARAMETER_UNITS] = self._source_parameter_units
181197
return record
182198

183-
def _extract_parameter_results(self, records):
199+
def _extract_source_parameter_results(self, records):
184200
return [
185201
r["depthToWaterFeet"] for r in records if not r["invalid"] and not r["dry"]
186202
]
187203

188204
def _extract_parameter_dates(self, records: list) -> list:
189205
return [get_datetime(r) for r in records]
190206

207+
def _extract_source_parameter_names(self, records):
208+
return [self._source_parameter_name for r in records]
209+
210+
def _extract_source_parameter_units(self, records):
211+
return [self._source_parameter_units for r in records]
212+
191213
def _extract_most_recent(self, records):
192214
record = get_most_recent(records, "dateTime")
193215
t = get_datetime(record)
194-
return {"value": record["depthToWaterFeet"], "datetime": t, "units": FEET}
216+
return {
217+
"value": record["depthToWaterFeet"],
218+
"datetime": t,
219+
"source_parameter_units": self._source_parameter_units,
220+
"source_parameter_name": DTW,
221+
}
195222

196223

197224
# ============= EOF =============================================

0 commit comments

Comments
 (0)