- );
- }
-}
-
-export {App};
diff --git a/mcf-browser/src/DisplayNode.tsx b/mcf-browser/src/DisplayNode.tsx
deleted file mode 100644
index 21c73f92..00000000
--- a/mcf-browser/src/DisplayNode.tsx
+++ /dev/null
@@ -1,151 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import React, {Component} from 'react';
-
-import * as API from './back-end/server-api';
-import {Node, Assertion} from './back-end/graph';
-import {TriplesTable} from './TriplesTable';
-import {LoadingSpinner} from './LoadingSpinner';
-import {ColorIndex, colorLegend} from './utils';
-
-interface DisplayNodePropType {
- /**
- * Node object to be displayed to user
- */
- node: Node;
- /**
- * Set id parameter in url to the given id.
- */
- onNodeClick: (id: string) => void;
-}
-
-interface DisplayNodeStateType {
- /**
- * The reference of the node to be displayed to the user.
- * ex: 'country/IND [l:LocalIndiaNode]'.
- */
- ref: string | null;
- /**
- * Indicates if triples are currently being fetched from the Data Commons
- * Knowledge Graph.
- */
- fetching: boolean;
- /**
- * The triples that the current node is the subject (or source) of.
- */
- asserts: Assertion[];
- /**
- * The triples that the current node is the target of.
- */
- invAsserts: Assertion[];
- /**
- * The class of the element containing the reference of the node should be.
- */
- elemClass: string | null;
-}
-
-/** Displays node data for a given node passed in through props. */
-class DisplayNode extends Component {
- /** Creates DisplayNode component.
- * @param {DisplayNodePropType} props the props passed in by parent component
- */
- constructor(props: DisplayNodePropType) {
- super(props);
- this.state = {
- ref: null,
- asserts: [],
- invAsserts: [],
- fetching: true,
- elemClass: null,
- };
- }
-
- /** Sets node data when the component mounts. */
- componentDidMount() {
- this.setNodeData();
- }
-
- /**
- * Sets node data when the node to display changes.
- * @param {DisplayNodePropType} prevProps The previous props before the
- * component updated, used to compare if the passed in node has changed.
- */
- componentDidUpdate(prevProps: DisplayNodePropType) {
- if (prevProps.node !== this.props.node) {
- this.setNodeData();
- }
- }
-
- /**
- * Loads data to display for the node passed in through props. This includes
- * fetching the remote data from DC KG for the node.
- */
- setNodeData() {
- const curNode = this.props.node;
- this.setState({
- ref: curNode.getRef(),
- fetching: true,
- asserts: [],
- invAsserts: [],
- elemClass: '',
- });
-
- API.getElemClass(curNode).then((elemClass) => {
- this.setState({elemClass: elemClass});
- });
-
- curNode.fetchRemoteData().then(() => {
- this.setState({
- asserts: curNode.assertions,
- invAsserts: curNode.invAssertions,
- fetching: false,
- });
- });
- }
-
- /** Renders the DisplayNode component.
- * @return {JSX.Element} the webpage using JSX code
- * */
- render() : JSX.Element {
- return (
-
-
-
Currently Viewing:
-
-
{this.state.ref}
-
-
-
-
-
Node Properties
-
- current node is source
-
-
-
-
Incoming Properties from Other Nodes
-
- current node is target
-
-
-
- );
- }
-}
-
-export {DisplayNode};
diff --git a/mcf-browser/src/FileEntry.tsx b/mcf-browser/src/FileEntry.tsx
deleted file mode 100644
index 7c008ced..00000000
--- a/mcf-browser/src/FileEntry.tsx
+++ /dev/null
@@ -1,167 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import React, {Component} from 'react';
-
-interface FileEntryPropType {
- /**
- * Passes a file list to be submitted to the back-end for parsing.
- */
- upload: Function;
- /**
- * Passes a list of urls to be retrieved, then passed to the back-end for
- * parsing.
- */
- loadFiles: Function;
- /**
- * Return to the home page/reset to current hash stored in App state.
- */
- goToHome: Function;
- /**
- * Sets whether the dropdown on the home page for additonal file entries
- * should be displayed.
- */
- toggle?: Function;
-}
-
-interface FileEntryStateType{
- /**
- * Stores user's text entry in the first url entry box. This should be a url
- * to either a MCF or TMCF file.
- */
- mcfTmcfUrl: string;
- /**
- * Stores user's text entry in the second url entry box. This should be a url
- * to a CSV file.
- */
- csvUrl: string;
-}
-
-/** Component to display options user has for uploading files. */
-class FileEntry extends Component {
- /** Constructor for class, sets initial state
- * @param {DisplayNodePropType} props the props passed in by parent component
- */
- constructor(props: FileEntryPropType) {
- super(props);
- this.state = {
- mcfTmcfUrl: '',
- csvUrl: '',
- };
- }
-
- /**
- * Submits the urls currently in the text input boxes to be retreived and
- * loaded when the enter key is preessed.
- *
- * @param {Event} event The keyUp event that triggers the function call.
- */
- async handleUrlKeyUp(event: React.KeyboardEvent) {
- if (event.keyCode === 13) {
- if (this.state.mcfTmcfUrl.split('.').pop() === 'mcf') {
- // set the base file hash with the given file names
- await this.props.loadFiles([this.state.mcfTmcfUrl]);
- this.setState({csvUrl: '', mcfTmcfUrl: ''});
- // trigger hash to be set to fileHash
- this.props.goToHome();
- if (this.props.toggle) {
- this.props.toggle();
- }
- } else if (this.state.mcfTmcfUrl.split('.').pop() === 'tmcf' &&
- (this.state.csvUrl.split('.').pop() === 'csv') ) {
- await this.props.loadFiles([this.state.mcfTmcfUrl, this.state.csvUrl]);
- this.setState({csvUrl: '', mcfTmcfUrl: ''});
- // trigger hash to be set to fileHash
- this.props.goToHome();
- if (this.props.toggle) {
- this.props.toggle();
- }
- }
- }
- }
-
- /**
- * Renders the component by building the JSX.
- *
- * @return {JSX.Element} the component using JSX code
- */
- render() : JSX.Element {
- return (
-
- {/* Options to directly upload a file via file selector. */}
-
- );
- }
-}
-
-
-export {FileEntry};
diff --git a/mcf-browser/src/GraphExplorer.tsx b/mcf-browser/src/GraphExplorer.tsx
deleted file mode 100644
index 31334026..00000000
--- a/mcf-browser/src/GraphExplorer.tsx
+++ /dev/null
@@ -1,120 +0,0 @@
-/**
- * Copyright 2022 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import React, {Component} from 'react';
-
-import {LoadingSpinner} from './LoadingSpinner';
-import {PageBar} from './PageBar';
-
-const NODES_PER_PAGE = 25;
-
- interface GraphExplorerPropType {
- /**
- * Indicates if uploaded files are currently being parsed.
- */
- loading: boolean;
-
- /**
- * IDs for nodes stored in App's state which are the subject nodes of
- * triples from any parsed files.
- */
- subjNodes: string[];
-
- /**
- * Set id parameter in url to the given id. Used when user clicks a
- * subject node to explore.
- */
- onNodeClick: (id: string) => void;
- }
-
- interface GraphExplorerStateType {
- /** Tracks which page the user is currently viewing (0-indexed) */
- page: number;
- }
-
-/** Component to display the Graph explorer */
-class GraphExplorer extends Component<
- GraphExplorerPropType,
- GraphExplorerStateType
- > {
- /** Constructor for class, sets initial state
- * @param {GraphExplorerPropType} props the props passed in by parent
- * component
- */
- constructor(props: GraphExplorerPropType) {
- super(props);
- this.state = {
- page: 0,
- };
- }
-
- /** Renders the GraphExplorer component.
- * @return {JSX.Element} the component using TSX code
- */
- render() : JSX.Element {
- const maxPage = Math.ceil(this.props.subjNodes.length / NODES_PER_PAGE);
- const switchPage = (page: number) => this.setState({page});
- return (
-
- {/* display loading animation while waiting*/}
-
-
- {/* display list of subject node ids*/}
-
- );
- }
-}
-
-export {GraphExplorer};
diff --git a/mcf-browser/src/Header.tsx b/mcf-browser/src/Header.tsx
deleted file mode 100644
index ac04114f..00000000
--- a/mcf-browser/src/Header.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import React, {Component} from 'react';
-
-const ENTER_KEY = 13;
-
-
-interface HeaderPropType {
- /**
- * Set a 'search' parameter in url to the specified id.
- */
- searchId: Function;
- /**
- * Return to the home page.
- */
- onHomeClick: React.MouseEventHandler;
- /**
- * Ids stored in App's state for the subject nodes of triples from
- * any parsed files.
- */
- subjIds: string[];
-}
-
-interface HeaderStateType {
- /**
- * Text input from user via search bar.
- */
- searchVal: string;
-}
-
-/** Header component contains the id search bar, upload files, and return home
- * button.
- */
-class Header extends Component {
- /** Constructor for class, sets initial state
- *
- * @param {HeaderPropType} props the props passed in by parent component
- */
- constructor(props: HeaderPropType) {
- super(props);
- this.state = {
- searchVal: '',
- };
- }
-
- /**
- * Calls props method to search for an id when the user presses enter.
- * @param {Event} event OnKeyUp event from html search input element.
- */
- handleSearch(event: React.KeyboardEvent) {
- if (event.keyCode === ENTER_KEY) {
- this.props.searchId(event.target.value);
- this.setState({searchVal: ''});
- }
- }
-
- /** Renders header element
- * @return {JSX.Element} the webpage using JSX code
- */
- render() : JSX.Element {
- return (
-
- {/* return home button*/}
-
-
- {/* search for id w/dropdown of suggestions of the subject nodes*/}
- this.setState({searchVal: event.target.value})}
- onKeyUp={(event) => {
- this.handleSearch(event);
- }}/>
-
-
- );
- }
-}
-
-export {Header};
diff --git a/mcf-browser/src/Home.tsx b/mcf-browser/src/Home.tsx
deleted file mode 100644
index 1c7ed094..00000000
--- a/mcf-browser/src/Home.tsx
+++ /dev/null
@@ -1,201 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import React, {Component} from 'react';
-
-import {Series} from './back-end/time-series';
-import {ParsingError} from './back-end/utils';
-import {FileEntry} from './FileEntry';
-import {GraphExplorer} from './GraphExplorer';
-import {ParsingErrorsTable} from './ParsingErrorsTable';
-import {TimelineExplorer} from './TimelineExplorer';
-import {openFile} from './utils';
-
-interface HomePropType {
- /**
- * List of the files that have been uploaded by user.
- */
- fileList: Blob[];
- /**
- * Passes a file list to be submitted to the back-end for parsing.
- */
- upload: Function;
- /**
- * Passes a list of urls to be retrieved, then passed to the back-end
- * for parsing.
- */
- loadFiles: Function;
- /**
- * Return to the home page.
- */
- goToHome: Function;
- /**
- * Clears the loaded data from all files and resets App to its initial state.
- */
- clear: React.MouseEventHandler;
- /**
- * Error messages from parsing files specifying line number, line, and
- * helpful message indicating the error.
- */
- errs: ParsingError[];
- /**
- * Indicates if uploaded files are currently being parsed.
- */
- loading: boolean;
- /**
- * IDs for nodes stored in App's state which are the subject nodes of
- * triples from any parsed files.
- */
- subjNodes: string[];
- /**
- * Set id parameter in url to the given id. Used when user clicks a
- * subject node to explore.
- */
- onNodeClick: (id: string) => void;
- /**
- * Time series data uploaded by the user
- */
- timeData: Series[];
-}
-
-interface HomeStateType {
- /**
- * Determines if the file entry dropdown option should be displayed.
- */
- dropdown: boolean;
-}
-
-/** Displays the currently loaded files, clear button, parsing errors, and
- * subject nodes.
- */
-class Home extends Component {
- /** Constructor for class, sets initial state
- *
- * @param {HomePropType} props the props passed in by parent component
- */
- constructor(props: HomePropType) {
- super(props);
- this.state = {
- dropdown: false,
- };
- }
-
- /**
- * Toggles the boolean value of this.state.dropdown
- * whenever user expands or collapses the dropdown
- */
- toggleDropdown() {
- this.setState({dropdown: !this.state.dropdown});
- }
-
- /**
- * Renders the component
- *
- * @return {JSX.Element} the webpage using JSX code
- */
- render(): JSX.Element {
- if (this.props.fileList.length === 0) {
- // show file entry options, but do not toggle dropdown on file submission
- return (
-
-
-
-
-
- );
- }
-
- let addFileButtonClass;
- let addFileButtonText;
-
- if (this.state.dropdown) {
- addFileButtonClass = 'button expanded';
- addFileButtonText = 'Add File (-)';
- } else {
- addFileButtonClass = 'button';
- addFileButtonText = 'Add File (+)';
- }
-
- // show current files and subject nodes
- return (
-
- );
- }
-}
-
-export {Home};
diff --git a/mcf-browser/src/LoadingSpinner.tsx b/mcf-browser/src/LoadingSpinner.tsx
deleted file mode 100644
index b642c10a..00000000
--- a/mcf-browser/src/LoadingSpinner.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import React from 'react';
-
-interface LoadingSpinnerPropType {
- /**
- * Indicates if spinner should be displayed.
- */
- loading: boolean;
- /**
- * The message to be displayed while page is loading
- */
- msg: string;
-}
-
-// spinning animation to demonstrate loading, used in DisplayNode and Home
-const LoadingSpinner = (props: LoadingSpinnerPropType) => {
- if (!props.loading) {
- return null;
- }
- return (
-
-
-
-
{props.msg}
-
- );
-};
-
-export {LoadingSpinner};
diff --git a/mcf-browser/src/PageBar.tsx b/mcf-browser/src/PageBar.tsx
deleted file mode 100644
index 18f28a97..00000000
--- a/mcf-browser/src/PageBar.tsx
+++ /dev/null
@@ -1,166 +0,0 @@
-/**
- * Copyright 2022 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import React, {Component} from 'react';
-
-// the number of pages to display from the start and end initially
-// 1, 2, ... NUM_PAGES_TO_DISPLAY
-const NUM_PAGES_TO_DISPLAY = 3;
-
- interface PageBarPropType {
- /** Tracks which page the user is currently viewing (0-indexed) */
- page: number;
-
- /** Stores the maximum number of pages */
- maxPage: number;
-
- /** The function to go to the next page */
- onNextPageClicked: (page: number, maxPage: number) => void;
-
- /** The function to go to the previous page */
- onPrevPageClicked: (page: number) => void;
-
- /** The function to go to a certain page */
- onPageNumClicked: (page: number) => void;
- }
-
-/** Component for rendering the pagination bar */
-class PageBar extends Component {
- /**
- * Returns the span to be used for a given page string
- * @param {string} pageString the string to display
- * @param {number} i the index of the page string
- * @return {JSX.Element} the span element in TSX code
- */
- renderPageBox(pageString: string, i: number) : JSX.Element {
- if (pageString === '...') {
- // return non-clickable span
- return {pageString};
- } else {
- return (
- this.props.onPageNumClicked(parseFloat(pageString))}
- >
- {pageString}
-
- );
- }
- }
-
- /** Get page strings given the current page
- * @param {number} page the current page that the user is on
- * @return {string[]} the strings to render in the pagination bar
- */
- getPageStrings(page: number) : string[] {
- // Get all page strings
- const actualPage = page + 1; // Switch from 0-indexed to 1-indexed
- const maxPage = this.props.maxPage;
- let pagesToDisplay: Set;
-
- // Check if page is on the boundary
- if (
- actualPage < NUM_PAGES_TO_DISPLAY ||
- actualPage > maxPage - NUM_PAGES_TO_DISPLAY + 1
- ) {
- // Generate pages to display
- const pagesList = [];
-
- for (let i = 0; i < NUM_PAGES_TO_DISPLAY; i++) {
- pagesList.push(i + 1);
- pagesList.push(maxPage - i);
- }
-
- // Filter out duplicates and out of bounds
- pagesToDisplay = new Set(
- pagesList.filter((i) => i > 0 && i <= maxPage),
- );
- } else {
- const pagesList =
- [1, actualPage - 1, actualPage, actualPage + 1, maxPage];
- pagesToDisplay = new Set(pagesList);
- }
-
- const pages = [...pagesToDisplay];
- pages.sort((a, b) => a - b);
-
- const pageStrings = [];
- let prevPage = null;
- for (const i of pages) {
- if (prevPage && (i - prevPage) > 1) {
- pageStrings.push('...');
- }
- pageStrings.push(i.toString());
- prevPage = i;
- }
-
- return pageStrings;
- }
-
- /** Returns previous page
- * @param {number} currPage the current page the user is on
- * @return {number} the previous page
- */
- static getPrevPage(currPage: number) : number {
- return (currPage === 0) ? 0 : currPage - 1;
- }
-
- /** Return next page
- * @param {number} currPage the current page the user is on
- * @param {number} maxPage the maximum number of pages
- * @return {number} the next page
- */
- static getNextPage(currPage: number, maxPage: number) : number {
- return (currPage === maxPage - 1) ? maxPage - 1 : currPage + 1;
- }
-
-
- /**
- * Renders the bar that allows user to control the page
- * @return {JSX.Element} the component in TSX code
- */
- render() : JSX.Element {
- // Get all page strings
- const pageStrings = this.getPageStrings(this.props.page);
-
- // Render all page strings
- return (
-
- );
- }
-}
-
-export {PageBar};
diff --git a/mcf-browser/src/ParsingErrorsTable.tsx b/mcf-browser/src/ParsingErrorsTable.tsx
deleted file mode 100644
index 7a334b45..00000000
--- a/mcf-browser/src/ParsingErrorsTable.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import React from 'react';
-
-import {ParsingError} from './back-end/utils';
-
-interface ParsingErrorsTablePropType {
- /**
- * The App state's parsingErrs list of error message Objects from parsing
- * files. Each object in the array contains one file name and one list of
- * errors found in that file.
- */
- errsList: ParsingError[];
-}
-
-/* Simple component to render the parsing errors table. */
-const ParsingErrorsTable = (props: ParsingErrorsTablePropType) => {
- if (!props.errsList.length) {
- return null;
- }
- return (
-
- );
-};
-
-export {ParsingErrorsTable};
diff --git a/mcf-browser/src/TimeGraph.tsx b/mcf-browser/src/TimeGraph.tsx
deleted file mode 100644
index aa32c17c..00000000
--- a/mcf-browser/src/TimeGraph.tsx
+++ /dev/null
@@ -1,265 +0,0 @@
-/**
- * Copyright 2022 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import React, {Component} from 'react';
-import {Line} from 'react-chartjs-2';
-import {
- Chart as ChartJS,
- CategoryScale,
- LinearScale,
- PointElement,
- LineElement,
- Title,
- Tooltip,
- Legend,
-} from 'chart.js';
-
-import {Series} from './back-end/time-series';
-import {LocationMapping} from './TimelineExplorer';
-
-const COLORS = [
- '#bcf60c',
- '#fabebe',
- '#008080',
- '#e6beff',
- '#9a6324',
- '#fffac8',
- '#800000',
- '#aaffc3',
- '#e6194b',
- '#3cb44b',
- '#ffe119',
- '#4363d8',
- '#f58231',
- '#911eb4',
- '#46f0f0',
- '#f032e6',
- '#808000',
- '#ffd8b1',
- '#000075',
- '#808080',
- '#d37295',
- '#000000',
-];
-
-const LINE_OPTIONS = {
- interaction: {
- intersect: false,
- },
- scales: {
- x: {
- grid: {
- display: false,
- },
- },
- },
- plugins: {
- legend: {
- display: true,
- labels: {
- font: {
- size: 12,
- },
- },
- },
- },
- title: {},
-};
-
-ChartJS.register(
- CategoryScale,
- LinearScale,
- PointElement,
- LineElement,
- Title,
- Tooltip,
- Legend,
-);
-
-interface LineOptions {
- [element: string]: Object;
-}
-
-interface DataSet {
- data: number[];
- label: string;
- fill: boolean;
- borderColor: string;
-}
-
-interface LineData {
- labels: string[];
- datasets: DataSet[];
-}
-
-interface TimeGraphPropType {
- /**
- * Passes the data to be plotted
- */
- data: Series[];
-
- /**
- * The title of the graph
- */
- title: string;
-
- /** A mapping from a location dcid to its name */
- locationMapping: LocationMapping;
-}
-
-interface TimeGraphStateType {
- /** The lineData for the Line graph component */
- lineData: LineData;
-
- /** The lineOptions for the Line graph component */
- lineOptions: LineOptions;
-}
-
-/** Component to display a single graph */
-class TimeGraph extends Component {
- /** Constructor for class, sets initial state
- * @param {TimeGraphPropType} props the props passed in by parent component
- */
- constructor(props: TimeGraphPropType) {
- super(props);
- this.state = {
- lineData: {
- labels: [],
- datasets: [],
- },
- lineOptions: {},
- };
- }
-
- /** Sets lineData and lineOptions when rendering */
- componentDidMount() {
- const lineOptions = this.getOptions();
- const lineData = this.getLineData();
- this.setState(
- {
- lineData,
- lineOptions,
- },
- );
- }
-
- /** Returns a new set of data where each series
- * has the same x-values (the union of all x-values)
- * @return {Series[]} an array of the data where missing values
- * are filled with undefined
- */
- getUnionData(): Series[] {
- const data = [];
-
- // Get union of x-values
- const allXSet: Set = new Set();
- for (const series of this.props.data) {
- for (const datapoint of series.data) {
- allXSet.add(datapoint.x);
- }
- }
-
- // Get sorted x-values
- const allX = [...allXSet];
- allX.sort();
-
- // Fill in missing values for each Series
- for (const series of this.props.data) {
- const seriesData: {[x: string]: number} = {};
- for (const datapoint of series.data) {
- seriesData[datapoint.x] = datapoint.y;
- }
-
- // Make a copy to use
- const newSeries = series.copy();
-
- // Update with new x,y values
- const newData = allX.map((x) => {
- return {
- x,
- y: seriesData[x],
- };
- });
- newSeries.data = newData;
-
- data.push(newSeries);
- }
-
- return data;
- }
-
- /**
- * Generates the data object necessary for the Line component
- * @return {LineData} the data object to be used a prop for Line
- */
- getLineData(): LineData {
- // Change series to have union of all x-values
- // Also sorts the data in the process
- const data = this.getUnionData();
-
- if (data.length === 0) {
- return {
- labels: [],
- datasets: [],
- };
- }
-
- // Get object per series
- const labels = data[0].data.map((datapoint) => datapoint.x);
- const datasets = data.map((series, i) => {
- const labelID = series.observationAbout;
- const label = this.props.locationMapping[labelID];
- return {
- label: label,
- fill: false,
- data: series.data.map((datapoint) => datapoint.y),
- borderColor: COLORS[i % COLORS.length],
- };
- });
-
- return {labels, datasets};
- }
-
- /**
- * Generate the graph's options
- * @return {LineOptions} the options for the graph
- */
- getOptions(): LineOptions {
- const options = {...LINE_OPTIONS};
- options.title = {
- display: true,
- text: this.props.title,
- };
- return options;
- }
-
- /** Renders the TimeGraph component.
- * @return {JSX.Element} the component using TSX code
- */
- render() : JSX.Element | null {
- if (Object.keys(this.state.lineData).length == 0) {
- return null;
- }
- return (
-
-
-
- );
- }
-}
-
-export {TimeGraph};
diff --git a/mcf-browser/src/TimelineExplorer.tsx b/mcf-browser/src/TimelineExplorer.tsx
deleted file mode 100644
index 7e349eae..00000000
--- a/mcf-browser/src/TimelineExplorer.tsx
+++ /dev/null
@@ -1,490 +0,0 @@
-/**
- * Copyright 2022 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import React, {Component} from 'react';
-import Select, {MultiValue} from 'react-select';
-
-import {Series, SeriesIdObject} from './back-end/time-series';
-import {LoadingSpinner} from './LoadingSpinner';
-import {getName} from './back-end/utils';
-import {PageBar} from './PageBar';
-import {TimeGraph} from './TimeGraph';
-
-const STAT_VARS_PER_PAGE = 10;
-const INITIAL_NUM_LOCATIONS = 5;
-const GROUP_NUMBER = 20; // The maximum number of series per graph
-
-interface SubGroup {
- subGroup: Series[];
- title: string;
-}
-interface LocationGroupings {
- [group: string]: SubGroup[];
-}
-
-interface Grouping {
- [group: string]: Series[];
-}
-
-interface LocationMapping {
- [dcid: string]: string;
-}
-
-interface SelectOption {
- value: string;
- label: string;
-}
-
-interface TimelineExplorerPropType {
- /**
- * Passes the data to be plotted
- */
- data: Series[];
-
- /**
- * Indicates if uploaded files are currently being parsed.
- */
- loading: boolean;
-}
-
-interface TimelineExplorerStateType {
- /** The currently selected locations to filter by */
- locations: string[];
-
- /** A list of possible locations to select from */
- locationOptions: SelectOption[]
-
- /** A mapping from a location dcid to its name */
- locationMapping: LocationMapping;
-
- /** The list of groups to plot, grouped by stat var */
- statVarGroups: Series[][];
-
- /** Tracks which page the user is currently viewing (0-indexed) */
- page: number;
-}
-
-/** Component to display the timeline explorer */
-class TimelineExplorer extends Component<
- TimelineExplorerPropType,
- TimelineExplorerStateType
-> {
- /** Constructor for class, sets initial state
- * @param {TimelineExplorerPropType} props the props passed in by parent
- * component
- */
- constructor(props: TimelineExplorerPropType) {
- super(props);
- this.state = {
- locations: [],
- locationOptions: [],
- locationMapping: {},
- statVarGroups: [],
- page: 0,
- };
- }
-
- /**
- * Update the state variables whenever necessary given
- * the current value of this.props
- */
- updateState() {
- const locationsPromise = this.getAllLocations();
- locationsPromise.catch(
- () => {
- alert(
- `Error getting locations for data: ${this.props.data}`,
- );
- },
- );
- locationsPromise.then(
- (locationOptions) => {
- const locations = locationOptions.slice(0, INITIAL_NUM_LOCATIONS)
- .map(
- (option) => option.value,
- );
- const locationMapping: LocationMapping = {};
- locationOptions.forEach(
- (obj) => locationMapping[obj.value] = obj.label,
- );
- const page = 0;
- const statVarGroups = Object.values(
- this.getVariableToSeries(locations),
- ) as Series[][];
- this.setState(() => {
- return {
- locations,
- locationOptions,
- locationMapping,
- statVarGroups,
- page,
- };
- });
- },
- );
- }
-
- /** Tracks when component is mounted */
- componentDidMount() {
- this.updateState();
- }
-
- /** Set the location options
- * @param {TimelineExplorerPropType} prevProps the previous props
- */
- componentDidUpdate(prevProps: TimelineExplorerPropType) {
- if (prevProps.data !== this.props.data) {
- this.updateState();
- }
- }
-
- /** Returns the series objects which match the current locations filter
- * @param {string[]} locations a list of locations to filter by
- * @return {Series[]} series that match the filter
- */
- getFilteredData(locations: string[]): Series[] {
- const locationSet = new Set(locations);
-
- const filteredData = this.props.data.filter(
- (series) => locationSet.has(series.observationAbout),
- );
-
- return filteredData;
- }
-
- /** Processes the data passed in by props and returns the
- * data grouped by variableMeasured
- * @param {string[]} locations a list of locations to filter by
- * @return {Grouping} an object mapping from variableMeasured to an array
- * of Series with that variableMeasured value
- */
- getVariableToSeries(locations: string[]): Grouping {
- const output: Grouping = {};
- // Filter by location
- const data = this.getFilteredData(locations);
-
- for (const series of data) {
- const varMeasured = series.variableMeasured ?
- series.variableMeasured :
- '';
- if (!output[varMeasured]) {
- output[varMeasured] = [];
- }
- output[varMeasured].push(series);
- }
-
- return output;
- }
-
-
- /** Returns the JSX to render a group of related series
- * @param {Series[]} seriesList a list of series objects with
- * the same varMeasured
- * @param {LocationMapping} locationMapping a mapping from location dcid to
- * name
- * @return {JSX.Element} a details element plotting all of the series
- */
- renderSeriesGroup(seriesList: Series[], locationMapping: LocationMapping)
- : JSX.Element | null {
- if (seriesList.length === 0) {
- return null;
- }
- const varMeasured = seriesList[0].variableMeasured;
- const groups = this.groupSeriesByLocations(seriesList);
- const groupNames = Object.keys(groups);
-
- return (
-
- {varMeasured}
- {
- groupNames.map(
- (groupName) =>
- this.renderTimeGraph(
- groups[groupName],
- groupName,
- groupNames.length === 1,
- locationMapping,
- ),
- )
- }
-
- );
- }
-
- /** Get all locations using the observationAbout property
- * @return {SelectOption[]} a list of unique location objects
- */
- private async getAllLocations(): Promise {
- // Get an array of unique locations
- const seenLocations = new Set();
- const locations: string[] = [];
- for (const series of this.props.data) {
- const location = series.observationAbout;
- if (!seenLocations.has(location)) {
- locations.push(location);
- seenLocations.add(location);
- }
- }
-
- const labelPromises = locations.map((location) => {
- return ((location.startsWith('dcid:') ?
- getName(location.slice(5)) :
- getName(location)
- ));
- });
-
- return Promise.all(labelPromises).then(
- (labels) => {
- return locations.map((location, i) => {
- return {
- value: location,
- label: labels[i],
- };
- });
- },
- );
- }
-
-
- /** Groups data with equal values for everything except for their
- * locations into groups of groupNumber to be plotted together
- * @param {Series[]} seriesList the data to group
- * @return {LocationGroupings} an object where the keys are the group names
- * and the values are arrays where each element is a group of data
- * that contains the actual series list and the title of the graph
- */
- groupSeriesByLocations(seriesList: Series[]): LocationGroupings {
- // Group similar series
- const groups: Grouping = {};
- const exclude = ['observationAbout'];
- for (const series of seriesList) {
- const group = series.getHash(exclude);
-
- if (!groups[group]) {
- groups[group] = [];
- }
-
- groups[group].push(series);
- }
-
- // Separate groups into groups of size groupNumber
- const finalGroups: LocationGroupings = {};
-
- const groupNames = Object.keys(groups);
- for (const groupName of groupNames) {
- const group = groups[groupName];
- const numberOfSubgroups = Math.ceil(group.length / GROUP_NUMBER);
-
- finalGroups[groupName] = [];
-
- for (let i = 0; i < group.length; i += GROUP_NUMBER) {
- const subGroup = group.slice(i, i + GROUP_NUMBER);
- const title = (numberOfSubgroups > 1) ?
- `${groupName} (${i + 1} of ${numberOfSubgroups}) ` :
- `${groupName}`;
-
- finalGroups[groupName].push({subGroup, title});
- }
- }
-
- return finalGroups;
- }
-
- /**
- * Plot a TimeGraph component given all of the data and metadata
- * @param {SubGroup} seriesObj an object containing all the series to plot
- * and metadata for the plot
- * @param {LocationMapping} locationMapping a mapping from location dcid to
- * name
- * @return {JSX.Element} the TimeGraph component in TSX code
- */
- plotSeriesObj(seriesObj: SubGroup, locationMapping: LocationMapping)
- : JSX.Element {
- return ( series.id,
- ).join(',')}
- />);
- }
-
- /**
- * Renders a section containing all of the graphs for a group
- * of related series
- * @param {SubGroup[]} group an array of objects where each object contains
- * the data for a graph
- * @param {string} groupName the name of the group for the summary
- * @param {boolean} keepOpen whether or not to render the details open
- * @param {LocationMapping} locationMapping a mapping from location dcid to
- * name
- * @return {JSX.Element} the details section in TSX code
- */
- renderTimeGraph(
- group: SubGroup[],
- groupName: string,
- keepOpen: boolean,
- locationMapping: LocationMapping,
- ): JSX.Element {
- const facets: SeriesIdObject = Series.fromID(groupName);
- const facetKeys = Object.keys(facets) as (keyof typeof facets)[];
- return (
-
- {groupName}
- {facetKeys.map((facet) => {
- return (
- (facets[facet] && facet !== 'variableMeasured') ?
-