-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix discrepancies with legacy portal in station / history counts #83
Changes from 20 commits
536b98a
ad9a349
9a2f805
f2b4c20
c8ba910
c0da0aa
ceb479d
6d359d5
29c7388
0487bcc
fb3fc98
4d286a4
767dc2c
c8b8d13
902b9f0
99879e7
a3066e8
3bdfee6
dc00504
4ee1204
7068d51
fc0ea55
28a0776
3ab6890
d83ca51
d4b886e
2a252f9
cf18cb1
9ef1bf9
8cc447c
3ba06a5
781a98f
2701c30
1184f53
0acbc97
c36ee1e
76299b0
7f63908
3484475
db1a860
a1b6252
06fe051
e4ed68f
579adc9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,102 +3,170 @@ | |
// passed into React Table. | ||
|
||
import PropTypes from 'prop-types'; | ||
import React, { Component } from 'react'; | ||
import React from 'react'; | ||
import ReactTable from 'react-table'; | ||
import find from 'lodash/fp/find'; | ||
|
||
import flow from 'lodash/fp/flow'; | ||
import map from 'lodash/fp/map'; | ||
|
||
import FrequencySelector from '../../selectors/FrequencySelector'; | ||
import logger from '../../../logger'; | ||
import { | ||
stationNetwork, | ||
uniqStationFreqs, | ||
uniqStationLocations, | ||
uniqStationNames, | ||
uniqStationObsPeriods, | ||
uniqStationVariableNames | ||
} from '../../../utils/station-info'; | ||
|
||
import 'react-table/react-table.css'; | ||
import './StationMetadata.css'; | ||
|
||
|
||
logger.configure({ active: true }); | ||
|
||
// TODO: Put these in a util module | ||
const formatDate = d => d ? d.toISOString().substr(0,10) : 'unknown'; | ||
|
||
const lexCompare = (a, b) => { | ||
const n = Math.min(a.length, b.length); | ||
for (let i = 0; i < n; i += 1) { | ||
if (a[i] < b[i]) { | ||
return -1; | ||
} | ||
if (a[i] > b[i]) { | ||
return 1; | ||
} | ||
} | ||
return a.length - b.length; | ||
} | ||
|
||
export default class StationMetadata extends Component { | ||
static propTypes = { | ||
stations: PropTypes.array, | ||
allNetworks: PropTypes.array.isRequired, | ||
}; | ||
|
||
render() { | ||
const { stations, allNetworks, ...restProps } = this.props; | ||
|
||
const columns = [ | ||
{ | ||
id: 'Network', | ||
Header: 'Network', | ||
minWidth: 80, | ||
maxWidth: 100, | ||
accessor: station => { | ||
const network = find({ uri: station.network_uri })(allNetworks); | ||
return network ? network.name : '?'; | ||
}, | ||
}, | ||
{ | ||
id: 'Native ID', | ||
Header: 'Native ID', | ||
minWidth: 80, | ||
maxWidth: 100, | ||
accessor: 'native_id' | ||
}, | ||
{ | ||
id: 'Station Name', | ||
Header: 'Station Name', | ||
minWidth: 120, | ||
maxWidth: 200, | ||
accessor: station => station.histories[0].station_name, | ||
function StationMetadata({ | ||
stations, allNetworks, allVariables, ...restProps | ||
}) { | ||
const columns = [ | ||
{ | ||
id: 'Network', | ||
Header: 'Network', | ||
minWidth: 80, | ||
maxWidth: 100, | ||
accessor: station => { | ||
const network = stationNetwork(allNetworks, station); | ||
return network ? network.name : '?'; | ||
}, | ||
{ | ||
id: 'Location', | ||
Header: 'Location', | ||
minWidth: 120, | ||
maxWidth: 200, | ||
accessor: station => { | ||
const hx = station.histories[0]; | ||
return ( | ||
<div> | ||
{-hx.lon} W <br/> | ||
{hx.lat} N <br/> | ||
Elev. {hx.elevation} m | ||
</div> | ||
); | ||
} | ||
}, | ||
{ | ||
id: 'Record', | ||
Header: 'Record', | ||
minWidth: 100, | ||
maxWidth: 200, | ||
// accessor: station => 'record', | ||
accessor: station => { | ||
return ( | ||
<div> | ||
{formatDate(station.min_obs_time)} to <br/> | ||
{formatDate(station.max_obs_time)} | ||
</div> | ||
)} | ||
}, | ||
{ | ||
minWidth: 80, | ||
maxWidth: 100, | ||
id: 'Obs Freq', | ||
Header: 'Obs Freq', | ||
accessor: station => | ||
FrequencySelector.valueToLabel(station.histories[0].freq), | ||
}, | ||
]; | ||
}, | ||
{ | ||
id: 'Native ID', | ||
Header: 'Native ID', | ||
minWidth: 80, | ||
maxWidth: 100, | ||
accessor: 'native_id' | ||
}, | ||
{ | ||
id: 'Unique Names', | ||
Header: 'Unique Names', | ||
minWidth: 120, | ||
maxWidth: 200, | ||
accessor: uniqStationNames, | ||
sortMethod: lexCompare, | ||
Cell: row => ( | ||
<ul className={"compact"}> | ||
{map(name => (<li>{name}</li>), row.value)} | ||
</ul> | ||
), | ||
}, | ||
{ | ||
id: 'Unique Locations', | ||
Header: 'Unique Locations', | ||
minWidth: 120, | ||
maxWidth: 200, | ||
sortable: false, | ||
accessor: uniqStationLocations, | ||
Cell: row => ( | ||
<ul className={"compact"}> | ||
{ | ||
map(loc => ( | ||
<li> | ||
{-loc.lon} W <br/> | ||
{loc.lat} N <br/> | ||
Elev. {loc.elevation} m | ||
</li> | ||
), row.value) | ||
} | ||
</ul> | ||
), | ||
}, | ||
{ | ||
id: 'Unique Records', | ||
Header: 'Unique Records', | ||
minWidth: 100, | ||
maxWidth: 100, | ||
sortable: false, | ||
accessor: uniqStationObsPeriods, | ||
Cell: row => ( | ||
<ul className={"compact"}> | ||
{ | ||
map(period => ( | ||
<li> | ||
{formatDate(period.min_obs_time)} to <br/> | ||
{formatDate(period.max_obs_time)} | ||
</li> | ||
), row.value) | ||
} | ||
</ul> | ||
), | ||
}, | ||
{ | ||
minWidth: 80, | ||
maxWidth: 100, | ||
id: 'Uniq Obs Freqs', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What exactly does the "Uniq" in this label refer to? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Stations can have multiple histories, hence multiple freqs; these are reduced to their unique elements (see also comment above). |
||
Header: 'Uniq Obs Freqs', | ||
accessor: flow(uniqStationFreqs, map(FrequencySelector.valueToLabel)), | ||
sortMethod: lexCompare, | ||
Cell: row => ( | ||
<ul className={"compact"}> | ||
{map(freq => (<li>{freq}</li>), row.value)} | ||
</ul> | ||
), | ||
}, | ||
{ | ||
minWidth: 100, | ||
maxWidth: 250, | ||
id: 'Variables', | ||
Header: 'Variables', | ||
accessor: uniqStationVariableNames(allVariables), | ||
sortable: false, | ||
Cell: row => ( | ||
<ul className={"compact"}> | ||
{map(name => (<li>{name}</li>), row.value)} | ||
</ul> | ||
), | ||
}, | ||
{ | ||
id: '# Hx', | ||
Header: '# Hx', | ||
minWidth: 30, | ||
maxWidth: 30, | ||
accessor: station => station.histories.length, | ||
}, | ||
]; | ||
|
||
return ( | ||
<ReactTable | ||
data={stations} | ||
columns={columns} | ||
defaultPageSize={100} | ||
{...restProps} | ||
/> | ||
); | ||
} | ||
return ( | ||
<ReactTable | ||
data={stations} | ||
columns={columns} | ||
defaultPageSize={100} | ||
{...restProps} | ||
/> | ||
); | ||
} | ||
|
||
StationMetadata.propTypes = { | ||
stations: PropTypes.array, | ||
allNetworks: PropTypes.array.isRequired, | ||
allVariables: PropTypes.array.isRequired, | ||
}; | ||
|
||
export default StationMetadata; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is for sorting in the metadata table?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it is for sorting lists of things, e.g., lists of names, obs freqs. These arise because a station can have many histories, hence many names, obs freqs, etc. (These lists are reduced to their unique elements.)
lexCompare
implements standard lexicographic ordering for lists.I plan to document all this in the code / README before I merge.