diff --git a/README.md b/README.md
deleted file mode 100644
index c503e55dd..000000000
--- a/README.md
+++ /dev/null
@@ -1,72 +0,0 @@
-# Help
-
-## Introduction
-DomainViz allows users to identify and visualize protein domains on one or more protein sequences using multiple domain prediction platforms (Pfam and Prosite) to understand the consensus positionality and abundance of different domains within protein groups and families. While Pfam and Prosite each individually provide domain information for a single sequence, searching multiple proteins is not possible, requiring extensive manual curation. DomainViz automates the simultaneous identification of domains from multiple proteins and provides both consolidated and consensus outputs for further analysis and publication. This is highlighted by the production of a ‘consensus’ histogram plot that depicts the distribution and positionality of domains within a representative median protein sequence derived from the input protein sequences.
-
-### General Operations
-File Upload: DomainViz accepts a single multiple sequence fasta file. The file extension should be .FA or .FASTA and the file size should be less than 10 Mb. Each sequence present within the fasta file should be formatted as follows:
-
->AT3G19040.1|PACid:19663337_AtTAFIIb
-MICRVDYGSNDEEYDGPELQVVTEEDHLLPKREYLSSEIAESRR
-YREVIRSEREEEKRRKAKQKKKLQRGILENYPPRRNDGISSESGQNINSLCVSDFERNRTEYAPQPKRRKKGQVGLANILESIVDTLRVKEVNVSYLFLK
-PVTKKEAPNYLEIVKCPMDLSTIRDKVRRMEYRDRQQFRHDVWQIKFNAHLYNDGRNLSIPPLADELLVKCDRLLDEYRDELKEAEKGIVDSSDSLR*
-
-The chevron denotes the header line containing unique identifying information, which is ended by a line return to denote this as a separate entity from the sequence. At the end of each sequence should be a (*) to ensure the end of the sequence is denoted. Sequences of 16 amino acids or less, and greater than 10005000 amino acids are not compatible with DomainViz.
-
-#### NOTE: Ensure that headers and sequences are not duplicated in the file and that standard amino acid code is used. Accepted characters are GALMFWKQESPVICYHRNDTX, in upper or lower case.
-
-### Submit Task and Analysis:
-Once a file is successfully uploaded, use the Submit Task button to start the analysis. This will redirect to a new results page with an unique identifying 32 digit Result ID. Please copy and save the Result ID. You can use this ID to retrieve results for a period of 7 days post analysis. Please note that some searches can take a long time, from several minutes to hours and you can check the status of your analysis using your Result ID.
-#### Example Result ID: 5ff3eeda_5842_5879_d627_f4d1faea3c84
-
-Without a Result ID , the data will not be retrievable and the analysis will need to be re-run. If an error occurs, please follow the directions in the pop-up box that results.
-
-### Data Retrieval:
-To retrieve a previous analysis, enter the Result ID into the “Result ID” text box on the DomainViz homepage . Analyses are retained for a total of 7 days prior to automatic deletion.
-
-### Load Example:
-This option will automatically load a .fa or .fasta file to produce an example analysis visualization as well as an example downloadable output.
-
-## Settings
-### Absolute Results:
-Absolute results means that we will plot absolute numbers on y axis of plots instead of relative ones. If the box is unchecked, we plot relative results, if it is checked, we plot absolute results.
-
-### Minimum Domain Prevalence:
-Only domains occuring in a ratio higher than the number are plotted will be present in the visualized and compiled output (e.g. If the value is 0.5, the domain has to occur somewhere in the protein of at least 50% of sequences).
-
-### Minimum domain position conservation:
-Only domains occuring at a ratio higher than this number at a specific place in the protein group will be presented in the output (e.g. if value is 0.5, 50% of the sequences have to have this domain at the same relative position).
-
-### Scale:
-The number input here represents the number of inches per 100 amino acids that the plot will use to display each visualization.
-
-
-## Data Interpretation
-
-The histograms depict both the position and prevalence of eachspecific protein domains (identified from the Pfam and Prosite databases) within the group of proteins input by the user.
-
-Generally, results with non-overlapping domains can be directly interpreted as showing the prevalence and position of each domain with different degrees of conservation within the protein group.
-
-If the visualization of a group of proteins shows two overlapping domains (e.g. A and B), at for example each with 50% 0.5 prevalence, then there are three possible explanations:ways how the figure could have been compiled in this way.
-
-#### Case 1: A subset of proteins have no domain predicted, while the remaining subset of proteins have both domains A and B predicted at the same spot. (In this case, the subsets are each half of the total set of proteins).
-
-#### Case 2: A subset of proteins have domain A predicted, and the possess an other subset of proteins have domain B predicted. (In this case, the subsets are each half of the total set of proteins.
-
-#### Case 3: Any combination of Case 1 and Case 2 leading to the same prevalence ratios. Only further investigation of the results available in the download option can discriminate the case.
-
-### Downloadable DomainViz Outputs
-The DomainViz output is available for download as a .zip file containing multiple files types. This includes: Job.id.tsv files containing the raw output data from Pfam and Prosite. Job.id_ProteinGroup.csv allows users to replot the data used in creating each visualized histogram. Lastly, Job.id_ProteinGroup.pdf files that are high resolution vector files that can be used for further manipulation and/or publication.
-
-## FAQ
-1. Q. How do I retrieve my results after navigating away from the processing results?
- A. Ensure that you retain the 32-character unique identifier result ID displayed on the Results page. EnterPlace this code into the Result ID field on the homemain page and hit “Go to My Results”. Alternatively, bookmark the Results page and revisit it after some time.
-
-2. Q. I have been warned that I have duplicate sequences in my FASTA file?
- A. DomainViz will still process your file under the assumption that duplicate sequences refer to independent proteins that share the same sequence. We advise checking that spurious duplicate sequences were not accidentally introduced during input file creation as they can lead to altered results. run; however, it is recommended that these be removed before proceeding further.
-
-3. Q. What is the maximal number of protein sequences that can be analyzed at once?
- A. DomainViz can currently assess 1000 protein sequences at once. NOTE: The more sequences in a single query the longer the analysis will take.
-
-4. Q. My fasta isn't uploading, it says there is a problem with one of my lines? How do I fix this?
- A. Please remove the lines that are causing issues.
\ No newline at end of file
diff --git a/website-glen/README.md b/website-glen/README_REACT.md
similarity index 100%
rename from website-glen/README.md
rename to website-glen/README_REACT.md
diff --git a/website-glen/deployment/README.md b/website-glen/deployment/README.md
new file mode 100644
index 000000000..1c1eb17a2
--- /dev/null
+++ b/website-glen/deployment/README.md
@@ -0,0 +1,143 @@
+# Intro to the site
+Hello! If you are reading this, you are probably trying to deploy, redeploy, or fix the Uhrig Lab DomainViz website. Thanks!
+
+The way this site is organized is rather standard, however, I (Cameron Ridderikhoff) haven't fully separated the REST API/backend from the frontend.
+
+The frontend is contained mainly within the `src` folder, but there are some important files contained within the `public` folder as well, namely `index.html`, `example_iframe.html` and `favicon.ico`. You can find these folders at the path: `Glen Website/website-glen/src` and `Glen Website/website-glen/public` respectively.
+
+The api is contained in a folder aptly named: `api`. You can find this in the path: `Glen Website/website-glen/api`, however, this folder is mainly for Flask environment variables, and for Flask's wsgi.py file, which is the entrypoint for the backend's service. This will be further explained in *Section 2 - Backend Deployment*. You can find the Python files that contain the API endpoints in the `api/api` subfolder. Feel free to rename this if you like, but you will have to change the service files.
+
+
+# Section 1 - Frontend Deployment
+## Section 1.1 Redeploy - Frontend
+The frontend deployment is quite easy, at least if you are redeploying the site, and not making too many major changes. There is a file within this folder `Glen Website/website-glen/deployment/redeploy.sh` that will automatically redeploy both the frontend and backend. However, this shell file isn't ideal, and could certainly use some updating (There is a TODO in the file if you want some more work).
+
+This file needs to be run with `sudo`, so the full commands from logging onto the server should be:
+### 1.1.1 IMPORTANT
+```bash
+$cd Website_Glen/website-glen/deployment
+$sudo ./redeploy.sh
+```
+
+You may have to deal with npm (it has certainly been a struggle for me), and I find the best way to fix issues I am having with npm is to delete the `node_modules` folder and `package-lock.json` files, and then run `redeploy.sh`, which runs `npm install` to refresh your packages. This usually fixes enough npm package conflicts to allow the `redeploy.sh` to run to completition.
+
+## Section 1.2 Fresh Deploy - Frontend
+This guide only works for Linux distributions. If you have another OS that you are trying to serve this site from, you can loosely follow these instructions, but many may not work, or may have incorrect file paths.
+
+If you run in the situation that Glen, or others, ask you to deploy the site onto a fresh Cirrus instance, or other cloud infrastructure (Azure/AWS/GCP, etc...) then you will need to do a bit more work. I have tried to list all of the steps below, but I may miss something, so stay sharp! (Note: you will also have to install the backend - see *Section 2.2 Fresh Deploy - Backend*)
+1. Clone the repo
+2. Install node and npm
+3. Run `sudo npm install` in the `Website_Glen/website_glen` directory to ensure npm has correctly installed.
+4. Create a SSL certificate using [certbot](https://certbot.eff.org/) NOTE: We are running `Nginx` on whatever Ubuntu/Linux distro you are using.
+5. Ensure that the file paths of the certificate match those of the frontend service file (`Website_Glen/website-glen/deployment/prodoplot.nginx`):
+```bash
+$ssl_certificate /etc/letsencrypt/live/uhriglabdev.biology.ualberta.ca/fullchain.pem;
+$ssl_certificate_key /etc/letsencrypt/live/uhriglabdev.biology.ualberta.ca/privkey.pem;
+```
+If they are not, either change the filepaths, or lines 8 and 9 of the frontend service file.
+6. Copy the frontend service file into the `/etc/nginx/sites-available/` folder
+```bash
+$sudo cp Website_Glen/website-glen/deployment/prodoplot.nginx /etc/nginx/sites-available/prodoplot.nginx
+```
+7. Create a soft-link from the sites-available folder to sites-enabled:
+```bash
+$sudo ln /etc/nginx/sites-available/prodoplot.nginx /etc/nginx/sites-enabled/prodoplot.nginx
+```
+8. Reload Nginx:
+```bash
+$sudo systemctl reload nginx
+```
+
+With all of this done, you should have a working frontend, that you can view in your web browser.
+
+**RESOURCES**
+Here are some of the videos/blogs that I used to learn how to deploy an Nginx site:
+1. [How to Deploy a React + Flask App](https://blog.miguelgrinberg.com/post/how-to-deploy-a-react--flask-project)
+2. [How to run Flask App on HTTPS](https://blog.miguelgrinberg.com/post/running-your-flask-application-over-https)
+
+
+# Section 2 - Backend Deployment
+The backend is slightly more complex, but certainly manageable. It is quite easy to redeploy, but rather difficult to deploy fresh.
+
+## Section 2.1 Redeploy - Backend
+The backend deployment is quite easy, at least if you are redeploying the site, and not making too many major changes, much like the frontend. There is a file within this folder `Glen Website/website-glen/deployment/redeploy.sh` that will automatically redeploy both the frontend and backend. (See *Section 1.1 Redeploy - Frontend* for more details)
+
+This file needs to be run with `sudo`, so the full commands from logging onto the server should be:
+### 2.1.1 IMPORTANT
+```bash
+$cd Website_Glen/website-glen/deployment
+$sudo ./redeploy.sh
+```
+
+Luckily python is nicer than npm, so usually modules aren't as big of an issue. However, if you run into any issues, install all packages inside the virtual environment by traveling to the first api folder and activate the virtual environment and install packages:
+```bash
+$cd Website_Glen/website-glen/api/
+$source venv/bin/activate
+$pip3 install -r requirements.flask.txt
+```
+Then navigate to the second api folder and activate the virtual environment and install packages:
+```bash
+$cd Website_Glen/website-glen/api/api/
+$source propplotenv/bin/activate
+$pip3 install -r requirements.domainviz.txt
+```
+
+If you add more packages to the backend, add them to `requirements.flask.txt`.
+
+## Section 2.2 Fresh Deploy - Backend
+This guide only works for Linux distributions. If you have another OS that you are trying to serve this site from, you can loosely follow these instructions, but many may not work, or may have incorrect file paths.
+
+The same applies for backend as for frontend.
+1. Clone the repo
+2. Install python3.8 [Downloads Page](https://www.python.org/downloads/release/python-3811/)
+3. Install python3 virtual environment
+```bash
+$sudo python3 -m pip install --user virtualenv
+```
+4. Create a virtual environment for the backend in the `Website_Glen/website-glen/api/` directory:
+```bash
+$sudo python3 -m venv venv
+```
+5. Activate the virtual environment:
+```bash
+$source venv/bin/activate
+```
+6. Deactivate the environment:
+```bash
+$deactivate
+```
+7. Install packages:
+```bash
+$pip install -r requirements.flask.txt
+```
+8. Create a virtual environment for domainviz.py in the `Website_Glen/website-glen/api/api/` directory:
+```bash
+$sudo python3 -m venv protplotenv
+```
+9. Activate the virtual environment:
+```bash
+$source venv/bin/activate
+```
+10. Install packages:
+```bash
+$pip install -r requirements.domainviz.txt
+```
+11. Deactivate the environment:
+```bash
+$deactivate
+```
+12. Copy the backend service file from the `Website_Glen/website_glen/deployment` folder into the `/etc/systemd/system/` folder:
+```bash
+$sudo cp prodoplot.service /etc/systemd/system/prodoplot.service
+```
+13. Run the service:
+```bash
+$sudo systemctl daemon-reload
+$sudo systemctl start prodoplot.service
+```
+
+With all of this done, you should have a working backend, and you can check that it is running by running the command:
+```bash
+$sudo systemctl status prodoplot
+```
+It should say "active (running)" in green text, and have a green dot by the `prodoplot.service` name. Press `CTL+C` to exit.
\ No newline at end of file
diff --git a/website-glen/deployment/redeploy.sh b/website-glen/deployment/redeploy.sh
index 8361c15de..946f3b2a3 100755
--- a/website-glen/deployment/redeploy.sh
+++ b/website-glen/deployment/redeploy.sh
@@ -1,3 +1,7 @@
+# TODO: The IFS and following for loop should be able to display the information of the currently running jobs,
+# and then you should be able to kill those jobs before closing the service files with `systemctl restart prodoplot`.
+# The goal there is to be able to re-run the jobs using a direct python3 call to the domainviz.py file, thus not losing any
+# data for users upon redeployment.
#!/bin/sh
NUM_INSTANCES=$(ps -ef | grep "python" | egrep -v "grep|vi|more|pg" | wc -l)
if [ $NUM_INSTANCES -gt 0 ];
diff --git a/website-glen/public/index.html b/website-glen/public/index.html
index 49861dfbd..9c10c5899 100644
--- a/website-glen/public/index.html
+++ b/website-glen/public/index.html
@@ -14,6 +14,7 @@
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
+
diff --git a/website-glen/src/App.js b/website-glen/src/App.js
index 0dc53a906..bd9f43db0 100644
--- a/website-glen/src/App.js
+++ b/website-glen/src/App.js
@@ -2,7 +2,17 @@ import React from 'react';
import './App.css';
import 'fontsource-roboto';
import { makeStyles } from '@material-ui/core/styles';
-import { AppBar, Toolbar, Typography, Button, createMuiTheme, ThemeProvider, Divider, Box, Container } from '@material-ui/core';
+import {
+ AppBar,
+ Toolbar,
+ Typography,
+ Button,
+ createMuiTheme,
+ ThemeProvider,
+ Divider,
+ Box,
+ Container
+} from '@material-ui/core';
import {
BrowserRouter,
Switch,
@@ -19,8 +29,16 @@ import { Help } from './components/Help';
import { TermsOfUse } from './components/TermsOfUse';
import { PrivacyStatement } from './components/PrivacyStatement';
import DomainViz from './components/DomainViz';
+/* MotifX was the previous name of UMotif. When using this module, have a look at the UMotifMerge branch
+ * and attempt to merge that into the main codebase. https://github.com/UhrigLab/DomainViz/tree/UMotifMerge
+ * That way, that work is not lost.
+ */
// import MotifX from './components/MotifX';
+
+/* This makeStyles() is used to bring the Raleway font into the DomainViz site,
+ * as well as for certain typeographys used on the toolbar.
+ */
const useStyles = makeStyles((theme) => ({
app: {
minHeight: '85.8vh',
@@ -62,10 +80,10 @@ const useStyles = makeStyles((theme) => ({
const theme = createMuiTheme({
palette: {
primary: {
+ // This is grey as hex.
main: '#e8e8e8',
},
secondary: {
- // This is green.A700 as hex.
main: '#000000',
},
},
@@ -92,8 +110,13 @@ function App() {
Protein tools
- {/* Need this for spacing for now */}
+ {/* Need this for spacing - If you are better at frontend than me, please change it */}
+
+ {/* The Home button is currently disabled, as is the MotifX/UMotif button. This is so that
+ * users are automatically redirected to the DomainViz page, since there is only one tool enabled at the moment.
+ * When UMotif is added, you should enable the Home button, and un-comment the Home.js component to create a real home page.
+ */}
{/* */}
diff --git a/website-glen/src/README.md b/website-glen/src/README.md
new file mode 100644
index 000000000..dbaace40e
--- /dev/null
+++ b/website-glen/src/README.md
@@ -0,0 +1,23 @@
+# Cameron's Style Guide
+This guide is to be used to understand how I styled my ReactJS components. The short answer is "poorly". This was the first website I created, and as such, the styling leaves much to be desired.
+
+Each page is set up using the material-ui `` component. The Grid is setup in rows, and every `{12}` xs points is equal to one row with the spacing that I used.
+
+For example, `` is one row, or,
+```html
+
+
+
+
+```
+is also one single row.
+
+Sometimes there are empty `` objects, these are used to create empty space
+Every 12 xs points are separated from the next with a blank line.
+
+# Cameron's Component Guide
+All components are functional components. Look up a React functional components guide if you need help.
+
+For some reason, some of my components are set up as `function xyz() {}` and others as `const xyz = () => {}`. This is likely caused by me looking at different coding practices, and not realizing what the difference between the two are, or why one would choose either. This should be changed, for consistency.
+
+As well, some components use a tab spacing of 2 spaces, and others use a spacing of 4 spaces. I'm honestly not sure when this happened, but by the time I noticed it, it would have been too much work to rectify, so I left it as is.
\ No newline at end of file
diff --git a/website-glen/src/components/About.js b/website-glen/src/components/About.js
index 47b08175a..805bcc868 100644
--- a/website-glen/src/components/About.js
+++ b/website-glen/src/components/About.js
@@ -4,7 +4,6 @@ import { makeStyles } from '@material-ui/core/styles';
import { Link as RouterLink } from 'react-router-dom';
-
const useStyles = makeStyles((theme) => ({
paper: {
padding: theme.spacing(2),
@@ -21,7 +20,20 @@ const useStyles = makeStyles((theme) => ({
height: '580px',
},
}));
+
+
export const About = () => {
+ /* This functional component displays information about the site, and about our organization.
+ * A rather simple component, and very similar to Help.js, PrivacyStatement.js, and TermsOfUse.js.
+ *
+ * One complexity of this component is the use of RouterLink vs. Link. While both components are called Link in their respective
+ * libraries, I have renamed the react-router Link to , which is a component that allows the user to navigate around
+ * our site, whereas the component from material-ui is an external link to another site.
+ *
+ * Based on this Google Doc: https://docs.google.com/document/d/1bflUShOzD8q-_b0dgBLxk6FaLmpfTEnRkgwMIOGcaxQ/edit
+ *
+ * This component could be improved by using css, and changing from a Grid layout to a more flexible one.
+ */
const classes = useStyles()
return (
@@ -31,6 +43,7 @@ export const About = () => {
The
+ {/* Inline allows us to have a differently styled piece of text on the same line as text */}
Uhrig Lab
at the University of Alberta is a protein biochemistry and proteomics research group interested in protein function discovery. We are also currently developing multiple open-source, accessible, and easy to use protein sequence analysis tools to enable biological discovery.
@@ -49,4 +62,3 @@ export const About = () => {
);
}
-
diff --git a/website-glen/src/components/AccordionSetup.js b/website-glen/src/components/AccordionSetup.js
index 564e22b82..99091174a 100644
--- a/website-glen/src/components/AccordionSetup.js
+++ b/website-glen/src/components/AccordionSetup.js
@@ -6,6 +6,7 @@ import AccordionDetails from '@material-ui/core/AccordionDetails';
import Typography from '@material-ui/core/Typography';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+
const useStyles = makeStyles((theme) => ({
root: {
width: '100%',
@@ -16,7 +17,14 @@ const useStyles = makeStyles((theme) => ({
},
}));
+
function AccordionSetup({ id, header, body }) {
+ /* This functional component is an accordian, combined with accordion details. It's useful
+ * since one only has to pass in an id, header and body, and a full accordion is returned.
+ *
+ * Useful for saving lines of code, which is the biggest benefit of reusable components.
+ * It could be improved by adding a parameter for the header and body typography settings.
+ */
const classes = useStyles();
return (
@@ -38,5 +46,4 @@ function AccordionSetup({ id, header, body }) {
>
);
}
-
export default AccordionSetup;
diff --git a/website-glen/src/components/DomainViz.js b/website-glen/src/components/DomainViz.js
index 9d06d9563..01a3f1a68 100644
--- a/website-glen/src/components/DomainViz.js
+++ b/website-glen/src/components/DomainViz.js
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
-//import request from 'utils/Request'; TODO refactor to remove burden from ProtPlot.js
+//import request from 'utils/Request'; TODO refactor DomainViz to remove burden from the component, since it already does too much
import axios from 'axios';
import UploadFile from './utils/UploadFile';
import AccordionSetup from './AccordionSetup';
@@ -14,6 +14,7 @@ import { useHistory } from 'react-router';
import DomainVizIcon from './img/domainviz.png';
import { saveAs } from 'file-saver';
+
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
@@ -37,6 +38,7 @@ const useStyles = makeStyles((theme) => ({
},
}));
+
//generates random id;
let guid = () => {
let s4 = () => {
@@ -48,8 +50,14 @@ let guid = () => {
return s4() + s4() + '.' + s4() + '.' + s4() + '.' + s4() + '.' + s4() + s4() + s4();
}
+
function ProtPlot() {
- //temp for production
+ /* This functional component contains the logic, as well as the UI for the DomainViz page. This is the page that allows
+ * users to upload their fasta files, add their own settings, and produce the results. It also automatically redirects the user
+ * to the correct output page.
+ */
+
+ //TODO This was supposed to be temp for production, and the Home page was supposed to be added, but it hasn't been done yet.
function getImages() {
let id = textFields.uidTextField;
if (!id) {
@@ -73,12 +81,15 @@ function ProtPlot() {
history.push('/view-results/' + textFields.uidTextField);
}
//end of temp
+
const classes = useStyles();
const resultID = guid();
const history = useHistory()
+ // Input files
const [fastaFiles, setFastaFiles] = useState([]);
+ // User-defined parameters
const [checkboxes, setCheckboxes] = useState({
absoluteResultsCheckbox: false,
fastaFileLoadedCheckbox: false,
@@ -87,7 +98,6 @@ function ProtPlot() {
const handleCheckBox = (event) => {
setCheckboxes({ ...checkboxes, [event.target.name]: event.target.checked });
};
-
const [textFields, setTextFields] = useState({
cutoffTextField: '0.05',
maxCutoffTextField: '0.05',
@@ -102,6 +112,7 @@ function ProtPlot() {
saveAs(response.url);
});
}
+
function uploadTestFastaFile() {
// This function sends dummy info to the backend, so that it knows which file to use
setFastaFiles(
@@ -109,6 +120,7 @@ function ProtPlot() {
)
}
function uploadMultipleTestFastaFiles() {
+ // This function sends dummy info to the backend, so that it knows which files to use
setFastaFiles(
[
{file: "mult_test_1", name: "multi_test_file_1.fa"},
@@ -130,8 +142,6 @@ function ProtPlot() {
}
}
function changeFAFileName(index, newName) {
- console.log(fastaFiles)
- console.log(index)
// If the fastafile is one of the example files, we only replace its name, otherwise, we replace the whole file
// since that is the only way to change a File's name in Javascript.
if (fastaFiles[index].file) {
@@ -154,8 +164,11 @@ function ProtPlot() {
]);
}
}
- async function handleFastaFiles(fileList) {
+ async function handleFastaFiles(fileList) {
+ /* This function is run when the user uploads one or more fasta files, it validates the files
+ * and stores them in the fastaFile State variable to send them to the backend.
+ */
// Check that there are no test files in the fastaFiles list, since the backend cannot currently handle both test and regular files
for (let i=0; i {
- console.log(response);
+ // Reset setting values
textFields.scaleFigureTextField = '';
textFields.maxCutoffTextField = '';
textFields.cutoffTextField = '';
@@ -288,6 +305,7 @@ function ProtPlot() {
+
Protein domain search {'&'} visualization tool
@@ -371,7 +389,7 @@ function ProtPlot() {
- {/* temp for production */}
+ {/* temp for production - should be added to Home component long-term. */}
diff --git a/website-glen/src/components/Footer.js b/website-glen/src/components/Footer.js
index c5f0c5ab3..d9b9c9a7f 100644
--- a/website-glen/src/components/Footer.js
+++ b/website-glen/src/components/Footer.js
@@ -32,34 +32,41 @@ const useStyles = makeStyles((theme) => ({
},
}));
+
function Footer () {
- const classes = useStyles();
- return (
-
-
-
-
-
-
-
-
- Terms of Use
-
-
- Privacy Statement
-
-
-
-
- Contact:
-
- protools@ualberta.ca
-
- {'\u00A9'} 2020 The Uhrig Lab
-
-
+ /* This function is the footer for each page, and is incredibly poorly styled, however
+ * it works for all current pages.
+ * It contains links to important pages, and an email link.
+ */
+ const classes = useStyles();
+ return (
+
+
+
+
+
+
+
+
+
+ Terms of Use
+
+
+ Privacy Statement
+
-
+
+
+ Contact:
+
+ protools@ualberta.ca
+
+ {'\u00A9'} 2020 The Uhrig Lab
+
+
+
+
);
}
export default Footer;
+
diff --git a/website-glen/src/components/Help.js b/website-glen/src/components/Help.js
index 10208318d..bf6c66e5b 100644
--- a/website-glen/src/components/Help.js
+++ b/website-glen/src/components/Help.js
@@ -25,7 +25,17 @@ const useStyles = makeStyles((theme) => ({
width: "1000px"
},
}));
+
+
export const Help = () => {
+ /* This functional component displays helpful information for a user who may be struggling to use the tool.
+ * A rather simple component, and very similar to About.js, PrivacyStatement.js, and TermsOfUse.js.
+ *
+ * Based on this Google Doc: https://docs.google.com/document/d/1degKXhefAYDrJlXRwpU-yPnqwVgvFfo67koMiitGrqg/edit#heading=h.b11zvzh3fvy0
+ *
+ * This component could be improved by using css, and changing from a Grid layout to a more flexible one.
+ * Potentially it could be changed by only having one link to each unique URL, rather than many.
+ */
const classes = useStyles()
return (
@@ -101,6 +111,7 @@ export const Help = () => {
Snapshot: Users can click the Camera icon in the toolbar to take a .png snapshot of the current display in the plot (for example, after hiding a particular domain).
+ {/* This iframe is an example of our outputs, and will not appear unless the `example_iframe.html` file is in the `public` folder */}
@@ -108,6 +119,7 @@ export const Help = () => {
The mv-plots depict both the position and prevalence of each protein domain within the group of proteins input by the user. Conventional symbolic representations of the domains detected are also generated and placed next to the mv-plots.Note: interactive example is accessible by submitting the “Load Example” dataset on the main DomainViz page.
+ {/* This image is an example of our outputs, as a PDF */}
Generally, results with non-overlapping domains can be directly interpreted as showing the prevalence and position of each domain with different degrees of conservation within the protein group.
@@ -209,7 +221,6 @@ export const Help = () => {
Result Output: Job.id_ProteinGroup_prosite_domain_results_per_aa.tsvThe same as Job.id_ProteinGroup_pfam.csv, however for ProSite domains.
-
}>
FAQ
@@ -243,10 +254,9 @@ export const Help = () => {
-
+
);
}
-
diff --git a/website-glen/src/components/Home.js b/website-glen/src/components/Home.js
index d1d10c53a..8b1267591 100644
--- a/website-glen/src/components/Home.js
+++ b/website-glen/src/components/Home.js
@@ -21,6 +21,10 @@ import { useHistory } from 'react-router-dom';
// }));
function Home() {
+ /* This component is a hot mess, and only redirects the user to /domainviz when they arrive at / (ie. The main page of the site.)
+ * We originally were planning on having a fully fledged homepage, but since we only ended up creating the DomainViz tool, this was
+ * deemed unneccesary for the time being.
+ */
// const classes = useStyles();
let history = useHistory();
//automatically reroute to domainviz
@@ -72,5 +76,5 @@ function Home() {
>
);
}
-
export default Home;
+
diff --git a/website-glen/src/components/MotifX.js b/website-glen/src/components/MotifX.js
index c02a9a281..6aa782a87 100644
--- a/website-glen/src/components/MotifX.js
+++ b/website-glen/src/components/MotifX.js
@@ -3,6 +3,8 @@ import React from 'react';
function MotifX() {
+ // View the UMotifMerge branch in Github to see this component more closely resembling what it should be.
+ // https://github.com/UhrigLab/DomainViz/tree/UMotifMerge
return (
MotifX page
diff --git a/website-glen/src/components/PrivacyStatement.js b/website-glen/src/components/PrivacyStatement.js
index 0e5b41841..0af5cdee4 100644
--- a/website-glen/src/components/PrivacyStatement.js
+++ b/website-glen/src/components/PrivacyStatement.js
@@ -15,12 +15,23 @@ const useStyles = makeStyles((theme) => ({
textDecoration: 'underline',
},
}));
+
+
export const PrivacyStatement = () => {
+ /* This functional component displays helpful information for a user who wishes to know how we use their information.
+ * It may be out of date, and may require some rewriting.
+ * A rather simple component, and very similar to About.js, Help.js, and TermsOfUse.js.
+ *
+ * Based on this Google Doc: https://docs.google.com/document/d/1-pqGz7OFeM32aj1ebjRE45E84hGaBkCOCxoP-PXMiIw/edit#heading=h.75lceviscgpz
+ *
+ * This component could be improved by using css, and changing from a Grid layout to a more flexible one.
+ */
const classes = useStyles()
return (
+
Privacy StatementThis Privacy Statement explains what personal data is collected by the specific Uhrig Lab tool and service you are requesting, for what purposes, how it is processed, and how we keep it secure.
@@ -97,11 +108,9 @@ export const PrivacyStatement = () => {
Published on:Wednesday, December 16, 2020
-
-
+
);
}
-
diff --git a/website-glen/src/components/TermsOfUse.js b/website-glen/src/components/TermsOfUse.js
index 17246979f..9830f9bf1 100644
--- a/website-glen/src/components/TermsOfUse.js
+++ b/website-glen/src/components/TermsOfUse.js
@@ -10,12 +10,23 @@ const useStyles = makeStyles((theme) => ({
color: theme.palette.text.secondary,
},
}));
+
+
export const TermsOfUse = () => {
+ /* This functional component displays helpful information for a user who wishes to know how they can use the site.
+ * It may be out of date, and may require some rewriting.
+ * A rather simple component, and very similar to About.js, Help.js, and PrivacyStatement.js.
+ *
+ * Based on this Google Doc: https://docs.google.com/document/d/1-W9vfgZtzWgRO1X1m5rlOIWKIZMUE2onbEePClEOt7g/edit#heading=h.xas289ecghsv
+ *
+ * This component could be improved by using css, and changing from a Grid layout to a more flexible one.
+ */
const classes = useStyles()
return (
+
Terms of useGeneral
@@ -43,10 +54,9 @@ export const TermsOfUse = () => {
5. The original data may be subject to rights claimed by third parties, including but not limited to, patent, copyright, other intellectual property rights, biodiversity-related access and benefit-sharing rights. It is the responsibility of users of our services to ensure that their exploitation of the data does not infringe any of the rights of such third parties.The Terms of Use and the use of the Uhrig Lab Protein Tools website are governed by Canadian law.
-
+
);
}
-
diff --git a/website-glen/src/components/ViewPDF.js b/website-glen/src/components/ViewPDF.js
index 0a293f020..6ddd57526 100644
--- a/website-glen/src/components/ViewPDF.js
+++ b/website-glen/src/components/ViewPDF.js
@@ -36,10 +36,21 @@ const useStyles = makeStyles((theme) => ({
width: "293px"
},
}));
+
let interval;
export const groupsize = 6;
+
+
export const ViewPDF = () => {
+ /* This functional component is a page for the user to view the outputs of their DomainViz run.
+ * It could be extended to include outputs from UMotif as well, but this has not been done yet.
+ *
+ * The name of the component is out-of-date, since now we use iframes rather than PDFs for the outputs on the site itself.
+ *
+ * Overall this component is too complex, and some of the work done here should be shunted off to other components.
+ */
const url = window.location.pathname;
+ // TODO To many useState()s, needs refactoring
const [images, setImages] = useState([]);
const [groupNames, setGroupNames] = useState([]);
const [displayImages, setDisplayImages] = useState(false);
@@ -58,14 +69,20 @@ export const ViewPDF = () => {
function goToHome() {
history.push('/')
}
+
const handleClickOpen = () => {
setOpen(true);
};
-
const handleClose = () => {
setOpen(false);
};
+ // This useEffect is very complex, but essentially it gets the output images from the backend, groups them based on their group names
+ // displays the images if they exist, and if not, it shows both an alert, and a "failed" message on the page (Why did I do both?).
+ // As well, it controls the progress wheel based on the cookie-completion value sent from the backend.
+ //
+ // It runs a GET request to the backend every 5 seconds for an update on the run's progress. Once the run has completed,
+ // it stops GET-ing from the backend.
useEffect(() => {
interval = setInterval(() => {
fetch('/api/images/' + uid).then(response =>
@@ -97,6 +114,8 @@ export const ViewPDF = () => {
console.log("No message was found with this cookie: Cookie -1.")
}
}
+ // If data.failed isn't -1, the run hasn't actually failed, but it is still in progress, with the failed
+ // number representing how far along in the run we are, from 1-100.
else if (data.failed < 100) {
setShowProgressBar(true);
setProgress(data.failed);
@@ -114,6 +133,10 @@ export const ViewPDF = () => {
}, 5000);
return () => clearInterval(interval);
}, [uid]);
+
+ // This second useEffect() is run whenever the first useEffect() updates one of the important state variables, as can be seen in the
+ // [images, failed, currentMessage, messages, groupNames.length] list.
+ // This is what actually updates the UI with the message, or the iframes.
useEffect(() => {
if (images.length / groupNames.length === groupsize) {
setDisplayImages(true);
@@ -130,6 +153,7 @@ export const ViewPDF = () => {
return (
+
@@ -141,6 +165,7 @@ export const ViewPDF = () => {
{(displayImages && !showProgressBar && failed === false) &&
+ // The component is what actually displays the iframes.
}
{(!displayImages && showProgressBar && failed === false) &&
@@ -148,6 +173,8 @@ export const ViewPDF = () => {
{(messages.length) && //If the message only contains whitespace, display the loading text
+ // The component shows a list of the messages that have been returned from the cookies
+ // produced in the backend.
}
Task processing can take several minutes to several hours. Please wait or copy the Result ID, exit, and retrieve your results later using the homepage.
@@ -172,6 +199,7 @@ export const ViewPDF = () => {
}
+ {/* End of TEMP */}
{(!displayImages && !showProgressBar && failed) &&
@@ -186,6 +214,11 @@ export const ViewPDF = () => {
}
+ {/*
+ This Grid item contains the Exit button, and the dialog popup that appears when the user tries to exit the page.
+ While useful, we need to open this dialog when the user tries to navigate away from this page using other methods,
+ such as the AppBar, or the browser navigation buttons.
+ */}
+
);
}
diff --git a/website-glen/src/components/utils/FastaFileMap.js b/website-glen/src/components/utils/FastaFileMap.js
index fbefdb7aa..516fc0c70 100644
--- a/website-glen/src/components/utils/FastaFileMap.js
+++ b/website-glen/src/components/utils/FastaFileMap.js
@@ -1,6 +1,7 @@
import { React, useState, Fragment } from 'react';
import { Button, Grid, makeStyles, TextField, Typography, Paper } from '@material-ui/core';
+
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
@@ -16,48 +17,60 @@ const useStyles = makeStyles((theme) => ({
},
}));
+
export const FastaFileMap = ({ fastaFiles, changeName, removeFile }) => {
- const classes = useStyles();
- const [faFileNames, setFAFileNames] = useState([]);
+ /* This component displays the fasta files after they are uploaded, and gives the user the option to
+ * perform actions such as deletion, and renaming of each file.
+ *
+ * As a `Map` component, this component receives a list of objects, fasta files in this instance,
+ * and displays each object in a way that allows reusability, and conciseness. However, this component may be improved
+ * by making it more customizable, and more general, ie, making it more of a FileMap.
+ *
+ * Must be used inside of a Grid container component.
+ */
- const handleFAFilesChange = (event, faFileName, index) => {
- if (faFileNames.length < fastaFiles.length) {
- let names = []
- for (let i=0; i {
+ if (faFileNames.length < fastaFiles.length) {
+ let names = []
+ for (let i=0; i {
- if (faFileNames.length !== 0) {
- changeName(index, faFileNames[index]);
- }
- else {
- alert("Please change a name before clicking the \"Confirm\" button.")
- }
+ const returnName = (event, index) => {
+ if (faFileNames.length !== 0) {
+ changeName(index, faFileNames[index]);
}
- const deleteFile = (event, index) => {
- removeFile(index)
+ else {
+ alert("Please change a name before clicking the \"Confirm\" button.")
}
+ }
+ const deleteFile = (event, index) => {
+ removeFile(index)
+ }
- return (
+ return (
<>
+ {/* Each file will have the following. */}
{fastaFiles.map((file, i) => {
return (
-
+ // Since this component must exist inside of a Grid container component, we don't add a second container.
diff --git a/website-glen/src/components/utils/MessageMap.js b/website-glen/src/components/utils/MessageMap.js
index 71d93614e..c0b244dd1 100644
--- a/website-glen/src/components/utils/MessageMap.js
+++ b/website-glen/src/components/utils/MessageMap.js
@@ -2,8 +2,15 @@ import { React } from 'react';
import { Typography} from '@material-ui/core';
export const MessageMap = ({ messages }) => {
-
- return (
+ /* This component displays the messages received from the cookies generated by Pascal's domainviz.py script,
+ *
+ * As a `Map` component, this component receives a list of objects, strings in this instance,
+ * and displays each object in a way that allows reusability, and conciseness.
+ *
+ * Must be used inside of a Grid item component.
+ * Should be used inside of a Paper component for best styling.
+ */
+ return (
<>
{"Information about the run: "}
{messages.map((message, index) => {
diff --git a/website-glen/src/components/utils/PDF.js b/website-glen/src/components/utils/PDF.js
index 15204a684..744fd023d 100644
--- a/website-glen/src/components/utils/PDF.js
+++ b/website-glen/src/components/utils/PDF.js
@@ -3,7 +3,10 @@ import React, { useState } from 'react';
import { Page } from 'react-pdf';
import { Document } from 'react-pdf/dist/esm/entry.webpack';
+
export const PDF = ({pdf}) => {
+ // This component is not used any more, and can be deleted. It was used to display PDF files when we used those,
+ // but now we use iframes instead. This code was basically all taken from the react-pdf readme page.
const [numPages, setNumPages] = useState(null);
const [didError, setDidError] = useState(false);
@@ -38,4 +41,5 @@ export const PDF = ({pdf}) => {
}
>
);
-}
\ No newline at end of file
+}
+
diff --git a/website-glen/src/components/utils/PDFMap.js b/website-glen/src/components/utils/PDFMap.js
index bded3b05f..66af1b6a5 100644
--- a/website-glen/src/components/utils/PDFMap.js
+++ b/website-glen/src/components/utils/PDFMap.js
@@ -5,6 +5,7 @@ import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import { saveAs } from 'file-saver';
import { groupsize } from '../ViewPDF'
+
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
@@ -26,33 +27,47 @@ const useStyles = makeStyles((theme) => ({
},
}));
+// TODO rename PDF-X components to iframe-X or something similar, since we no longer use PDFs for on-site viewing.
export const PDFMap = ({ images, uid, groupNames }) => {
- const classes = useStyles()
- const [htmls, setHTMLs] = useState([])
- function gotoDownload() {
- fetch('/api/download/' + uid).then(response => {
- saveAs(response.url, 'DomainViz_results.zip')
- });
- }
+ /* This component fetches and displays the iframe files after they have been retrieved from the backend.
+ * perform actions such as deletion, and renaming of each file.
+ * iframes are cool since they allow multiple different actions, such as hiding certain elements, zooming, and more.
+ * This makes the site much more interactable, with minimal dev work.
+ *
+ * As a `Map` component, this component receives a list of objects, the ending to links for iframe files in this instance,
+ * and displays each object in a way that allows reusability, and conciseness.
+ *
+ * Must be used inside of a Grid container component.
+ */
+ const classes = useStyles()
+ const [htmls, setHTMLs] = useState([])
- // NOTE: There may be a warning in the console about this useEffect such as:
- // " Line 45:12: React Hook useEffect has missing dependencies: 'htmls.length' and 'images'.
- // Either include them or remove the dependency array react-hooks/exhaustive-deps"
- //
- // HOWEVER, do not add these, as there is asyncronous code running that will cause the same html file to be fetched
- // multiple times, rather than fetching all num_groups*groupsize. I realize that this is likely poor practices, however,
- // we are pressed for time and i cannot determine a solution that is better.
- useEffect(() => {
- for (let i=0; i {
- if (htmls.length < images.length) //shouldnt add more htmls if we already have the same amount as we have image links
- setHTMLs(old => [...old, response.url].sort());
- })
- }
- // eslint-disable-next-line
- }, []);
+ // We have a button that allows the user to download their entire results, and this function performs the downloading
+ function gotoDownload() {
+ fetch('/api/download/' + uid).then(response => {
+ saveAs(response.url, 'DomainViz_results.zip')
+ });
+ }
+
+ // NOTE: There may be a warning in the console about this useEffect such as:
+ // " Line 45:12: React Hook useEffect has missing dependencies: 'htmls.length' and 'images'.
+ // Either include them or remove the dependency array react-hooks/exhaustive-deps"
+ //
+ // HOWEVER, do not add these, as there is asyncronous code running that will cause the same html file to be fetched
+ // multiple times, rather than fetching all num_groups*groupsize. I realize that this is likely poor practices, however,
+ // we are pressed for time and i cannot determine a solution that is better.
+ useEffect(() => {
+ // This useEffect() is what GETs the iframes from the backend.
+ for (let i=0; i {
+ if (htmls.length < images.length) //shouldnt add more htmls if we already have the same amount as we have image links
+ setHTMLs(old => [...old, response.url].sort());
+ })
+ }
+ // eslint-disable-next-line
+ }, []);
- return (
+ return (
<>
@@ -71,6 +86,8 @@ export const PDFMap = ({ images, uid, groupNames }) => {
+ {/* This could be greatly improved, but I am not sure how. It works for now, but this is truly
+ terrible coding practices, having hard-coded indicies like this. */}
diff --git a/website-glen/src/components/utils/Request.js b/website-glen/src/components/utils/Request.js
index 12b7d24d0..f9b740ec2 100644
--- a/website-glen/src/components/utils/Request.js
+++ b/website-glen/src/components/utils/Request.js
@@ -1,5 +1,8 @@
import axios from 'axios';
+// This component should be used to POST the input data from the frontend to the backend, but it is currently all inside
+// of DomainViz.js. Other fetch/GET requests could also be put here, although, you may want to split this up futher between
+// GET and POST requests.
export default axios.create({
baseURL: 'localhost:3000',
timeout: 3000,
diff --git a/website-glen/src/components/utils/UploadFile.js b/website-glen/src/components/utils/UploadFile.js
index 538327e6b..32ff64cff 100644
--- a/website-glen/src/components/utils/UploadFile.js
+++ b/website-glen/src/components/utils/UploadFile.js
@@ -13,6 +13,9 @@ const useStyles = makeStyles((theme) => ({
}));
function UploadFile({ value, handleFile, acceptedTypes=".tsv", multiple=false }) {
+ /* This component is used to accept input files, do some simple validation, and perform a function call (handleFile)
+ * after the completion of the upload. It may, or may not allow multiple file uploads, depending on the value of the `multiple` variable.
+ */
const classes = useStyles();
const [inputValue, setInputValue] = useState("")
const returnFile = (file) => {
diff --git a/website-glen/src/components/utils/ValidateFile.js b/website-glen/src/components/utils/ValidateFile.js
index 35702db91..fe8e58c72 100644
--- a/website-glen/src/components/utils/ValidateFile.js
+++ b/website-glen/src/components/utils/ValidateFile.js
@@ -12,6 +12,7 @@ async function canReadFile(file) {
}
async function checkFasta(file) {
+ // Validates a physical fasta FILE.
return file.text().then(content => {
let headers = [];
let lines = content.split('\n');
@@ -66,6 +67,9 @@ async function checkFasta(file) {
}
async function isFasta(file) {
+ // NOTE: This is the function that is called by DomainViz whenever a fasta file is uploaded.
+ // All the functions above are helper functions called by this function.
+
// Check if file can be read
let valid = false;
await canReadFile(file).then(result => {
@@ -94,6 +98,10 @@ async function isFasta(file) {
}
export default isFasta
+// TODO the code below is not used, as we don't use groupfile or colorfile anymore. If @Pascal or @Glen decide they want to use these again
+// you can use this python code as a basis for the validation of those files.
+
+
// def is_groupfile(filename):
// # Default values
// vreturn = True
diff --git a/website-glen/src/setupTests.js b/website-glen/src/setupTests.js
index 74b1a275a..f6f97aeeb 100644
--- a/website-glen/src/setupTests.js
+++ b/website-glen/src/setupTests.js
@@ -2,4 +2,8 @@
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
+
+/*
+ * I haven't made any automated tests, but feel free to look into it.
+ */
import '@testing-library/jest-dom/extend-expect';