Skip to content
Merged
29 changes: 17 additions & 12 deletions server/routes/browser/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,34 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""Knowledge Graph related handlers."""

import json

import flask
from flask import request
from flask import Response
from flask import jsonify

from server.lib import fetch
from server.lib.cache import cache
from server.routes import TIMEOUT
import server.services.datacommons as dc

bp = flask.Blueprint('api_browser', __name__, url_prefix='/api/browser')


@bp.route('/provenance')
@cache.cached(timeout=TIMEOUT, query_string=True)
def provenance():
"""Returns all the provenance information."""
"""Return provenance name for all available data sources."""
prov_resp = fetch.property_values(['Provenance'], 'typeOf', False)
url_resp = fetch.property_values(prov_resp['Provenance'], "url", True)
prov_dcids = prov_resp.get('Provenance', [])

if not prov_dcids:
return jsonify({})

properties_to_fetch = ['name']
prop_resp = fetch.multiple_property_values(prov_dcids, properties_to_fetch,
True)

result = {}
for dcid, urls in url_resp.items():
if len(urls) > 0:
result[dcid] = urls[0]
return result
for dcid, props in prop_resp.items():
names = props.get('name', [])

result[dcid] = {'name': names[0] if names else dcid}

return jsonify(result)
Binary file modified server/tests/test_data/webdriver_recordings.tar.gz
Binary file not shown.
2 changes: 1 addition & 1 deletion server/webdriver/shared_tests/browser_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def test_page_serve_ca_population(self):
value='//*[@id="node-content"]/div[1]/div/table/tbody/tr[3]/td')
self.assertEqual(type_of_row[0].text, 'typeOf')
self.assertEqual(type_of_row[1].text, 'StatisticalVariable')
self.assertEqual(type_of_row[2].text, 'datacommons.org')
self.assertEqual(type_of_row[2].text, 'Data Commons')

# Assert observation charts loaded.
self.assertGreater(
Expand Down
3 changes: 1 addition & 2 deletions static/css/table.scss
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ $border-radius: 3px;
}

.node-table .property-column,
.node-table tr > td:last-of-type,
.node-table tr > th:last-of-type {
.node-table .provenance-column {
width: 25%;
}

Expand Down
30 changes: 18 additions & 12 deletions static/js/browser/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ const SCROLL_MARGIN = 10;
const SCROLL_TIMEOUT = 10000;
const SCROLL_DELAY = 500;

/**
* Mapping of raw provenance names (returned by the API) to custom display names.
* This can be used to make certain data sources more human-readable in the UI.
* If a provenance name is not in this map, the original string will be used.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does "original string" mean? Is it the DCID?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry - that was unclear! The original string is the provenance name given by the API. It would only ever be a DCID if there isn't a name (although as you verified, there should always be a name).

I'll update the comment.

*/
const PROVENANCE_DISPLAY_NAME_MAP: Record<string, string> = {
HumanReadableStatVars: "Data Commons",
};

interface BrowserPagePropType {
dcid: string;
nodeName: string;
Expand All @@ -49,7 +58,7 @@ interface BrowserPagePropType {
}

interface BrowserPageStateType {
provDomain: { [key: string]: URL };
provenanceNames: { [key: string]: string };
dataFetched: boolean;
}

Expand Down Expand Up @@ -101,7 +110,7 @@ export class BrowserPage extends React.Component<
super(props);
this.state = {
dataFetched: false,
provDomain: {},
provenanceNames: {},
};
}

Expand Down Expand Up @@ -163,7 +172,7 @@ export class BrowserPage extends React.Component<
<h3>{outArcHeader}</h3>
<OutArcSection
dcid={arcDcid}
provDomain={this.state.provDomain}
provenanceNames={this.state.provenanceNames}
nodeTypes={this.props.nodeTypes}
showAllProperties={showAllProperties}
/>
Expand All @@ -186,7 +195,7 @@ export class BrowserPage extends React.Component<
<InArcSection
nodeName={this.props.nodeName}
dcid={this.props.dcid}
provDomain={this.state.provDomain}
provenanceNames={this.state.provenanceNames}
/>
)}
{this.props.pageDisplayType === PageDisplayType.PLACE_STAT_VAR && (
Expand Down Expand Up @@ -222,18 +231,15 @@ export class BrowserPage extends React.Component<
.get("/api/browser/provenance")
.then((resp) => {
const provenance = resp.data;
const provDomain = {};
const provenanceNames = {};
for (const provId in provenance) {
const url = provenance[provId];
try {
provDomain[provId] = new URL(url).host;
} catch (err) {
console.log("Invalid url in prov: " + url);
}
const provenanceName = provenance[provId].name;
provenanceNames[provId] =
PROVENANCE_DISPLAY_NAME_MAP[provenanceName] || provenanceName;
}
this.setState({
dataFetched: true,
provDomain,
provenanceNames,
});
})
.catch((e) => {
Expand Down
29 changes: 16 additions & 13 deletions static/js/browser/arc_table_row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,16 @@ const NUM_VALUES_UNEXPANDED = 5;
interface ArcTableRowPropType {
propertyLabel: string;
values: Array<ArcValue>;
// If provenanceId and src are skipped, ensure that table only has 2-columns.
// If provenanceId and provenanceName are skipped, ensure that table only has 2-columns.
provenanceId?: string;
src?: URL;
provenanceName?: string;
// If set to true, will not add a link to the property node.
noPropLink?: boolean;
// Index of the property label; the same property can be listed multiple times
// in an arc table. This index differentiates them.
propIndex?: number;
// If set to true, will hide the provenance column entirely.
hideProvenanceColumn?: boolean;
}

interface ArcTableRowStateType {
Expand Down Expand Up @@ -137,17 +139,18 @@ export class ArcTableRow extends React.Component<
})}
</div>
</td>
{this.props.provenanceId && this.props.src ? (
<td className="provenance-column">
{this.props.provenanceId && (
<a href={HREF_PREFIX + this.props.provenanceId}>
{this.props.src}
</a>
)}
</td>
) : (
<td></td>
)}
{!this.props.hideProvenanceColumn &&
(this.props.provenanceId && this.props.provenanceName ? (
<td className="provenance-column">
{this.props.provenanceId && (
<a href={HREF_PREFIX + this.props.provenanceId}>
{this.props.provenanceName}
</a>
)}
</td>
) : (
<td className="provenance-column"></td>
))}
</tr>
);
}
Expand Down
4 changes: 2 additions & 2 deletions static/js/browser/in_arc_section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const LOADING_CONTAINER_ID = "browser-in-arc-section";
interface InArcSectionsPropType {
nodeName: string;
dcid: string;
provDomain: { [key: string]: URL };
provenanceNames: { [key: string]: string };
}
interface InArcSectionStateType {
data: { [parentType: string]: { [property: string]: Array<InArcValue> } };
Expand Down Expand Up @@ -78,7 +78,7 @@ export class InArcSection extends React.Component<
parentType={parentType}
property={predicate}
arcValues={arcsByPredicate[predicate]}
provDomain={this.props.provDomain}
provenanceNames={this.props.provenanceNames}
key={parentType + predicate}
/>
);
Expand Down
11 changes: 6 additions & 5 deletions static/js/browser/in_arc_subsection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ interface InArcSubsectionPropType {
parentType: string;
property: string;
arcValues: Array<InArcValue>;
provDomain: { [key: string]: URL };
provenanceNames: { [key: string]: string };
}

export class InArcSubsection extends React.Component<InArcSubsectionPropType> {
Expand Down Expand Up @@ -73,10 +73,11 @@ export class InArcSubsection extends React.Component<InArcSubsectionPropType> {
propertyLabel={this.props.property}
values={[{ dcid: arcValue.dcid, text: valueText }]}
provenanceId={arcValue.provenanceId}
src={
this.props.provDomain[arcValue.provenanceId]
? this.props.provDomain[arcValue.provenanceId]
: null
provenanceName={
this.props.provenanceNames[arcValue.provenanceId] || null
}
hideProvenanceColumn={
this.props.parentType === "Provenance"
}
/>
);
Expand Down
17 changes: 10 additions & 7 deletions static/js/browser/out_arc_section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export function shouldIgnoreProperty(property: string): boolean {

interface OutArcSectionPropType {
dcid: string;
provDomain: { [key: string]: URL };
provenanceNames: { [key: string]: string };
nodeTypes: string[];
showAllProperties: boolean;
}
Expand Down Expand Up @@ -121,21 +121,25 @@ export class OutArcSection extends React.Component<
}
const predicates = Object.keys(this.state.data);
predicates.sort(this.predicateComparator);
const isProvenanceNode = this.props.nodeTypes.includes("Provenance");
return (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure where the best place to put this comment is, but I just thought of another consideration. We really should not show the Provenance column at all when the node is of type Provenance itself. It doesn't make sense to show a link that just links back to the same page (i.e. goes nowhere). Could we add some logic to check if the subject node is of type Provenance and then show a 2-column table consisting of property and value only?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

<div className={`card p-0 ${ASYNC_ELEMENT_CLASS}`}>
<table className="node-table">
<tbody>
<tr key="header">
<th className="property-column">Property</th>
<th>Value</th>
<th>Provenance</th>
{!isProvenanceNode && (
<th className="provenance-column">Provenance</th>
)}
</tr>
<ArcTableRow
key={DCID_PREDICATE}
propertyLabel={DCID_PREDICATE}
values={[{ text: this.props.dcid }]}
provenanceId={""}
src={null}
provenanceName={null}
hideProvenanceColumn={isProvenanceNode}
/>
{predicates.map((predicate) => {
const valuesByProvenance = this.state.data[predicate];
Expand All @@ -147,12 +151,11 @@ export class OutArcSection extends React.Component<
propertyLabel={predicate}
values={valuesByProvenance[provenanceId]}
provenanceId={provenanceId}
src={
this.props.provDomain[provenanceId]
? this.props.provDomain[provenanceId]
: null
provenanceName={
this.props.provenanceNames[provenanceId] || null
}
propIndex={index}
hideProvenanceColumn={isProvenanceNode}
/>
);
}
Expand Down
Loading