- Created by Tom Son, last modified on Aug 01, 2022
As of 19/07/2022
How to install Node on Ubuntu 20.04
Use Option 3 — Installing Node Using the Node Version Manager
The beauty of this option is letting you choose different node versions for different projects
GMHazard was originally started with Node V12.18.2 & React 17
Frontend directory structure:
- /apis - The directory contains JS files to deal with APIs(Both Core and Project via Intermediate)
- We only have ProjectAPI. Make sure to have CoreAPI.js when the time comes
- /assets - Any assets
- /documents - We have some dummy documents which are used to render the Markdown on the Framework Documents tab
- /style - Any relevant CSS files are located here. Ideally, each CSS will be used uniquely for a component/view(Which is not the current setup, some overlapping is happening, but nothing major for now, as I believe the design will continue to be changed.)
- /components - React component
- /common - Components that are meant to be at the atomic level or reusable(can be used in more than one component/view)
- /Hazard - Components that are related to non-projects tab
- /Project - Components that are related to projects tab
- /NavBar - Components for the Navigation Bar
- /PermissionConfig - Components for Admins to modify/allocate projects permission to users
- /constants - Contain a single JS file that has some CONSTANT variables
- /context - React Context - Treat as global variables. By having this, you do not need to worry about passing data between components that are not directly connected
- /utils - Nothing to do with rendering, more of logic and simple setup(InitFontAwesome, History)
- /views - A set of components from /components. Each view is used to render a page on a dedicated URL(Except the footer)
- Project.js is there to render a page at `https://quakecoresoft.canterbury.ac.nz/gmhazard/project`
|-- "/apis" |-- "/assets" | |-- "/documents" | `-- "/style" |-- "/components" | |-- "/common" | | |-- "/Disaggregation" | | |-- "/GMS" | | |-- "/HazardCurve" | | |-- "/PermissionConfig" | | |-- "/Scenarios" | | |-- "/SiteSelection" | | `-- "/UHS" | |-- "/Hazard" | | |-- "/GMS" | | |-- "/Scenarios" | | |-- "/SeismicHazard" | | `-- "/SiteSelection" | |-- "/NavBar" | |-- "/PermissionConfig" | `-- "/Project" | |-- "/GMS" | |-- "/Scenarios" | |-- "/SeismicHazard" | `-- "/SiteSelection" |-- "/constants" | `-- "Constants.js" |-- "/context" | |-- "GlobalContext.js" | `-- "index.js" |-- "/utils" | |-- "/calculations" | |-- "History.js" | |-- "InitFontAwesome.js" | `-- "Utils.js" `-- "/views" |-- "Footer.js" |-- "FrameworkDocView.js" |-- "Hazard.js" |-- "Home.js" |-- "index.js" |-- "PermissionConfig.js" |-- "Profile.js" |-- "ProjectCreate.js" `-- "Project.js"
|-- "apis" | `-- "ProjectAPI.js" |-- "App.js" |-- "App.test.js" |-- "assets" | |-- "documents" | | |-- "15_Projects-Seismic-Hazard.md" | | |-- "1_Hazard-Site-Selection.md" | | |-- "2_Hazard-Seismic-Hazard.md" | | |-- "3_Hazard-GMS.md" | | |-- "4_Projects-Site-Selection.md" | | `-- "99_Projects-GMS.md" | |-- "icon-question.png" | |-- "info-icon.png" | |-- "loading.svg" | |-- "logo.svg" | `-- "style" | |-- "AdminPage.css" | |-- "App.css" | |-- "CreateProject.css" | |-- "Footer.css" | |-- "GMSForm.css" | |-- "GMSPlot.css" | |-- "GMSViewer.css" | |-- "GuideTooltip.css" | |-- "HazardForms.css" | |-- "HazardPlots.css" | |-- "ImageMap.css" | |-- "index.css" | |-- "Messages.css" | |-- "MetadataBox.css" | |-- "Modal.css" | |-- "NavBar.css" | |-- "NZCodeSection.css" | |-- "PermissionDashboard.css" | |-- "ScenarioPlot.css" | |-- "ScenarioViewer.css" | |-- "SingleColumnView.css" | |-- "SiteSelectionMap.css" | |-- "Spinner.css" | |-- "TwoColumnView.css" | `-- "UHSPlot.css" |-- "components" | |-- "common" | | |-- "CustomSelect.js" | | |-- "Disaggregation" | | | `-- "ContributionTable.js" | | |-- "DownloadButton.js" | | |-- "ErrorMessage.js" | | |-- "GMS" | | | |-- "GMSAvailableGMPlot.js" | | | |-- "GMSCausalParamPlot.js" | | | |-- "GMSDisaggDistributionPlot.js" | | | |-- "GMSIMDistributionsPlot.js" | | | |-- "GMSMwRrupPlot.js" | | | `-- "GMSSpectraPlot.js" | | |-- "GuideMessage.js" | | |-- "GuideTooltip.js" | | |-- "HazardCurve" | | | |-- "HazardBranchPlot.js" | | | `-- "HazardEnsemblePlot.js" | | |-- "IMCustomSelect.js" | | |-- "index.js" | | |-- "Loading.js" | | |-- "LoadingSpinner.js" | | |-- "MetadataBox.js" | | |-- "ModalComponent.js" | | |-- "PermissionConfig" | | | `-- "PermissionDashboard.js" | | |-- "ReactAuth0SPA.js" | | |-- "Scenarios" | | | |-- "MetadataTable.js" | | | `-- "ScenarioPlot.js" | | |-- "SingleColumnView.js" | | |-- "SiteSelection" | | | `-- "ImageMap.js" | | |-- "TwoColumnView.js" | | `-- "UHS" | | |-- "UHSBranchPlot.js" | | `-- "UHSPlot.js" | |-- "Hazard" | | |-- "GMS" | | | |-- "GMSForm.js" | | | |-- "GMSViewer.js" | | | `-- "index.js" | | |-- "Scenarios" | | | |-- "index.js" | | | |-- "ScenarioForm.js" | | | `-- "ScenarioViewer.js" | | |-- "SeismicHazard" | | | |-- "DisaggregationSection.js" | | | |-- "HazardCurveSection.js" | | | |-- "HazardForm.js" | | | |-- "HazardViewerDisaggregation.js" | | | |-- "HazardViewerHazardCurve.js" | | | |-- "HazardViewer.js" | | | |-- "HazardViewerUHS.js" | | | |-- "index.js" | | | |-- "NZS1170p5Section.js" | | | |-- "NZTASection.js" | | | `-- "UHSSection.js" | | `-- "SiteSelection" | | |-- "EnsembleSelect.js" | | |-- "index.js" | | |-- "SiteSelectionBasinDepth.js" | | |-- "SiteSelectionForm.js" | | |-- "SiteSelectionMap.js" | | |-- "SiteSelectionMapPin.js" | | |-- "SiteSelectionRegional.js" | | |-- "SiteSelectionViewer.js" | | |-- "SiteSelectionVS30.js" | | `-- "SiteSelectionVS30SiteConditions.js" | |-- "NavBar" | | |-- "index.js" | | |-- "LoginButton.js" | | |-- "LogoutButton.js" | | `-- "NavBar.js" | |-- "PermissionConfig" | | |-- "EditUserPermission.js" | | |-- "index.js" | | |-- "PagePermissionDashboard.js" | | `-- "ProjectPermissionDashboard.js" | |-- "PrivateRoute.js" | `-- "Project" | |-- "GMS" | | |-- "GMSForm.js" | | |-- "GMSViewer.js" | | `-- "index.js" | |-- "Scenarios" | | |-- "index.js" | | |-- "ScenarioForm.js" | | `-- "ScenarioViewer.js" | |-- "SeismicHazard" | | |-- "DisaggregationSection.js" | | |-- "HazardCurveSection.js" | | |-- "HazardForm.js" | | |-- "HazardViewerDisaggregation.js" | | |-- "HazardViewerHazardCurve.js" | | |-- "HazardViewer.js" | | |-- "HazardViewerUHS.js" | | |-- "index.js" | | `-- "UHSSection.js" | `-- "SiteSelection" | |-- "index.js" | |-- "SiteSelectionForm.js" | `-- "SiteSelectionViewer.js" |-- "constants" | `-- "Constants.js" |-- "context" | |-- "GlobalContext.js" | `-- "index.js" |-- "index.js" |-- "utils" | |-- "calculations" | | `-- "CalculateGMSSpectra.js" | |-- "History.js" | |-- "InitFontAwesome.js" | `-- "Utils.js" `-- "views" |-- "Footer.js" |-- "FrameworkDocView.js" |-- "Hazard.js" |-- "Home.js" |-- "index.js" |-- "PermissionConfig.js" |-- "Profile.js" |-- "ProjectCreate.js" `-- "Project.js"
Import order
- React always comes first, then an empty line
- Any third-party packages then an empty line
- Context and Constants then an empty line
- Internally used components and utils
- import CSS
Component structure
- useContext hook at the very top
- useState hooks
- useEffect hooks
- Functions
- JSX
// React import React, { useState, useContext, useEffect, Fragment } from "react"; // Third-party packages import Select from "react-select"; import { v4 as uuidv4 } from "uuid"; import makeAnimated from "react-select/animated"; // Context and Constants import { GlobalContext } from "context"; import * as CONSTANTS from "constants/Constants"; // Internally made components and utils import { GuideTooltip } from "components/common"; import { createAnnualExceedanceArray, isPSANotInIMList } from "utils/Utils"; // Import CSS import "assets/style/HazardForms.css"; // Actual components const UHSSection = () => { // useContext const { projectIMs, projectUHSRPs, setProjectUHSGetClick, setProjectSelectedUHSRP, projectSiteSelectionGetClick, } = useContext(GlobalContext); const animatedComponents = makeAnimated(); // useState hooks const [localRPs, setLocalRPs] = useState([]); const [rpOptions, setRPOptions] = useState([]); // useEffect hooks // Reset local variable to empty array when global changed to empty array (Reset) useEffect(() => { setLocalRPs([]); if (projectUHSRPs.length !== 0) { setRPOptions(createAnnualExceedanceArray(projectUHSRPs)); } else { setRPOptions([]); } }, [projectSiteSelectionGetClick]); // Functions const getUHS = () => { setProjectSelectedUHSRP(localRPs); setProjectUHSGetClick(uuidv4()); }; // JSX return ( <Fragment> <form autoComplete="off" onSubmit={(e) => e.preventDefault()}> <div className="form-group form-section-title"> {CONSTANTS.UNIFORM_HAZARD_SPECTRUM} <GuideTooltip explanation={CONSTANTS.TOOLTIP_MESSAGES["PROJECT_UHS"]} /> </div> <div className="form-group"> <label id="label-uhs-return-period" htmlFor="uhs-return-period" className="control-label" > {CONSTANTS.ANNUAL_EXCEEDANCE_RATE} (years<sup>-1</sup>) </label> <Select id="uhs-return-period" closeMenuOnSelect={false} components={animatedComponents} isMulti placeholder={ rpOptions.length === 0 ? `${CONSTANTS.PLACEHOLDER_NOT_AVAILABLE}` : `${CONSTANTS.PLACEHOLDER_SELECT_SIGN}` } value={localRPs.length === 0 ? [] : localRPs} onChange={(value) => setLocalRPs(value || [])} options={rpOptions} isDisabled={rpOptions.length === 0 || isPSANotInIMList(projectIMs)} menuPlacement="auto" menuPortalTarget={document.body} /> </div> </form> <div className="form-group"> <button id="uhs-update-plot" type="button" className="btn btn-primary mt-2" disabled={localRPs.length === 0 || isPSANotInIMList(projectIMs)} onClick={() => getUHS()} > {CONSTANTS.GET_BUTTON} </button> </div> </Fragment> ); }; export default UHSSection;
All GMHazard APIs are in a package form
- /api - Intmediate API is here as a proxy, so it forwards requests from the frontend to Core and/or Project API
- `intermediate_api.py` is mainly communicating with our DB and Auth0
- /logs - log events daily basis
|-- "CHANGELOG.md" |-- "Dockerfile" |-- "/intermediate_api" | |-- "/api" | | |-- "core_api.py" | | |-- "__init__.py" | | |-- "intermediate_api.py" | | `-- "project_api.py" | |-- "app.py" | |-- "auth0.py" | |-- "constants.py" | |-- "create_db.py" | |-- "custom_log_handler.py" | |-- "db.py" | |-- "decorators.py" | |-- "__init__.py" | |-- "/logs" | | |-- "logfile.log" | |-- "models.py" | `-- "utils.py" |-- "README.md" |-- "requirements.txt" `-- "setup.py"
Main features of the current database - More information
- Track users action
- Check whether a user has access to a certain project
DB workflow with projects
When a user access/projects, the app sends a request to the Project API via Intermediate API and the Intermediate API checks the projects between DB and Project API.
And only returns the projects to users that are matching(after the cross-check between DB and Project API)
For instance, with the Public access level:
- DB has projects A, B, C and E
- Projects API has projects A, B, C, D and E
- Then user only gets A, B, C and E as available projects. Hence, make sure to add project D to the DB
For instance, with the Private access level:
- DB(project table) has projects A, B, and C and all have access level Private
- DB(users_project) has projects A and B
- Project API has projects A, B, and C
- Then user only gets A and B as available projects because this user only has access to A and B but C
(When the time comes for Private projects or adding more public/private projects, it might be a good idea to implement an interface to add projects to the DB)
The way of adding projects to the DB
- Make sure you are on 1p where the MariaDB container is running
- Run the following command,
docker ps
to find a container's ID where the image label says mariadb - Run the following command to find the container's IPAddress,
docker inspect {replace me with container id} | grep "IPAd".
This will provide an IPAddress for the mariadb container - Access the container with the following command,
For the -u argument, it's for a username and needs a space between -u and username, whereas -p is for password and does not need a space between -p and passwordmysql -h {replace me with the ip address} -P 3306 -u {replace me with userid for DB} -p{replace me with password for DB}
- Then you are now in the MariaDB. Use some SQL skills to add projects and/or users_projects(if it is for the private access level project)
We have three different components to run GMHazard
Core and Project APIs are currently running on mantle.
These are all running as a service with systemd.
Scripts are already written, so it's quite simple to deploy the latest version
There are four directories under /home/seistech/apis
- core_api_dev
- core_api_ea
- project_api_dev
- project_api_ea
The current GMHazard web app, quakecoresoft.canterbury.ac.nz/gmhazard is based on the DEV APIs
To update the API to the latest version - Walkthrough with project_api_dev
- Ensure all the repositories inside the directory(In this case, project_api_dev) are updated with the master/main branch. - there are four, gmhazard, im_calculation, pro-processing and qcore
- Because all of those are packages, make sure to install it again if the version has changed(e.g., pip install -e apis/project_api)
Use the following command to start/stop/check the status of the API service
sudo service project_api_dev start/stop/status
in the best practice, make sure to check the status first, then stop, then status, then start.
The intermediate API and DB are running on 1p.
These are currently implemented with Docker
More information can be found here
The current GMHazard web app, quakecoresoft.canterbury.ac.nz/gmhazard is based on the develop branch
To update the intermediate API and database to the latest version - Walkthrough with develop version
- Make sure to be in the directory, gmhazard/tools/deployment/docker/develop
Run the following command
../Dockerise.sh develop
- Drop/remove any existing Docker containers - related to develop
- Change the branch to develop branch and pull to make sure its in the latest version
- Create a Docker container with the build date and git latest commit hash
- running the Intermediate API and DB
The frontend is also running on 1p
We deliver the frontend with the build version
Using the following command to build
npm run build
which will create another directory called /build
Then, we deploy it with NGINX
sudo cp -r ./build/ /var/www/gmhazard/
Finally, we should be able to access the GMHazard web app via link
- No labels