- NodeJS & NPM (download the most recent version for your OS at https://nodejs.org/en/download/)
- Postgres (download the most recent version for your OS at https://www.postgresql.org/download/)
- (OPTIONAL mac/linux only) Redis (download the most recent version for your OS at https://redis.io/download) ** Follow the instructions at (http://rejson.io/#building-and-loading-the-module) to set up the json module for redis
- Run the command '
createdb faers' in your command line interface - Download our DB dump file from the Google Drive called latest.sql
- Run the command '
psql faers < latest.sql' to import data into the database (this may take some time) - Connect to the database using
sudo -u mevuser psql faers.
- run the command '
pg_dump -h localhost -U mevuser -W -d faers > latest.sql' in the postgres command line interface
- Navigate to outermost folder in the repository on your computer
- Run
npm install - Navigate to front-end folder
- Run
npm install - Navigate to back-end folder
- Run
npm install - Navigate to outermost
mev-mqpfolder - Run
npm startto start the application
- SSH into the
mev.wpi.edumachine and navigate to/MEV/back-end - It is recommended to run the server in a linux screen session
- If needed, create a new screen by typing
screen - Use
screen -listto find the name of the screen - Re-attach to a screen with
screen -rname - Detach from a screen by pressing
ctrl + afollowed byd
- If needed, create a new screen by typing
- If the node server is running you can stop it with
ctrl-c - Start the server by running
npm run startBoth. This starts the node server and the redis cache - Detach from the screen by pressing
ctrl + afollowed byd
- If you need to restart the database for any reason, type the command
sudo systemctl restart postgresql.service
- SSH into the
mev.wpi.edumachine and stop the node server withctrl-c - Inside of the
mev-mqpfolder run thedeploy.shfile as 'sh deploy.shusername' where username is your user account name on themev.wpi.edumachine.- This will build the React app into a single build folder -this should not be pushed to GitHub-
- Copy this folder and the app.js to your user on the remote server
- Delete the previous build folder and app.js on the remote server
- Move the files from your user to the
/MEV/back-enddirectory
- This will require you to enter your
mev.wpi.edusystem account a few times. - SSH into the
mev.wpi.edumachine to start the node server again.
|-back-end
|-front-end
|____public
|____src
| |____resources
| |____components
| | |____visualization
| | | |____components
| | | | |____demographics
| | | | | |____components
| | | | | | |____components
| | | | |____timeline
| | | | | |____components
| | | | | | |____components
| | | | |____treeMap
| | | | | |____components
| | |____portal
| | | |____images
| | | |____userComponents
| | |____cases
| | |____components
| | |____editor
| | | |____images
| | |____reports
| | | |____components
| |____actions
| |____reducers
To make changes to the UI and other aspects of the front end, you must edit assets found in the front-end folder.
When editing the global state that Redux manages, you need to make changes in several different locations.
All asynchronous http requests are done from the /actions folder. This is where we talk to the back-end server to get data from our database. These async calls are created using the fetch() function which returns a Promise. Please read more about javascript promises here.
When adding to the global state you must create a function inside of a file in the /actions folder, add a case inside of a file in the /reducers folder and add an import to the file you are adding to the Redux state from.
In the timelineActions.js file we have:
export const setTimelineMinimizedToggle = toggle =>
dispatch => dispatch({ type: 'TOGGLE_TIMELINE_MINIMIZED', timelineMinimized: toggle });In this example we are adding a value toggle, labeled as timelineMinimized with a type TOGGLE_TIMELINE_MINIMIZED this is just a string to know what to listen for in the Reducer. This dispatch() function is what we need to call in order to have this information be sent to the reducers and be added to the global state.
In the timelineReducer.js file we have:
export default (state = initialTimelineState, action = {}) => {
switch (action.type) {
case 'SET_ENTIRE_TIMELINE':
return Object.assign({}, state, { entireTimelineData: action.entireTimelineData });
case 'TOGGLE_TIMELINE_MINIMIZED':
return Object.assign({}, state, { timelineMinimized: action.timelineMinimized });
default: return state;
}
};We are listening for the same TOGGLE_TIMELINE_MINIMIZED type, when we find that type we are setting the state to have the timelineMinimized value passed from the actions.
In the Timeline.jsx file we use this action like such:
We import the functions from the actions.
import { setTimelineMinimizedToggle, getEntireTimeline, setSelectedDate } from '../../../../actions/timelineActions';We then connect these actions to the current component as props.
export default connect(
mapStateToProps, // Used for reading the Global state
{ setTimelineMinimizedToggle, getEntireTimeline, setSelectedDate },
)(withStyles(styles)(Timeline));We add these functions to the proptypes of our component.
static propTypes = {
getEntireTimeline: PropTypes.func.isRequired,
setSelectedDate: PropTypes.func.isRequired,
setTimelineMinimizedToggle: PropTypes.func.isRequired,
}We can call these functions from our props like this.
this.props.setTimelineMinimizedToggle(true);To get from the global state, we need to check to make sure the information is in the state properly and then import and connect it into our component.
First make sure we have the proper case clause for the information we are looking for. From the example above, we are looking for TOGGLE_TIMELINE_MINIMIZED inside of the timelineReducer.js.
export default (state = initialTimelineState, action = {}) => {
switch (action.type) {
case 'SET_ENTIRE_TIMELINE':
return Object.assign({}, state, { entireTimelineData: action.entireTimelineData });
case 'TOGGLE_TIMELINE_MINIMIZED': // Here it is
return Object.assign({}, state, { timelineMinimized: action.timelineMinimized });
default: return state;
}
};We also need to make sure we have imported the timelineReducer into the index reducer inside of the /reducers/index.js file.
import demographic from './demographicReducer';
import filters from './filterReducer';
import multiSelectFilters from './multiSelectFilterReducer';
import user from './userReducer';
import mainVisualization from './visualizationReducer';
import timeline from './timelineReducer';
/**
* Redux Reducer that combines all of the other reducers to build the Redux State
*/
export default {
demographic,
filters,
multiSelectFilters,
mainVisualization,
timeline, // Exported as 'timeline'
user,
};We can see we import the timelineReducer and export it with the name timeline. This name is important.
Now we can go to our component Timeline.jsx and import the global state.
const mapStateToProps = state => ({
entireTimelineData: state.timeline.entireTimelineData,
demographicSexData: state.demographic.sex,
});
export default connect(
mapStateToProps,
{ setTimelineMinimizedToggle, getEntireTimeline, setSelectedDate },
)(withStyles(styles)(Timeline));Here in the mapStateToProps function we are getting from the global state with:
entireTimelineData: state.timeline.entireTimelineData,It is state.timeline since that is what we exported as inside of the reducers/index.js file. And in this case we are getting the entireTimelineData from above:
case 'SET_ENTIRE_TIMELINE':
return Object.assign({}, state, { entireTimelineData: action.entireTimelineData });Inside of our component we need to add this to the proptypes.
static propTypes = {
entireTimelineData: PropTypes.arrayOf(
PropTypes.shape({
init_fda_dt: PropTypes.string.isRequired,
serious: PropTypes.number.isRequired,
not_serious: PropTypes.number.isRequired,
}),
).isRequired,
}This data can now be accessed from the props as such:
this.props.entireTimelineDataAssets for the visualization page exist within the visualization folder src/components/visualization.
visualization/App.js uses the components for the treemaps, demographics, and timeline which assets are each in their respective folder inside visualization/components.
Assets for the reports listing page exist within the reports folder src/components/reports.
reports/ReportsList.jsx uses the components for the report listing grid which assets are all contained in the folder reports/components.
All assets for the narrative editor page exist within the editor folder src/components/editor
All assets for these pages exist within the portal folder src/components/portal
Assets for the dashboard page exist inside the portal/userComponents folder.
Our backend is a one file server that receives requests sent from the front end located at back-end/App.js
We use the express library (https://expressjs.com/) for our backend and documentation for use can be found at https://expressjs.com/en/4x/api.html#app.