[refactor,#527][m]: move the nextjs app at root of repo to examples/dataset-frictionless.

The current app at root of repo is for single frictionless dataset. Should be in its own example so we have space going forward for multiple example and for root to have core portal.js code.

* Also refactor its README (moved from root) to reflect it is just an example
* Move design content from the root README.md into DESIGN.md
* Stub a new root README.md based largely on examples/catalog/README.md
This commit is contained in:
Rufus Pollock
2021-04-12 16:15:20 +02:00
parent 02b7974396
commit 2562781e3e
34 changed files with 71 additions and 35 deletions

View File

@@ -0,0 +1,3 @@
{
"presets": ["next/babel"]
}

View File

@@ -0,0 +1,24 @@
This examples if for presenting a single dataset. The dataset should be a [Frictionless dataset (data package)][fd] i.e. there should be a `datapackage.json`.
[fd]: https://frictionlessdata.io/data-packages/
## Install
Git clone this directory then:
```
yarn install
```
## Usage
In this directory:
```bash
export PORTAL_DATASET_PATH=/path/to/my/dataset
yarn dev
```
And you will get a nice dataset page at `http://localhost:3000`
![](https://i.imgur.com/KSEtNF1.png)

View File

@@ -0,0 +1,31 @@
import React, { useState, useEffect } from 'react';
import createPlotlyComponent from "react-plotly.js/factory";
let Plot;
const Chart = (props) => {
const [plotCreated, setPlotCreated] = useState(0) //0: false, 1: true
useEffect(() => {
import(`plotly.js-basic-dist`).then(Plotly => { //import Plotly dist when Page has been generated
Plot = createPlotlyComponent(Plotly);
setPlotCreated(1)
});
}, [])
if (!plotCreated) {
return (<div>Loading...</div>)
}
return (
<div data-testid="plotlyChart">
<Plot {...props.spec}
layout={{ autosize: true }}
style={{ width: "100%", height: "100%" }}
useResizeHandler={true}
/>
</div>
)
}
export default Chart

View File

@@ -0,0 +1,28 @@
/* eslint-disable max-len */
import React from 'react';
import { DataGrid } from '@material-ui/data-grid';
/*
* @param schema: Frictionless Table Schmea
* @param data: an array of data objects e.g. [ {a: 1, b: 2}, {a: 5, b: 7} ]
*/
const Table = ({ schema, data }) => {
const columns = schema.fields.map((field) => (
{
field: field.title || field.name,
headerName: field.name,
width: 300
}
))
data = data.map((item, index)=>{
item.id = index //Datagrid requires every row to have an ID
return item
})
return (
<div data-testid="tableGrid" style={{ height: 400, width: '100%' }}>
<DataGrid rows={data} columns={columns} pageSize={5} />
</div>
);
}
export default Table

View File

@@ -0,0 +1,51 @@
CBOE Volatility Index (VIX) time-series dataset including daily open, close,
high and low. The CBOE Volatility Index (VIX) is a key measure of market
expectations of near-term volatility conveyed by S&P 500 stock index option
prices introduced in 1993.
## Data
From the [VIX FAQ][faq]:
> In 1993, the Chicago Board Options Exchange® (CBOE®) introduced the CBOE
> Volatility Index®, VIX®, and it quickly became the benchmark for stock market
> volatility. It is widely followed and has been cited in hundreds of news
> articles in the Wall Street Journal, Barron's and other leading financial
> publications. Since volatility often signifies financial turmoil, VIX is
> often referred to as the "investor fear gauge".
>
> VIX measures market expectation of near term volatility conveyed by stock
> index option prices. The original VIX was constructed using the implied
> volatilities of eight different OEX option series so that, at any given time,
> it represented the implied volatility of a hypothetical at-the-money OEX
> option with exactly 30 days to expiration.
>
> The New VIX still measures the market's expectation of 30-day volatility, but
> in a way that conforms to the latest thinking and research among industry
> practitioners. The New VIX is based on S&P 500 index option prices and
> incorporates information from the volatility "skew" by using a wider range of
> strike prices rather than just at-the-money series.
[faq]: http://www.cboe.com/micro/vix/faq.aspx
## Preparation
Run the shell script:
. scripts/process.sh
Output data is in `data/`.
### TODO
* Incorporate computed historical data (1990-2003)
* Consider incorporating VOX data
## License
No obvious statement on [historical data page][historical]. Given size and
factual nature of the data and its source from a US company would imagine this
was public domain and as such have licensed the Data Package under the Public
Domain Dedication and License (PDDL).
[historical]: http://www.cboe.com/micro/vix/historical.aspx

View File

@@ -0,0 +1,112 @@
{
"name": "finance-vix",
"title": "VIX - CBOE Volatility Index",
"homepage": "http://www.cboe.com/micro/VIX/",
"version": "0.1.0",
"license": "PDDL-1.0",
"sources": [
{
"title": "CBOE VIX Page",
"name": "CBOE VIX Page",
"web": "http://www.cboe.com/micro/vix/historical.aspx"
}
],
"resources": [
{
"name": "vix-daily",
"path": "vix-daily.csv",
"format": "csv",
"mediatype": "text/csv",
"schema": {
"fields": [
{
"name": "Date",
"type": "date",
"description": ""
},
{
"name": "VIXOpen",
"type": "number",
"description": ""
},
{
"name": "VIXHigh",
"type": "number",
"description": ""
},
{
"name": "VIXLow",
"type": "number",
"description": ""
},
{
"name": "VIXClose",
"type": "number",
"description": ""
}
],
"primaryKey": "Date"
}
}
],
"views": [
{
"name": "simple graph",
"id": 1,
"title": "title1",
"specType": "simple",
"spec": {
"type": "line",
"group": "VIXClose",
"series": [
"VIXOpen",
"VIXHigh"
]
}
},
{
"name": "plotly graph",
"id": 2,
"specType": "plotly",
"resources": [
"vix-daily"
],
"spec": {
"group": "VIXClose",
"series": [
"VIXOpen",
"VIXHigh",
"VIXLow"
],
"data": [
{
"type": "bar"
}
],
"layout": {
"title": "Plotly Layout Title",
"height": 450,
"xaxis": {
"title": "X Axis Title"
},
"yaxis": {
"title": "Y Axis Title"
},
"font": {
"family": "\"Open Sans\", verdana, arial, sans-serif",
"size": 12,
"color": "rgb(169, 169, 169)"
},
"titlefont": {
"family": "\"Open Sans\", verdana, arial, sans-serif",
"size": 17,
"color": "rgb(76, 76, 76)"
}
},
"config": {
"displayModeBar": false
}
}
}
]
}

View File

@@ -0,0 +1,20 @@
Date,VIXOpen,VIXHigh,VIXLow,VIXClose
2004-01-02,17.96,18.68,17.54,18.22
2004-01-05,18.45,18.49,17.44,17.49
2004-01-06,17.66,17.67,16.19,16.73
2004-01-07,16.72,16.75,15.05,15.05
2004-01-08,15.42,15.68,15.32,15.61
2004-01-09,16.15,16.88,15.57,16.75
2004-01-12,17.32,17.46,16.79,16.82
2004-01-13,16.06,18.33,16.53,18.04
2004-01-14,17.29,17.03,16.04,16.75
2004-01-15,17.07,17.31,15.49,15.56
2004-01-16,15.04,15.44,14.09,15
2004-01-20,15.77,16.13,15.09,15.21
2004-01-21,15.63,15.63,14.24,14.34
2004-01-22,14.02,14.87,14.01,14.71
2004-01-23,14.73,15.05,14.56,14.84
2004-01-26,15.78,15.78,14.52,14.55
2004-01-27,15.28,15.44,14.74,15.35
2004-01-28,15.37,17.06,15.29,16.78
2004-01-29,16.88,17.66,16.79,17.14
1 Date VIXOpen VIXHigh VIXLow VIXClose
2 2004-01-02 17.96 18.68 17.54 18.22
3 2004-01-05 18.45 18.49 17.44 17.49
4 2004-01-06 17.66 17.67 16.19 16.73
5 2004-01-07 16.72 16.75 15.05 15.05
6 2004-01-08 15.42 15.68 15.32 15.61
7 2004-01-09 16.15 16.88 15.57 16.75
8 2004-01-12 17.32 17.46 16.79 16.82
9 2004-01-13 16.06 18.33 16.53 18.04
10 2004-01-14 17.29 17.03 16.04 16.75
11 2004-01-15 17.07 17.31 15.49 15.56
12 2004-01-16 15.04 15.44 14.09 15
13 2004-01-20 15.77 16.13 15.09 15.21
14 2004-01-21 15.63 15.63 14.24 14.34
15 2004-01-22 14.02 14.87 14.01 14.71
16 2004-01-23 14.73 15.05 14.56 14.84
17 2004-01-26 15.78 15.78 14.52 14.55
18 2004-01-27 15.28 15.44 14.74 15.35
19 2004-01-28 15.37 17.06 15.29 16.78
20 2004-01-29 16.88 17.66 16.79 17.14

View File

@@ -0,0 +1,51 @@
CBOE Volatility Index (VIX) time-series dataset including daily open, close,
high and low. The CBOE Volatility Index (VIX) is a key measure of market
expectations of near-term volatility conveyed by S&P 500 stock index option
prices introduced in 1993.
## Data
From the [VIX FAQ][faq]:
> In 1993, the Chicago Board Options Exchange® (CBOE®) introduced the CBOE
> Volatility Index®, VIX®, and it quickly became the benchmark for stock market
> volatility. It is widely followed and has been cited in hundreds of news
> articles in the Wall Street Journal, Barron's and other leading financial
> publications. Since volatility often signifies financial turmoil, VIX is
> often referred to as the "investor fear gauge".
>
> VIX measures market expectation of near term volatility conveyed by stock
> index option prices. The original VIX was constructed using the implied
> volatilities of eight different OEX option series so that, at any given time,
> it represented the implied volatility of a hypothetical at-the-money OEX
> option with exactly 30 days to expiration.
>
> The New VIX still measures the market's expectation of 30-day volatility, but
> in a way that conforms to the latest thinking and research among industry
> practitioners. The New VIX is based on S&P 500 index option prices and
> incorporates information from the volatility "skew" by using a wider range of
> strike prices rather than just at-the-money series.
[faq]: http://www.cboe.com/micro/vix/faq.aspx
## Preparation
Run the shell script:
. scripts/process.sh
Output data is in `data/`.
### TODO
* Incorporate computed historical data (1990-2003)
* Consider incorporating VOX data
## License
No obvious statement on [historical data page][historical]. Given size and
factual nature of the data and its source from a US company would imagine this
was public domain and as such have licensed the Data Package under the Public
Domain Dedication and License (PDDL).
[historical]: http://www.cboe.com/micro/vix/historical.aspx

View File

@@ -0,0 +1,98 @@
{
"name": "finance-vix",
"title": "VIX - CBOE Volatility Index",
"homepage": "http://www.cboe.com/micro/VIX/",
"version": "0.1.0",
"license": "PDDL-1.0",
"sources": [
{
"title": "CBOE VIX Page",
"name": "CBOE VIX Page",
"web": "http://www.cboe.com/micro/vix/historical.aspx"
}
],
"resources": [
{
"name": "vix-daily",
"path": "vix-daily.csv",
"format": "csv",
"mediatype": "text/csv",
"schema": {
"fields": [
{
"name": "Date",
"type": "date",
"description": ""
},
{
"name": "VIXOpen",
"type": "number",
"description": ""
},
{
"name": "VIXHigh",
"type": "number",
"description": ""
},
{
"name": "VIXLow",
"type": "number",
"description": ""
},
{
"name": "VIXClose",
"type": "number",
"description": ""
}
],
"primaryKey": "Date"
}
}
],
"views": [
{
"name": "plotly graph",
"id": 2,
"specType": "plotly",
"resources": [
"vix-daily"
],
"spec": {
"group": "VIXClose",
"series": [
"VIXOpen",
"VIXHigh",
"VIXLow"
],
"data": [
{
"type": "bar"
}
],
"layout": {
"title": "Plotly Layout Title",
"height": 450,
"xaxis": {
"title": "X Axis Title"
},
"yaxis": {
"title": "Y Axis Title"
},
"font": {
"family": "\"Open Sans\", verdana, arial, sans-serif",
"size": 12,
"color": "rgb(169, 169, 169)"
},
"titlefont": {
"family": "\"Open Sans\", verdana, arial, sans-serif",
"size": 17,
"color": "rgb(76, 76, 76)"
}
},
"config": {
"displayModeBar": false
}
}
}
]
}

View File

@@ -0,0 +1,20 @@
Date,VIXOpen,VIXHigh,VIXLow,VIXClose
2004-01-02,17.96,18.68,17.54,18.22
2004-01-05,18.45,18.49,17.44,17.49
2004-01-06,17.66,17.67,16.19,16.73
2004-01-07,16.72,16.75,15.05,15.05
2004-01-08,15.42,15.68,15.32,15.61
2004-01-09,16.15,16.88,15.57,16.75
2004-01-12,17.32,17.46,16.79,16.82
2004-01-13,16.06,18.33,16.53,18.04
2004-01-14,17.29,17.03,16.04,16.75
2004-01-15,17.07,17.31,15.49,15.56
2004-01-16,15.04,15.44,14.09,15
2004-01-20,15.77,16.13,15.09,15.21
2004-01-21,15.63,15.63,14.24,14.34
2004-01-22,14.02,14.87,14.01,14.71
2004-01-23,14.73,15.05,14.56,14.84
2004-01-26,15.78,15.78,14.52,14.55
2004-01-27,15.28,15.44,14.74,15.35
2004-01-28,15.37,17.06,15.29,16.78
2004-01-29,16.88,17.66,16.79,17.14
1 Date VIXOpen VIXHigh VIXLow VIXClose
2 2004-01-02 17.96 18.68 17.54 18.22
3 2004-01-05 18.45 18.49 17.44 17.49
4 2004-01-06 17.66 17.67 16.19 16.73
5 2004-01-07 16.72 16.75 15.05 15.05
6 2004-01-08 15.42 15.68 15.32 15.61
7 2004-01-09 16.15 16.88 15.57 16.75
8 2004-01-12 17.32 17.46 16.79 16.82
9 2004-01-13 16.06 18.33 16.53 18.04
10 2004-01-14 17.29 17.03 16.04 16.75
11 2004-01-15 17.07 17.31 15.49 15.56
12 2004-01-16 15.04 15.44 14.09 15
13 2004-01-20 15.77 16.13 15.09 15.21
14 2004-01-21 15.63 15.63 14.24 14.34
15 2004-01-22 14.02 14.87 14.01 14.71
16 2004-01-23 14.73 15.05 14.56 14.84
17 2004-01-26 15.78 15.78 14.52 14.55
18 2004-01-27 15.28 15.44 14.74 15.35
19 2004-01-28 15.37 17.06 15.29 16.78
20 2004-01-29 16.88 17.66 16.79 17.14

View File

@@ -0,0 +1,51 @@
CBOE Volatility Index (VIX) time-series dataset including daily open, close,
high and low. The CBOE Volatility Index (VIX) is a key measure of market
expectations of near-term volatility conveyed by S&P 500 stock index option
prices introduced in 1993.
## Data
From the [VIX FAQ][faq]:
> In 1993, the Chicago Board Options Exchange® (CBOE®) introduced the CBOE
> Volatility Index®, VIX®, and it quickly became the benchmark for stock market
> volatility. It is widely followed and has been cited in hundreds of news
> articles in the Wall Street Journal, Barron's and other leading financial
> publications. Since volatility often signifies financial turmoil, VIX is
> often referred to as the "investor fear gauge".
>
> VIX measures market expectation of near term volatility conveyed by stock
> index option prices. The original VIX was constructed using the implied
> volatilities of eight different OEX option series so that, at any given time,
> it represented the implied volatility of a hypothetical at-the-money OEX
> option with exactly 30 days to expiration.
>
> The New VIX still measures the market's expectation of 30-day volatility, but
> in a way that conforms to the latest thinking and research among industry
> practitioners. The New VIX is based on S&P 500 index option prices and
> incorporates information from the volatility "skew" by using a wider range of
> strike prices rather than just at-the-money series.
[faq]: http://www.cboe.com/micro/vix/faq.aspx
## Preparation
Run the shell script:
. scripts/process.sh
Output data is in `data/`.
### TODO
* Incorporate computed historical data (1990-2003)
* Consider incorporating VOX data
## License
No obvious statement on [historical data page][historical]. Given size and
factual nature of the data and its source from a US company would imagine this
was public domain and as such have licensed the Data Package under the Public
Domain Dedication and License (PDDL).
[historical]: http://www.cboe.com/micro/vix/historical.aspx

View File

@@ -0,0 +1,131 @@
{
"name": "finance-vix",
"title": "VIX - CBOE Volatility Index",
"homepage": "http://www.cboe.com/micro/VIX/",
"version": "0.1.0",
"license": "PDDL-1.0",
"sources": [
{
"title": "CBOE VIX Page",
"name": "CBOE VIX Page",
"web": "http://www.cboe.com/micro/vix/historical.aspx"
}
],
"resources": [
{
"name": "vix-daily",
"path": "vix-daily.csv",
"format": "csv",
"mediatype": "text/csv",
"schema": {
"fields": [
{
"name": "Date",
"type": "date",
"description": ""
},
{
"name": "VIXOpen",
"type": "number",
"description": ""
},
{
"name": "VIXHigh",
"type": "number",
"description": ""
},
{
"name": "VIXLow",
"type": "number",
"description": ""
},
{
"name": "VIXClose",
"type": "number",
"description": ""
}
],
"primaryKey": "Date"
}
}
],
"views": [
{
"name": "vega4",
"resources": [
0
],
"specType": "vega",
"spec": {
"width": 600,
"height": 300,
"data": [
{
"name": "vix-daily"
}
],
"scales": [
{
"name": "VIXOpen",
"type": "point",
"range": "width",
"domain": {
"data": "vix-daily",
"field": "VIXOpen"
}
},
{
"name": "VIXHigh",
"type": "linear",
"range": "height",
"domain": {
"data": "vix-daily",
"field": "VIXHigh"
}
}
],
"axes": [
{
"orient": "bottom",
"scale": "VIXOpen"
},
{
"orient": "left",
"scale": "VIXHigh"
}
],
"marks": [
{
"type": "line",
"from": {
"data": "vix-daily"
},
"encode": {
"enter": {
"x": {
"scale": "VIXOpen",
"field": "VIXOpen"
},
"y": {
"scale": "VIXHigh",
"field": "VIXHigh"
},
"strokeWidth": {
"value": 2
}
},
"strokeOpacity": {
"value": 1
}
},
"hover": {
"strokeOpacity": {
"value": 0.5
}
}
}
]
}
}
]
}

View File

@@ -0,0 +1,20 @@
Date,VIXOpen,VIXHigh,VIXLow,VIXClose
2004-01-02,17.96,18.68,17.54,18.22
2004-01-05,18.45,18.49,17.44,17.49
2004-01-06,17.66,17.67,16.19,16.73
2004-01-07,16.72,16.75,15.05,15.05
2004-01-08,15.42,15.68,15.32,15.61
2004-01-09,16.15,16.88,15.57,16.75
2004-01-12,17.32,17.46,16.79,16.82
2004-01-13,16.06,18.33,16.53,18.04
2004-01-14,17.29,17.03,16.04,16.75
2004-01-15,17.07,17.31,15.49,15.56
2004-01-16,15.04,15.44,14.09,15
2004-01-20,15.77,16.13,15.09,15.21
2004-01-21,15.63,15.63,14.24,14.34
2004-01-22,14.02,14.87,14.01,14.71
2004-01-23,14.73,15.05,14.56,14.84
2004-01-26,15.78,15.78,14.52,14.55
2004-01-27,15.28,15.44,14.74,15.35
2004-01-28,15.37,17.06,15.29,16.78
2004-01-29,16.88,17.66,16.79,17.14
1 Date VIXOpen VIXHigh VIXLow VIXClose
2 2004-01-02 17.96 18.68 17.54 18.22
3 2004-01-05 18.45 18.49 17.44 17.49
4 2004-01-06 17.66 17.67 16.19 16.73
5 2004-01-07 16.72 16.75 15.05 15.05
6 2004-01-08 15.42 15.68 15.32 15.61
7 2004-01-09 16.15 16.88 15.57 16.75
8 2004-01-12 17.32 17.46 16.79 16.82
9 2004-01-13 16.06 18.33 16.53 18.04
10 2004-01-14 17.29 17.03 16.04 16.75
11 2004-01-15 17.07 17.31 15.49 15.56
12 2004-01-16 15.04 15.44 14.09 15
13 2004-01-20 15.77 16.13 15.09 15.21
14 2004-01-21 15.63 15.63 14.24 14.34
15 2004-01-22 14.02 14.87 14.01 14.71
16 2004-01-23 14.73 15.05 14.56 14.84
17 2004-01-26 15.78 15.78 14.52 14.55
18 2004-01-27 15.28 15.44 14.74 15.35
19 2004-01-28 15.37 17.06 15.29 16.78
20 2004-01-29 16.88 17.66 16.79 17.14

View File

@@ -0,0 +1,8 @@
module.exports = {
testPathIgnorePatterns: ["<rootDir>/.next/", "<rootDir>/node_modules/", "<rootDir>/examples/catalog/*"],
setupFilesAfterEnv: ["<rootDir>/tests/setupTests.js"],
transform: {
"^.+\\.(js|jsx|ts|tsx)$": "<rootDir>/node_modules/babel-jest",
"\\.(css|less|scss|sass)$": "identity-obj-proxy"
}
};

View File

@@ -0,0 +1,32 @@
import remark from 'remark'
import html from 'remark-html'
import { Dataset } from 'frictionless.js'
import toArray from 'stream-to-array'
export async function getDataset(directory) {
// get dataset descriptor and resources
const f11sDataset = await Dataset.load(directory)
const descriptor = f11sDataset.descriptor
const resources = await Promise.all(f11sDataset.resources.map(async (resource) => {
let _tmp = resource.descriptor
let rowStream = await resource.rows({ keyed: true })
_tmp.sample = await toArray(rowStream)
_tmp.size = resource.size
return _tmp
}))
const readme = descriptor.readme
const processed = await remark()
.use(html)
.process(readme)
const readmeHtml = processed.toString()
const dataset = {
readme: readme,
readmeHtml: readmeHtml,
descriptor: descriptor,
resources: resources
}
return dataset
}

View File

@@ -0,0 +1,110 @@
import { simpleToPlotly, plotlyToPlotly, vegaToVega } from 'datapackage-render'
/**
* Prepare views for dataset
* @params {object} dataset object of the form:
* { readme: readme,
readmeHtml: readmeHtml,
descriptor: descriptor,
resources: resources
}
*/
export function addView(dataset) {
const views = dataset.descriptor.views
const countViews = views ? views.length : 0
if (countViews === 0) {
return {
props: {
dataset,
error: true
}
}
}
const specs = {} //hold list of view specs
for (let i = 0; i < countViews; i++) {
const view = views[i]
if (("resources" in view) && Array.isArray(view.resources)) {
let resource;
const resourceKey = view.resources[0]
if (typeof resourceKey == 'number') {
resource = dataset.resources[resourceKey]
view.resources[0] = resource
} else {
resource = dataset.resources.filter((resource) => {
return resource.name == resourceKey
})
view.resources[0] = resource[0]
}
} else {
view.resources = [dataset.resources[0]] //take the first resources in datapackage
}
view.resources[0].data = getDataForViewSpec(view.resources[0], view.specType)
view.resources[0]._values = view.resources[0].data
if (view.specType === 'simple') {
try {
const spec = simpleToPlotly(view)
if (spec) {
spec.specType = 'simple'
specs[i] = spec
}
} catch (err) {
console.log(err);
}
} else if (view.specType === 'plotly') {
try {
const spec = plotlyToPlotly(view)
if (spec) {
spec.specType = 'plotly'
specs[i] = spec
}
} catch (err) {
console.log(err);
}
} else if (view.specType === 'vega') {
try {
const spec = vegaToVega(view)
if (spec) {
spec.specType = 'vega'
specs[i] = spec
}
} catch (err) {
console.log(err);
}
}
}
return {
props: {
dataset,
specs: JSON.stringify(specs),
error: false
}
}
}
/**
* Generates the data for each view spec. Plotly and Vega accept different data
* formats.
* @param {*} resource
* @param {*} specType
*/
export function getDataForViewSpec(resource, specType) {
if (specType == "vega") {
return resource.sample
} else if (["simple", 'plotly'].includes(specType)) {
const sample = resource.sample
let data = []
data.push(Object.keys(sample[0])) //add the column names
for (let i = 0; i < sample.length; i++) {
const item = sample[i];
data.push(Object.values(item)) //add the rows
}
return data
}
}

View File

@@ -0,0 +1,57 @@
{
"name": "portal",
"description": "The data presentation framework",
"author": "Datopian",
"license": "MIT",
"version": "0.1.0",
"bin": {
"portal": "./bin/portal.js"
},
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"test": "jest --coverage"
},
"dependencies": {
"@material-ui/core": "^4.11.3",
"@material-ui/data-grid": "^4.0.0-alpha.20",
"@tailwindcss/typography": "^0.4.0",
"autoprefixer": "^10.0.4",
"datapackage-render": "git+https://github.com/frictionlessdata/datapackage-render-js.git",
"filesize": "^6.1.0",
"frictionless.js": "^0.13.4",
"next": "latest",
"plotly.js-basic-dist": "^1.58.4",
"postcss": "^8.1.10",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-plotly.js": "^2.5.1",
"react-table": "^7.6.3",
"react-vega": "^7.4.2",
"remark": "^13.0.0",
"remark-html": "^13.0.1",
"tailwindcss": "^2.0.2",
"vega": "^5.19.1",
"vega-lite": "^5.0.0",
"chalk": "^4.1.0",
"commander": "^6.2.0",
"cpy": "^8.1.1",
"cross-spawn": "^7.0.3",
"figlet": "^1.5.0",
"listr": "^0.14.3",
"open": "^8.0.2",
"ora": "^5.1.0",
"prompts": "^2.4.0"
},
"devDependencies": {
"@testing-library/dom": "^7.29.6",
"@testing-library/jest-dom": "^5.11.9",
"@testing-library/react": "^11.2.5",
"babel-jest": "^26.6.3",
"identity-obj-proxy": "^3.0.0",
"jest": "^26.6.3",
"jest-canvas-mock": "^2.3.1",
"jest-dom": "^4.0.0"
}
}

View File

@@ -0,0 +1,8 @@
import '../styles/globals.css'
import '../styles/tailwind.css'
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp

View File

@@ -0,0 +1,214 @@
import path from 'path'
import Head from 'next/head'
import Table from '../components/Table'
import filesize from 'filesize'
import { Vega } from 'react-vega';
import { getDataset } from '../lib/dataset'
import Chart from '../components/Chart'
import { addView } from '../lib/utils'
const datasetsDirectory = process.env.PORTAL_DATASET_PATH || path.join(process.cwd(), "fixtures", "datasetsDoubleView")
export default function Home({ dataset, specs }) {
if (!dataset && !specs) {
return (
<div className="container">
<Head>
<title>Dataset</title>
<link rel="icon" href="/favicon.ico" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link href="https://fonts.googleapis.com/css2?family=Inconsolata&display=swap" rel="stylesheet" />
</Head>
<h1 data-testid="datasetTitle" className="text-3xl font-bold mb-8">
No dataset found in path
</h1>
</div>
)
}
const descriptor = dataset['descriptor']
const resources = dataset['resources']
let datasetSize = 0
if (resources) {
datasetSize = resources.length == 1 ?
resources[0].size :
resources.reduce((accumulator, currentValue) => {
return accumulator.size + currentValue.size
})
}
return (
<div className="container">
<Head>
<title>Dataset</title>
<link rel="icon" href="/favicon.ico" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link href="https://fonts.googleapis.com/css2?family=Inconsolata&display=swap" rel="stylesheet" />
</Head>
<section className="m-8" name="key-info">
<h1 data-testid="datasetTitle" className="text-3xl font-bold mb-8">
{descriptor.title}
</h1>
<h1 className="text-2xl font-bold mb-4">Key info</h1>
<div className="grid grid-cols-7 gap-4">
<div>
<h3 className="text-1xl font-bold mb-2">Files</h3>
</div>
<div>
<h3 className="text-1xl font-bold mb-2">Size</h3>
</div>
<div>
<h3 className="text-1xl font-bold mb-2">Format</h3>
</div>
<div>
<h3 className="text-1xl font-bold mb-2">Created</h3>
</div>
<div>
<h3 className="text-1xl font-bold mb-2">Updated</h3>
</div>
<div>
<h3 className="text-1xl font-bold mb-2">Licence</h3>
</div>
<div>
<h3 className="text-1xl font-bold mb-2">Source</h3>
</div>
</div>
<div className="grid grid-cols-7 gap-4">
<div>
<h3 className="text-1xl">{resources.length}</h3>
</div>
<div>
<h3 className="text-1xl">{filesize(datasetSize, { bits: true })}</h3>
</div>
<div>
<h3 className="text-1xl">{resources[0].format} zip</h3>
</div>
<div>
<h3 className="text-1xl">{descriptor.created}</h3>
</div>
<div>
<h3 className="text-1xl">{descriptor.updated}</h3>
</div>
<div>
<h3 className="text-1xl">{descriptor.license}</h3>
</div>
<div>
<h3 className="text-1xl">
<a className="text-yellow-600"
href={descriptor.sources[0].web}>
{descriptor.sources[0].title}
</a>
</h3>
</div>
</div>
</section>
<section className="m-8" name="file-list">
<h1 className="text-2xl font-bold mb-4">Data Files</h1>
<div className="grid grid-cols-7 gap-4">
<div>
<h3 className="text-1xl font-bold mb-2">File</h3>
</div>
<div>
<h3 className="text-1xl font-bold mb-2">Description</h3>
</div>
<div>
<h3 className="text-1xl font-bold mb-2">Size</h3>
</div>
<div>
<h3 className="text-1xl font-bold mb-2">Last Changed</h3>
</div>
<div>
<h3 className="text-1xl font-bold mb-2">Download</h3>
</div>
</div>
{resources.map((resource, index) => {
return (
<div key={`${index}_${resource.name}`} className="grid grid-cols-7 gap-4">
<div>
<h3 className="text-1xl">{resource.name}</h3>
</div>
<div>
<h3 className="text-1xl">{resource.description || "No description"}</h3>
</div>
<div>
<h3 className="text-1xl">{filesize(resource.size, { bits: true })}</h3>
</div>
<div>
<h3 className="text-1xl">{resource.updated}</h3>
</div>
<div>
<h3 className="text-1xl">
<a className="text-yellow-600" href={resource.path}>
{resource.format} ({filesize(resource.size, { bits: true })})
</a>
</h3>
</div>
</div>
)
})}
</section>
<section className="m-8" name="graph">
<h1 className="text-2xl font-bold mb-4">Graph</h1>
{!specs || Object.keys(specs).length == 0 ? (<div>
<h1>No graph to display</h1>
</div>) :
(
Object.values(JSON.parse(specs)).map((spec, i) => {
if (spec.specType == "vega") {
return (
<div key={`${i}_views`} className="ml-14">
<Vega spec={spec} />
</div>
)
} else if (["simple", "plotly"].includes(spec.specType)) {
return (
<div key={`${i}_views`}>
<Chart spec={spec} />
</div>)
} else {
return <h1 key={`${i}_views`}>Cannot display view</h1>
}
})
)}
</section>
<section className="m-8" name="sample-table" >
<h1 className="text-2xl font-bold mb-4">Data Preview</h1>
<h2 className="text-1xl">{descriptor.title}</h2>
{resources[0].sample ? (
<Table data={resources[0].sample} schema={resources[0].schema} />
) : (
'No preview is available for this dataset'
)}
</section>
<section className="m-8" name="sample-table">
<h1 className="text-2xl font-bold mb-4">README</h1>
<div className="prose">
<div dangerouslySetInnerHTML={{ __html: dataset.readmeHtml }} />
</div>
</section>
</div>
)
}
export async function getStaticProps() {
if (!datasetsDirectory) {
return { props: {} }
}
const dataset = await getDataset(datasetsDirectory)
const datasetWithViews = addView(dataset)
return datasetWithViews
}

View File

@@ -0,0 +1,8 @@
// If you want to use other PostCSS plugins, see the following:
// https://tailwindcss.com/docs/using-with-preprocessors
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,3 @@
.MuiTableCell-root {
@apply font-mono
}

View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@@ -0,0 +1,22 @@
const defaultTheme = require("tailwindcss/defaultTheme");
module.exports = {
purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
darkMode: false, // or 'media' or 'class'
theme: {
container: {
center: true,
},
extend: {
fontFamily: {
mono: ["Inconsolata", ...defaultTheme.fontFamily.mono]
}
},
},
variants: {
extend: {},
},
plugins: [
require('@tailwindcss/typography'),
],
}

View File

@@ -0,0 +1,26 @@
import React from 'react'
import {render } from '@testing-library/react';
import path from 'path'
import Chart from '../../components/Chart';
import { getDataset } from "../../lib/dataset"
import { addView } from '../../lib/utils'
let dataset
let datasetWithView
beforeAll(async () => {
const datasetsDirectory = path.join(process.cwd(), 'fixtures', 'datasetsPlotlyView')
dataset = await getDataset(datasetsDirectory)
datasetWithView = addView(dataset)
});
/** @test {Chart Component} */
describe('Chart Component', () => {
it('should render without crashing', async () => {
const spec = JSON.parse(datasetWithView.props.specs)[0]
const { findByTestId } = render(<Chart spec={spec} />)
expect(await findByTestId("plotlyChart"))
});
});

View File

@@ -0,0 +1,27 @@
import React from 'react'
import { render } from '@testing-library/react';
import path from 'path'
import Table from '../../components/Table';
import { getDataset } from "../../lib/dataset"
let dataset
beforeAll(async () => {
const datasetsDirectory = path.join(process.cwd(), 'fixtures', 'datasetsPlotlyView')
dataset = await getDataset(datasetsDirectory)
});
/** @test {Table Component} */
describe('Table Component', () => {
it('should render without crashing', async () => {
const resource = dataset.resources[0]
render(<Table data={resource.sample} schema={resource.schema} />)
});
it('tableGrid div is found', async () => {
const resource = dataset.resources[0]
const { findByTestId } = render(<Table data={resource.sample} schema={resource.schema} />)
expect(await findByTestId('tableGrid'))
});
});

View File

@@ -0,0 +1,33 @@
import { getDataset } from "../../lib/dataset"
import path from 'path'
let directory
let dataset
beforeAll(async () => {
directory = path.join(process.cwd(), 'fixtures', 'datasetsDoubleView')
dataset = await getDataset(directory)
})
describe("Dataset", () => {
it("loads a dataset from a local folder", async () => {
expect(dataset).toStrictEqual(
expect.objectContaining({
readme: expect.any(String),
readmeHtml: expect.any(String),
descriptor: expect.any(Object),
resources: expect.any(Object),
})
)
})
it("returns a resource with required fields", () => {
const resource = dataset.resources[0]
const expectedFields = ["path", "pathType", "name", "format", "mediatype",
"schema", "encoding", "sample", "size"]
expect(expectedFields).toStrictEqual(
Object.keys(resource)
)
})
});

View File

@@ -0,0 +1,78 @@
import path from 'path'
import { getDataset } from "../../lib/dataset"
import { addView, getDataForViewSpec } from '../../lib/utils'
const plotlyDatasetsDirectory = path.join(process.cwd(), 'fixtures', 'datasetsPlotlyView')
const vegaDatasetsDirectory = path.join(process.cwd(), 'fixtures', 'datasetsVegaView')
const doubleDatasetsDirectory = path.join(process.cwd(), 'fixtures', 'datasetsDoubleView')
let plotlyDataset
let vegaDataset
let doubleDataset
let plotlyDatasetWithView
let vegaDatasetWithView
let doubleDatasetWithView
beforeAll(async () => {
plotlyDataset = await getDataset(plotlyDatasetsDirectory)
vegaDataset = await getDataset(vegaDatasetsDirectory)
doubleDataset = await getDataset(doubleDatasetsDirectory)
plotlyDatasetWithView = addView(plotlyDataset)
vegaDatasetWithView = addView(vegaDataset)
doubleDatasetWithView = addView(doubleDataset)
});
describe("AddView", () => {
it("_value field is added to Plotly datapackage", () => {
const resource = plotlyDatasetWithView.props.dataset.resources[0]
expect("_values" in resource).toBe(true)
expect(resource["_values"].length > 0).toBe(true)
});
it("Plotly spec is added to datapackage", () => {
const spec = JSON.parse(plotlyDatasetWithView.props.specs)[0]
expect(spec.specType).toBe("plotly")
expect(spec.layout.title).toBe("Plotly Layout Title")
expect(spec.data[0].x.length).toBeGreaterThan(0)
expect(spec.data[0].y.length).toBeGreaterThan(0)
});
it("_value field is added to datapackage with double views", () => {
const resources = doubleDatasetWithView.props.dataset.resources
resources.map((resource) => {
expect("_values" in resource).toBe(true)
expect(resource["_values"].length > 0).toBe(true)
})
});
it("view spec is created for each view in a datapackage", () => {
const specs = JSON.parse(doubleDatasetWithView.props.specs)
const simpleSpec = specs[0]
const plotlySpec = specs[1]
expect(simpleSpec.specType).toBe("simple")
expect(simpleSpec.layout.title).toBe("title1")
expect(simpleSpec.data[0].x.length).toBeGreaterThan(0)
expect(simpleSpec.data[0].y.length).toBeGreaterThan(0)
expect(plotlySpec.specType).toBe("plotly")
expect(plotlySpec.layout.title).toBe("Plotly Layout Title")
expect(plotlySpec.data[0].x.length).toBeGreaterThan(0)
expect(plotlySpec.data[0].y.length).toBeGreaterThan(0)
});
});
describe("getDataForViewSpec", () => {
it("Generates right data for vega spec", ()=>{
const resource = vegaDataset.resources[0]
const data = getDataForViewSpec(resource, "vega")
expect(data).toStrictEqual(resource.sample)
})
it("Generates right data for plotly spec", ()=>{
const resource = plotlyDataset.resources[0]
const data = getDataForViewSpec(resource, "plotly")
expect(data).not.toStrictEqual(resource.sample[0])
expect(data[0]).toStrictEqual(Object.keys(resource.sample[0]))
})
})

View File

@@ -0,0 +1,39 @@
import React from 'react'
import { render } from '@testing-library/react';
import path from 'path'
import Home from '../../pages/index';
import { getDataset } from "../../lib/dataset"
import { addView } from '../../lib/utils'
let plotlyDatasetWithView
beforeAll(async () => {
const plotlyDatasetsDirectory = path.join(process.cwd(), 'fixtures', 'datasetsPlotlyView')
const plotlyDataset = await getDataset(plotlyDatasetsDirectory)
plotlyDatasetWithView = addView(plotlyDataset)
});
/** @test {Home Component} */
describe('Home Component', () => {
it('should render without crashing', async () => {
const dataset = plotlyDatasetWithView.props.dataset
const specs = plotlyDatasetWithView.props.specs
const { findAllByText } = render(<Home dataset={dataset} specs={specs} />)
expect(await findAllByText('README'))
});
it('Sections are found in home page', async () => {
const dataset = plotlyDatasetWithView.props.dataset
const specs = plotlyDatasetWithView.props.specs
const { findByTestId, findAllByText } = render(<Home dataset={dataset} specs={specs} />)
expect(await findAllByText('Key info'))
expect(await findAllByText('Data Files'))
expect(await findAllByText('Graph'))
expect(await findAllByText('Data Preview'))
expect(await findByTestId('datasetTitle'))
});
});

View File

@@ -0,0 +1,2 @@
import 'jest-canvas-mock';
import "@testing-library/jest-dom/extend-expect";

File diff suppressed because it is too large Load Diff