Implementing dataset grid

parent 40258472
......@@ -7125,6 +7125,11 @@
"minimist": "0.0.8"
}
},
"moment": {
"version": "2.22.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.22.1.tgz",
"integrity": "sha512-shJkRTSebXvsVqk56I+lkb2latjBs8I+pc2TzWc545y2iFnSjm7Wg0QMh+ZWcdSLQyGEau5jI8ocnmkyTgr9YQ=="
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
......@@ -9285,6 +9290,11 @@
"warning": "3.0.0"
}
},
"react-moment": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/react-moment/-/react-moment-0.7.0.tgz",
"integrity": "sha1-nMtch75oPQjCU7SEaRZ0Q/CcPYk="
},
"react-overlays": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.8.3.tgz",
......
public/favicon.ico

3.78 KB | W: | H:

public/favicon.ico

2.89 KB | W: | H:

public/favicon.ico
public/favicon.ico
public/favicon.ico
public/favicon.ico
  • 2-up
  • Swipe
  • Onion skin
......@@ -25,12 +25,10 @@
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<link rel="stylesheet" href="https://npmcdn.com/react-bootstrap-table/dist/react-bootstrap-table.min.css">
<title>React App</title>
<title>ESRF Portal</title>
</head>
<body>
<noscript>
......
import React, { Component } from 'react';
import './App.css';
import LoginContainer from './containers/LoginContainer.js';
import MyDataContainer from './containers/MyDataContainer.js';
import InvestigationsContainer from './containers/InvestigationsContainer.js';
import DOIContainer from './containers/DOIContainer.js';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import LoginForm from './components/Login/LoginForm'
import Header from './components/Header.js'
import { Menu } from './components/Menu/Menu.js'
import DatasetsContainer from './containers/DatasetsContainer.js';
import { BrowserRouter as Router, Route } from "react-router-dom";
class App extends Component {
render() {
return (
<Router>
<Router path='/'>
<div>
<Route path="/" component={LoginContainer} />
<Route path="/investigations" component={MyDataContainer} />
<Route path="/" component={LoginContainer} />
<Route path="/investigations" component={InvestigationsContainer} />
<Route path="/doi" component={DOIContainer} />
<Route path="/topics" />
<Route path="/investigation/:id" component={DatasetsContainer} />
</div>
</Router>
......
import axios from "axios"
import {
getInvestigationsByUser,
getInvestigationsWithDOI } from '../api/icat/icat.js'
getInvestigationsWithDOI,
getDatasetsByInvestigationId
} from '../api/icat/icat.js'
import { FECTH_INVESTIGATIONS, FECTH_INVESTIGATIONS_DOI} from '../constants/ActionTypes'
import {
FECTH_INVESTIGATIONS,
FECTH_INVESTIGATIONS_DOI,
FECTH_DATASETS_BY_INVESTIGATION_ID
} from '../constants/ActionTypes'
export function fetchMyInvestigations(sessionId, username) {
return function (dispatch) {
......@@ -27,3 +33,13 @@ export function fetchInvestigationsWithDOI(sessionId, username) {
}
}
export function fetchDatasetsByInvestigationId(sessionId, username, investigationId){
return function (dispatch) {
dispatch(
{
type: FECTH_DATASETS_BY_INVESTIGATION_ID,
payload : axios.get(getDatasetsByInvestigationId(sessionId, username, investigationId))
});
}
}
import ICAT from '../../config/icat/icat.js'
//const GET_INVESTIGATIONS_BY_USER = "query=select distinct investigation from Investigation investigation , investigation.investigationUsers as investigationUserPivot , investigationUserPivot.user as investigationUser where investigationUser.name = ':user' ORDER BY investigation.name asc limit 0, 50"
const GET_INVESTIGATIONS_BY_USER = "query=select distinct investigation from Investigation investigation , investigation.investigationUsers as investigationUserPivot , investigationUserPivot.user as investigationUser ORDER BY investigation.name asc limit 0, 300"
const GET_INVESTIGATIONS_BY_USER = "query=select distinct investigation from Investigation investigation , investigation.investigationUsers as investigationUserPivot , investigationUserPivot.user as investigationUser order by investigation.startDate DESC"
const GET_INVESTIGATIONS_WITH_DOI = "query=select distinct investigation from Investigation investigation , investigation.investigationUsers as investigationUserPivot , investigationUserPivot.user as investigationUser where investigation.doi <> null ORDER BY investigation.name asc limit 0, 300"
const GET_INVESTIGATIONS_WITH_DOI = "query=select distinct investigation from Investigation investigation , investigation.investigationUsers as investigationUserPivot , investigationUserPivot.user as investigationUser where investigation.doi <> null ORDER BY investigation.name asc "
const GET_DATASETS_BY_INVESTIGATION_ID = "query=SELECT dataset.id, dataset.name, dataset.startDate, dataset.endDate, investigation.name, parameterType.name, parameter.stringValue, parameter.numericValue FROM DatasetParameter as parameter JOIN parameter.dataset dataset JOIN parameter.type parameterType JOIN dataset.investigation investigation where investigation.id = :investigationId";
const GET_SESSION = "sessionId=:sessionId";
const ICAT_ENTITY_MANAGER = ICAT.server + "/icat/entityManager?";
......@@ -22,3 +24,8 @@ export function getInvestigationsWithDOI(sessionId, user){
return ICAT_ENTITY_MANAGER + getSession(sessionId) + "&" + GET_INVESTIGATIONS_WITH_DOI.replace(":user", user) + "&server=https://icat.esrf.fr";
}
export function getDatasetsByInvestigationId(sessionId, user, investigationId){
return ICAT_ENTITY_MANAGER + getSession(sessionId) + "&" + GET_DATASETS_BY_INVESTIGATION_ID.replace(":investigationId", investigationId) + "&server=https://icat.esrf.fr";
}
import React from 'react';
import PageHeader from 'react-bootstrap/lib/Grid';
class Header extends React.Component {
render() {
......
span.doiBadge {
font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif;
color: #fff;
background: #000;
text-decoration: none;
padding: 2px 0 2px 4px;
border-color: #000;
}
span.doiBadge a {
color: #fff;
background: #09c;
text-decoration: none;
margin-left: 4px;
padding: 2px 5px 2px 4px;
border-radius: 0 5px 5px 0;
}
a {
color: #007bff;
text-decoration: none;
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
.borderRadius-5 {
border-radius: 5px;
}
.font-1p4rem {
font-size: 1.4rem;
}
.investigation-table-container{
margin-top: 5px;
margin-bottom: 10px;
margin-right: 30px;
margin-left: 30px;
}
import React from 'react';
import PropTypes from 'prop-types'
import Button from 'react-bootstrap-button-loader';
//import { Grid, Row, Col } from 'react-bootstrap';
import Moment from 'react-moment';
import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table';
import './InvestigationTable.css';
class InvestigationTable extends React.Component {
constructor(props) {
......@@ -12,29 +11,67 @@ class InvestigationTable extends React.Component {
fetching : this.props.data.fetching,
}
}
componentDidMount(){
}
doiFormatter(cell, row) { // String example
if (cell != null) {
//return `<span class='doiBadge borderRadius-5 font-1p4rem'>DOI<a id='doiLink' href='https://doi.esrf.fr/{cell}'>{cell}</a></span>`;
return `<span class='doiBadge borderRadius-5 font-1p4rem'>DOI<a id='doiLink' target='_blank' href='https://doi.esrf.fr/${cell}' >${cell}</a></span>`;
}
}
dateFormatter(cell, row) { // String example
if (cell != null) {
return <Moment parse="YYYY-MM-DD HH:mm" format="DD/MM/YYYY">${cell}</Moment>;
}
}
volumeFormatter(cell, row) { // String example
return "<kbd style='background-color:#E8E8E8;color:gray;'>" + Math.floor((Math.random() * 100) + 1) + " GB </kbd>";
}
datasetFormatter(cell, row) { // String example
return "<kbd style='background-color:#E8E8E8;color:gray;'>" + Math.floor((Math.random() * 10000) + 100) + "</kbd>";
}
nameFormatter(cell, row) { // String example
return `<a href='/investigation/${row.id}'><span class="glyphicon glyphicon-circle-arrow-right"> ${cell} </span></a>`;
}
render() {
if (this.props.fetching){
return "Loading";
}
const options = {
paginationSize: 5,
sizePerPage: 25,
paginationShowsTotal: true,
hidePageListOnlyOnePage: true
};
return (
/*<Grid fluid>
{this.state.investigations.map((object, i) =>
<Row key={i} className="show-grid">
<Col xs={2} md={2}>{object.name}</Col>
<Col xs={6} md={6}>{object.summary}</Col>
<Col xs={2} md={2}>{object.startDate}</Col>
<Col xs={2} md={2}>{object.doi}</Col>
</Row>)}
</Grid>*/
<div>
<BootstrapTable data={ this.state.investigations } pagination>
<TableHeaderColumn dataField='id' isKey>id</TableHeaderColumn>
<TableHeaderColumn dataField='name'>Name</TableHeaderColumn>
<TableHeaderColumn dataField='summary'>Summary</TableHeaderColumn>
<TableHeaderColumn dataField='doi'>DOI</TableHeaderColumn>
</BootstrapTable>
<div className='investigation-table-container'>
<BootstrapTable
data={ this.state.investigations }
options={ options }
pagination
striped
search
hover
condensed>
<TableHeaderColumn width='10%' hidden isKey dataField='id'>id</TableHeaderColumn>
<TableHeaderColumn width='10%' dataFormat={ this.nameFormatter } dataField='name'>Name</TableHeaderColumn>
<TableHeaderColumn dataSort width='5%' dataField='visitId'>Beamline</TableHeaderColumn>
<TableHeaderColumn dataSort dataField='summary'>Title</TableHeaderColumn>
<TableHeaderColumn width='5%' dataFormat={ this.datasetFormatter }>Datasets</TableHeaderColumn>
<TableHeaderColumn width='5%' dataFormat={ this.volumeFormatter }>Volume</TableHeaderColumn>
<TableHeaderColumn dataSort width='5%' dataField='startDate' dataFormat={ this.dateFormatter }>Date</TableHeaderColumn>
<TableHeaderColumn dataSort width='5%' dataField='releaseDate' dataFormat={ this.dateFormatter }>Release</TableHeaderColumn>
<TableHeaderColumn width='15%' dataFormat={ this.doiFormatter } dataField='doi'></TableHeaderColumn>
</BootstrapTable>
</div>
);
......
......@@ -31,7 +31,7 @@ class LoginForm extends React.Component {
}
render() {
const { isLoading, error } = this.state;
/** If there is sessionId it means that we are already been authenticated **/
if (this.props.user.sessionId) {
......
import React from 'react';
import { Glyphicon, Thumbnail, Nav, Image, NavItem, NavDropdown, MenuItem, Navbar, FormGroup, FormControl, Button } from 'react-bootstrap';
import { Link } from 'react-router-dom';
import { Glyphicon, Nav, NavItem, NavDropdown, MenuItem, Navbar, Button } from 'react-bootstrap';
import logo from'../../images/ebs.gif';
import './Menu.css';
......@@ -24,7 +23,7 @@ export class Menu extends React.Component {
return <Navbar collapseOnSelect style={{ marginTop:'5px'}}>
<Navbar.Header >
<Navbar.Brand >
<a href="/" style={{fontSize:'24px'}}><img src={logo} style={{height:'15', width:'15'}} />ESRF Data Portal</a>
<a href="/" style={{fontSize:'24px'}}><img alt="" src={logo} style={{height:'15', width:'15'}} />ESRF Data Portal</a>
</Navbar.Brand>
<Navbar.Toggle />
</Navbar.Header>
......
......@@ -11,5 +11,8 @@ export const FECTH_INVESTIGATIONS_DOI = "FECTH_INVESTIGATIONS_DOI"
export const FECTH_INVESTIGATIONS_DOI_FULFILLED = "FECTH_INVESTIGATIONS_DOI_FULFILLED"
export const FECTH_INVESTIGATIONS_DOI_PENDING = "FECTH_INVESTIGATIONS_DOI_PENDING"
export const FECTH_DATASETS_BY_INVESTIGATION_ID="FECTH_DATASETS_BY_INVESTIGATION_ID"
export const FECTH_DATASETS_BY_INVESTIGATION_ID_FULFILLED="FECTH_DATASETS_BY_INVESTIGATION_ID_FULFILLED"
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import axios from 'axios';
import { fetchInvestigationsWithDOI} from '../actions/myData.js';
import Login from '../components/Login/LoginForm';
import Header from '../components/Header.js'
import { Menu } from '../components/Menu/Menu.js'
import { fetchInvestigationsWithDOI} from '../actions/data.js';
import InvestigationTable from "../components/Investigation/InvestigationTable.js"
......@@ -26,8 +21,7 @@ class DOIContainer extends Component {
this.props.fetchInvestigationsWithDOI(this.state.sessionId, this.state.username);
}
render() {
render() {
return (<InvestigationTable data={this.props.doi}></InvestigationTable>);
}
}
......
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { fetchDatasetsByInvestigationId } from '../actions/data.js';
import InvestigationTable from "../components/Investigation/InvestigationTable.js"
class DatasetsContainer extends Component {
constructor(props) {
super(props);
this.state = {
investigationId : this.props.match.params.id,
username : this.props.user.username,
sessionId : this.props.user.sessionId,
datasets : this.props.data.datasets
}
}
componentDidMount(){
this.props.fetchDatasetsByInvestigationId(this.state.sessionId, this.state.username, this.state.investigationId);
}
render() {
return this.state.username;
}
}
function mapStateToProps(state) {
return {
user: state.user,
data : state.data
};
}
function mapDispatchToProps(dispatch) {
return {
fetchDatasetsByInvestigationId: bindActionCreators(fetchDatasetsByInvestigationId, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(DatasetsContainer);
\ No newline at end of file
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import axios from 'axios';
import { fetchMyInvestigations} from '../actions/myData.js';
import Login from '../components/Login/LoginForm';
import Header from '../components/Header.js'
import { Menu } from '../components/Menu/Menu.js'
import { fetchMyInvestigations} from '../actions/data.js';
import InvestigationTable from "../components/Investigation/InvestigationTable.js"
class MyDataContainer extends Component {
class InvestigationsContainer extends Component {
constructor(props) {
super(props);
this.state = {
investigations : this.props.myData.investigations,
investigations : this.props.data.investigations,
error : null,
username : this.props.user.username,
sessionId : this.props.user.sessionId,
......@@ -28,14 +22,15 @@ class MyDataContainer extends Component {
}
render() {
return (<InvestigationTable data={this.props.myData}></InvestigationTable>);
return (<InvestigationTable data={this.props.data}></InvestigationTable>);
}
}
function mapStateToProps(state) {
return {
user: state.user,
myData : state.myData
data : state.data
};
}
......@@ -48,4 +43,4 @@ function mapDispatchToProps(dispatch) {
export default connect(
mapStateToProps,
mapDispatchToProps
)(MyDataContainer);
\ No newline at end of file
)(InvestigationsContainer);
\ No newline at end of file
import {
FECTH_INVESTIGATIONS, FECTH_INVESTIGATIONS_FULFILLED, FECTH_INVESTIGATIONS_PENDING
FECTH_INVESTIGATIONS,
FECTH_INVESTIGATIONS_FULFILLED,
FECTH_INVESTIGATIONS_PENDING,
FECTH_DATASETS_BY_INVESTIGATION_ID_FULFILLED
} from '../constants/ActionTypes'
const initialState = {"fetching": false, "fetched": false, "investigations":[], "error": null}
const initialState = {"fetching": false, "fetched": false, "investigations":[], "error": null, datasets : []}
const myData = (state = initialState, action) => {
const data = (state = initialState, action) => {
switch (action.type) {
case FECTH_INVESTIGATIONS_PENDING:{
......@@ -20,10 +23,44 @@ const myData = (state = initialState, action) => {
state = {...state, error: action.payload.message, fetched: false, fetching: false}
break;
}
case FECTH_DATASETS_BY_INVESTIGATION_ID_FULFILLED:{
/** this groups by column 0 that correspond to investigationId */
let datasets = parametersToDatasetObject(groupBy(action.payload.data, 0));
state = {...state, datasets: datasets, fetched: true, fetching: false}
break;
}
default:
break;
}
return state
}
export default myData
export default data
var groupBy = function(xs, key) {
return xs.reduce(function(rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
var parametersToDatasetObject = function(parametersGroupedByInvestigationId){
let datasets = {};
for (var datasetId in parametersGroupedByInvestigationId){
for (var index in parametersGroupedByInvestigationId[datasetId]){
var param = parametersGroupedByInvestigationId[datasetId][index];
var key = param[5];
var value = param[6];
if (datasets[datasetId] == null){
datasets[datasetId] = {};
}
datasets[datasetId][key] = value;
}
}
var array = [];
for (var ds in datasets){
array.push(datasets[ds]);
}
return array;
};
import { combineReducers } from 'redux'
import user from './login'
import myData from './myData.js'
import data from './data.js'
import doi from './doi.js'
export default combineReducers({
user,
myData,
data,
doi
})
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment