Skip to content

Commit

Permalink
Session projects (#262)
Browse files Browse the repository at this point in the history
* fix computing size of summary info

* fix bug of saving default scene, reset ngl state during unmounting preview

* add not completed selecting of compounds

* add not completed selecting of compounds

* add not completed selecting of compounds

* add not completed selecting of compounds

* add showing selected compounds

* fix showing selected compounds

* fix showing selected compounds

* add refactor of compounds view

* add refactor of compounds view

* add refactor of redux constants

* add refactor of redux constants

* remove console log

* add not completed test

* remove not completed tests

* add test commit

* #2 add projects reducer and add project modal window

* #2 add create new project modal window mock-up

* #2 add formik

* #2 add autocomplete field for tags

* #3 first draft of issue reporting

* #2 fix validation of select component

* #2 fix tags array

* #3 added URL to LogRocket session into Sentry report on exception

* #3 modified modal window and hooked input fields

* #2 add call api and response actions

* #2 change target property

* #2 change target validation

* #2 open project on given url

* #2 add action of create initial snapshot

* #3 added functionality for getting picture of screen, uploading it to github repo and attaching it to created issue + updated layout of modal form

* #3 add environment variable

* #3 create screenshot before open modal window

* #2 enable to reload project from snapshot

* #3 added form validation

* #3 removed .env from tracking

* #3 added missing styles

* #3 add loading indications when capturing screen and creating issue

* #3 reverted loading

* #2 add snapshot to project

* #2 add load project from snapshot

* #40 remove download Yank button

* #40 remove header of viewer controls, use buttonGroups for viewer controls

* #3 disable all fields on creating issue, added email validation, changed color of button, added info prompt about screen capture, minor fixes, experimental - try to capture just browser tab without cursor

* #40 remove showing vectors in display viewer control, add shadow to viewer controls

* #40 clear the filters after click on Clear selection button

* #40 modify filter dialog (not completed yet)

* #40 apply changes instantly, not on “apply” in sort - filter dialog

* #40 minimise filter/sort dialog

* #40 propagate JS events in filter/sort dialog

* #40 reordering of rows in Settings controls

* #40 move download CSV button to summary info header

* #40 fix hit navigator box size, when I clear filter

* #40 remove unused components

* #3 decomposition of issue report into logic segments

* #3 updated env variable in readme to its proper name

* #3 review modifications, changed axios calls to api

* #3 fixed METHOD referencing

* #3 changed constants prefix to follow path structure, fixed constatnts typo, removed unnecessary log

* #40 close sort dialog after click on clear selection

* #7 added new idea submission, made report form as parent and widen it, removed commented part of code from githubApi

* #58 reordered header bar, show also target name and change cursor to pointer on clickable images

* #3 added also "Cancel" button for preceding dialog of taking a screenshot to have an option to cancel this action

* #48 add not finished table in molecule view

* #48 remove target name from label

* #59 added Janssen into header, changed "Supported by" to be a modal pop up and added there ultra dd, imi, horizon 2020

* #48 move quality labels and title label next to ALCV buttons

* #48 change site number style

* #4 added table with descriptions for mouse controls

* #3 #4 added loading progress to submit button, closing modal is done pure just via cancel button

* #48 add properties header with calculating of window width

* #48 change alignment of grid components in Preview.js

* #48 add count of vectors total (pickable)

* #3 #4 removed "Report error" from the menu, log also version of application into github issues

* #4 typo

* #48 add computation of #cpd property

* #48 fix opening of sort dialog

* #48 fix table rows id

* #48 change size of ALCV buttons

* #48 change size of image in molecule view

* #48 add header of properties

* #48 center the properties

* #48 redesign of molecule view to smallest as possible

* #48 fix count of compounds

* #48 change width of count of compounds property

* #66 change default background color of nglView

* #66 activate first molecule group

* #66 fix padding of quality labels

* #66 add version of app to menu

* #66 Activate first hit of first cluster, display all

* #92 fix layout of grid containers

* #67 testing base64

* #67 add base64 decoding

* #92 remove grid breakpoint of preview component, move breakpoints to css

* #92 change size of control buttons

* #107 not completed fix of compounds spinning wheel

* #107 disable auto check first cluster and molecule

* #107 add not completed loading images of compounds

* #107 add not completed loading images of compounds

* #107 add not completed loading images of compounds

* #107 add not completed loading images of compounds

* #107 add not completed loading images of compounds

* #107 add not completed loading images of compounds

* #107 add not completed loading images of compounds

* #107 add not completed loading images of compounds

* #107 add not completed loading images of compounds

* #107 fix of loading images of compounds

* #41 fix showing compound in NGL view

* #41 fix showing compound in NGL view

* #41 fix showing compound in NGL view

* #41 fix compound classes in compounds picker

* #41 fix compound classes in compounds picker

* #41 fix compound classes in compounds picker

* #41 fix compound classes in compounds picker

* #41 fix first compound classes in compounds picker

* #41 fix uncheck of compounds

* #41 fix uncheck of compounds

* #41 fix uncheck of compounds

* #41 fix uncheck of compounds

* #41 fix uncheck of compounds

* #41 fix uncheck of compounds

* #41 fix uncheck of compounds

* #41 fix uncheck of compounds

* #41 fix uncheck of compounds

* #41 fix uncheck of compounds

* #41 fix clear selection of compounds

* #41 fix select all compounds

* #41 fix select all compounds

* #108 do not reset paging of molecule list on component update (causes loader to stuck), removed undefined a attribute

* #41 fix indexes of compounds

* #41 fix indexes of compounds

* #41 reset objects from nglView and showedCompoundList

* #41 reset objects from nglView and showedCompoundList

* #41 reset compoundsClasses

* #41 reset compoundsClasses

* #41 reset compoundsClasses

* #41 reset compoundsClasses

* #41 reset highlightedCompoundId, reset configuration

* #41 fix reset compound classes

* #109 changed saving type when sharing snapshot

* #41 fix reset compound classes

* #41 fix reset compound classes

* #109 fixed not working 'Save session as' and 'Share snapshot' buttons on existing session, changed name of saved snapshot from 'undefined' to 'shared snapshot'

* #116 restore preview reducer from session

* #117 add snack bar for error handling

* #117 fix error handling

* #109 modified logic for buttons to work in existing session state, latestSession refers to recently created session and its uuid, added loading for snapshot sharing

* #117 fix error handling

* #117 fix error handling

* #125 remove cpm and vectors properties

* #125 remove clabel with count of compounds

* #125 fix bug of undefined stage

* #125 fix bug of undefined stage

* #125 show title of compounds

* #125 fix undefined stage

* #125 without sending errors to sentry

* reverted handling in errorBoundary and root

* removed all setError()

* #144 without sending errors to sentry

* #144 fix shift + click

* #144 fix axios body data

* removed yarn.lock

* Revert "removed yarn.lock"

This reverts commit 92da24c

* Revert "removed yarn.lock"

This reverts commit 92da24c

remove husky

* comment bad test

* #146 set proper target name

* #144 reset names of colored fields of compounds to pick

* #144 reset names of colored fields of compounds to pick

* #144 reset names of colored fields of compounds to pick in clear selection

* #144 simplify reset compound classes

* #149 changed "Supported by" to "Contributors" and added more entities

* #144 fix disappear of colour fields in compounds to pick

* #144 fix colour fields in compounds to pick

* increase version of app

* added missing log function for console, added setting latestSession for "Save session" to proper complete saving cycle

* #97 add automatic dept queuing

* #155 reduced radius of vectors with some available connections

* #148 changed background color of "A" button in molecule view so it is darker if it has some other options active

* #148 changed background color of "A" button in molecule view so it is darker if it has some other options active

* #148 added hover styling for selected molecule options

* #148 always deselect all control buttons if some is selected on "A" press

* #98 add hide all selected molecules

* #98 hide compound image in shopping cart

* #2 increase version of app

* #2 merge and add small refactoring

* #2 add fixed conflicts

* #2 remove old imports

* #2 reset project reducer after exit from project or target

* add docker configuration for localhost development

* #14 add docker compose for local BE development

* #14 add not completed manual snapshot

* add docker configuration for localhost development

* #14 load projects into table

* #14 enable to create project from scratch

* #14 enable to open project

* #14 enable to simple search for project

* #14 unable to create project without login, project history has expansion card

* #16 create init snapshot and restore from given snapshot

* #16 create init snapshot and restore from given snapshot

* #16 enable to remove project by logged in user

* #16 allow to create manual snapshot

* #16 fix redirecting after creation of manual snapshot

* #16 add validation of snapshot

* #16 fix expansion of project history

* #16 show tree loaded from BE

* #15 add not completed highlighting of selected snapshot in project history

* #15 fix undefined properties

* #15 add completed highlighting of selected snapshot in project history

* #15 fix order of project history tree

* #15 fix undefined value

* #190 Fix inconsistencies - Selecting and reselecting sites

* #246 fixed assigning and removing molecule orientations

* #15 fix method reload session

* #15 disable selection of all molecules when is loaded snapshot

* #15 fix loading of snapshot tree

* #15 remove project with cascade

* #15 create project from opened target

* #15 add not completed creating project from previous snapshot

* #15 add not tested freating of project from existing snapshot

* #15 fix bugs during creating of project from existing snapshot

* #15 fix next bugs during creating of project from existing snapshot

* #9 save initial snapshot only in project mode

* #9 fix loading initial snapshot of empty project

* #9 test without selecting all molecules

* #9 test with selecting all molecules

* #16 share snapshots of projects

* #255 not completed solution of switchings og snapshots in project

* #255 fix switching of snapshots in project

* #255 fix switching of snapshots in project

* #255 fix switching of snapshots in project

* #255 enable to make interaction after switching of snapshots in project

* #256 fix wrong displaying of snapshot details in sharing dialog

* #11 add tooltip of project in project table, fix date of snapshot

* #11 add not completed fix of infinity loop during creating initial snapshot

* #11 add fix of infinity loop during creating initial snapshot

* #11 work with projects with auto selecting first site and all molecules, this feature is kept only in target mode

* #11 test when is called creating initial snapshot method

* #11 fix creating of init snapshot after creating project

* #11 fix creating of init snapshot after creating project

* #261 disable create button during creating project from snapshot

* #16 add modal to save changes before switching snapshots

Co-authored-by: matej <matej.vavrek@m2ms.sk>
Co-authored-by: Rachael Skyner <reskyner@googlemail.com>
  • Loading branch information
3 people authored Apr 20, 2020
1 parent cb8f9e7 commit a421d1b
Show file tree
Hide file tree
Showing 61 changed files with 3,257 additions and 980 deletions.
Empty file modified docker-compose.dev.yml
100644 → 100755
Empty file.
35 changes: 23 additions & 12 deletions docker-compose.localhost.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ version: '3'

services:
mysql:
image: mysql:5.7.23
image: mysql:5.7
volumes:
- ../data/mysql_data:/var/lib/mysql
- ../data/mysql_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: django_db
Expand All @@ -16,7 +16,7 @@ services:
container_name: neo4j
image: neo4j:3.5
ports:
# Comment these two out in produciton
# Comment these two out in production
- "7474:7474"
- "7687:7687"
ulimits:
Expand All @@ -36,7 +36,7 @@ services:
volumes:
- ../data/logs:/code/logs/
- ../data/media:/code/media/
- ../fragalysis-frontend:/code/frontend
- ../fragalysis-frontend:/code/frontend/
- ../fragalysis-backend:/code/
ports:
- "8080:80"
Expand All @@ -45,15 +45,26 @@ services:
- graph
loader:
container_name: loader
image: loader:latest
image: xchem/fragalysis-stack:latest
command: /bin/bash /code/docker-entrypoint.sh
volumes:
- ../data/input:/fragalysis
- ../data/media:/code/media
- ../data/input:/fragalysis/
- ../data/media:/code/media/
- ../fragalysis-frontend:/code/frontend/
- ../fragalysis-backend:/code/
- ../dls-fragalysis-stack-openshift/images/loader/docker-entrypoint.sh:/code/docker-entrypoint.sh
- ../fragalysis-loader/run_loader.sh:/code/run_loader.sh
- ../fragalysis-loader/loader.py:/code/loader.py
- ../fragalysis-loader/test_entry.sh:/code/test_entry.sh
- ../fragalysis-loader/wait-for-it.sh:/code/wait-for-it.sh
- ../fragalysis-loader/database_check.py:/code/database_check.py
- ../fragalysis-loader/tests:/code/tests
- ../fragalysis-loader/loader:/code/loader
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: django_db
MYSQL_PASSWORD: django_password
MYSQL_USER: django
DATA_ORIGIN: EXAMPLE
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: django_db
MYSQL_PASSWORD: django_password
MYSQL_USER: django
DATA_ORIGIN: EXAMPLE
depends_on:
- mysql
14 changes: 8 additions & 6 deletions js/components/header/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ import {
Input,
Person,
Home,
Storage,
SupervisorAccount,
Menu as MenuIcon,
Work
Work,
Description
} from '@material-ui/icons';
import { HeaderContext } from './headerContext';
import { Button } from '../common';
Expand Down Expand Up @@ -65,7 +65,7 @@ const useStyles = makeStyles(theme => ({
},
loadingPaper: {
backgroundColor: theme.palette.background.default,
zIndex: 1,
zIndex: 1301,
width: '100%',
position: 'absolute',
opacity: 0,
Expand Down Expand Up @@ -296,13 +296,15 @@ export default memo(
</ListItemIcon>
<ListItemText primary="Home" />
</ListItem>
<Divider />

<ListItem button onClick={() => history.push(URLS.sessions)}>
<ListItem button onClick={() => history.push(URLS.projects)}>
<ListItemIcon>
<Storage />
<Description />
</ListItemIcon>
<ListItemText primary="Sessions" />
<ListItemText primary="Projects" />
</ListItem>

<ListItem button onClick={() => history.push(URLS.management)}>
<ListItemIcon>
<Work />
Expand Down
13 changes: 11 additions & 2 deletions js/components/helpers/useEnableUserInteracion.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState, useEffect } from 'react';
import { useSelector } from 'react-redux';
import { VIEWS } from '../../constants/constants';

export const useDisableUserInteraction = () => {
const [disableInteraction, setDisableInteraction] = useState(false);
Expand All @@ -9,13 +10,19 @@ export const useDisableUserInteraction = () => {
const countOfRemainingMoleculeGroups = useSelector(state => state.nglReducers.countOfRemainingMoleculeGroups);
const proteinsHasLoaded = useSelector(state => state.nglReducers.proteinsHasLoaded);
const countOfPendingNglObjects = useSelector(state => state.nglReducers.countOfPendingNglObjects);
const isLoadingTree = useSelector(state => state.projectReducers.isLoadingTree);
const isLoadingCurrentSnapshot = useSelector(state => state.projectReducers.isLoadingCurrentSnapshot);

useEffect(() => {
if (
isLoadingTree === false &&
isLoadingCurrentSnapshot === false &&
countOfPendingVectorLoadRequests === 0 &&
countOfPendingNglObjects === 0 &&
countOfPendingNglObjects[VIEWS.SUMMARY_VIEW] === 0 &&
countOfPendingNglObjects[VIEWS.MAJOR_VIEW] === 0 &&
((countOfRemainingMoleculeGroups === 0 && proteinsHasLoaded === true) ||
(countOfRemainingMoleculeGroups === null && proteinsHasLoaded === null))
(countOfRemainingMoleculeGroups === null && proteinsHasLoaded === null) ||
(countOfRemainingMoleculeGroups === null && proteinsHasLoaded === true))
) {
if (disableInteraction === true) {
setDisableInteraction(false);
Expand All @@ -30,6 +37,8 @@ export const useDisableUserInteraction = () => {
countOfPendingVectorLoadRequests,
countOfRemainingMoleculeGroups,
disableInteraction,
isLoadingCurrentSnapshot,
isLoadingTree,
proteinsHasLoaded
]);

Expand Down
94 changes: 48 additions & 46 deletions js/components/landing/Landing.js
Original file line number Diff line number Diff line change
@@ -1,72 +1,74 @@
/**
* Created by ricgillams on 21/06/2018.
*/
import { Grid } from '@material-ui/core';
import React, { memo, useEffect } from 'react';
import { Grid, Link } from '@material-ui/core';
import React, { memo, useContext, useEffect, useState } from 'react';
import TargetList from '../target/targetList';
import SessionList from '../session/sessionList';
//import SessionList from '../session/sessionList';
import { connect } from 'react-redux';
import * as apiActions from '../../reducers/api/actions';
import * as selectionActions from '../../reducers/selection/actions';
import { DJANGO_CONTEXT } from '../../utils/djangoContext';
import { Projects } from '../projects';
import { HeaderContext } from '../header/headerContext';
import { resetCurrentCompoundsSettings } from '../preview/compounds/redux/actions';
import { resetProjectsReducer } from '../projects/redux/actions';

const Landing = memo(({ resetSelectionState, resetTargetState, resetCurrentCompoundsSettings }) => {
let text_div;
const Landing = memo(
({ resetSelectionState, resetTargetState, resetCurrentCompoundsSettings, resetProjectsReducer }) => {
const { setSnackBarTitle } = useContext(HeaderContext);
const [loginText, setLoginText] = useState("You're logged in as " + DJANGO_CONTEXT['username']);

if (DJANGO_CONTEXT['authenticated'] === true) {
var entry_text = "You're logged in as " + DJANGO_CONTEXT['username'];
text_div = <h3>{entry_text}</h3>;
} else {
text_div = (
<h3>
To view own targets login here:
<a className="inline" href="/accounts/login">
FedID Login
</a>
</h3>
);
}
useEffect(() => {
if (DJANGO_CONTEXT['authenticated'] !== true) {
setLoginText(
<>
{'To view own targets login here: '}
<Link href="/accounts/login" color="inherit" variant="subtitle2">
FedID Login
</Link>
</>
);
}
}, []);

useEffect(() => {
resetTargetState();
resetSelectionState();
resetCurrentCompoundsSettings(true);
}, [resetTargetState, resetSelectionState, resetCurrentCompoundsSettings]);
useEffect(() => {
resetTargetState();
resetSelectionState();
setSnackBarTitle(loginText);
resetCurrentCompoundsSettings(true);
resetProjectsReducer();
}, [
resetTargetState,
resetSelectionState,
setSnackBarTitle,
loginText,
resetCurrentCompoundsSettings,
resetProjectsReducer
]);

return (
<Grid container spacing={2}>
<Grid container item xs={12} sm={6} md={4} direction="column" justify="flex-start">
<Grid item>
<h1>Welcome to Fragalysis</h1>
{text_div}
return (
<Grid container spacing={2}>
<Grid item xs={4}>
<TargetList key="TARGLIST" />
</Grid>
<Grid item>
<p>
<a className="inline" href="http://cs04r-sc-vserv-137.diamond.ac.uk:8089/overview/targets/">
Target status overview
</a>{' '}
(only accessible within Diamond)
</p>
<Grid item xs={8}>
{/*<SessionList />*/}
<Projects />
</Grid>
</Grid>
<Grid item xs={12} sm={6} md={4}>
<TargetList key="TARGLIST" />
</Grid>
<Grid item xs={12} sm={6} md={4}>
<SessionList />
</Grid>
</Grid>
);
});
);
}
);

function mapStateToProps(state) {
return {};
}
const mapDispatchToProps = {
resetSelectionState: selectionActions.resetSelectionState,
resetTargetState: apiActions.resetTargetState,
resetCurrentCompoundsSettings
resetCurrentCompoundsSettings,
resetProjectsReducer
};

export default connect(mapStateToProps, mapDispatchToProps)(Landing);
51 changes: 38 additions & 13 deletions js/components/preview/Preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Created by abradley on 14/04/2018.
*/

import React, { memo, useEffect, useRef, useState } from 'react';
import React, { memo, useContext, useEffect, useRef, useState } from 'react';
import { Grid, makeStyles, useTheme } from '@material-ui/core';
import NGLView from '../nglView/nglView';
import MoleculeList from './molecule/moleculeList';
Expand All @@ -12,13 +12,17 @@ import { CompoundList } from './compounds/compoundList';
import { ViewerControls } from './viewerControls';
import { ComputeSize } from '../../utils/computeSize';
import { withUpdatingTarget } from '../target/withUpdatingTarget';
import ModalStateSave from '../session/modalStateSave';
import { VIEWS } from '../../constants/constants';
import { withLoadingProtein } from './withLoadingProtein';
import { withSessionManagement } from '../session/withSessionManagement';
import { withSnapshotManagement } from '../snapshot/withSnapshotManagement';
import { useDispatch } from 'react-redux';
import { removeAllNglComponents } from '../../reducers/ngl/actions';
import { resetCurrentCompoundsSettings } from './compounds/redux/actions';
import { ProjectHistory } from './projectHistory';
import { ProjectDetailDrawer } from '../projects/projectDetailDrawer';
import { NewSnapshotModal } from '../snapshot/modals/newSnapshotModal';
import { HeaderContext } from '../header/headerContext';
import { unmountPreviewComponent } from './redux/dispatchActions';
import { NglContext } from '../nglView/nglProvider';
import { SaveSnapshotBeforeExit } from '../snapshot/modals/saveSnapshotBeforeExit';
//import HotspotList from '../hotspot/hotspotList';

const hitNavigatorWidth = 504;
Expand Down Expand Up @@ -59,10 +63,12 @@ const useStyles = makeStyles(theme => ({
}
}));

const Preview = memo(({ isStateLoaded, headerHeight }) => {
const Preview = memo(({ isStateLoaded, hideProjects }) => {
const classes = useStyles();
const theme = useTheme();

const { headerHeight } = useContext(HeaderContext);
const { nglViewList } = useContext(NglContext);
const nglViewerControlsRef = useRef(null);
const dispatch = useDispatch();

Expand All @@ -79,30 +85,39 @@ const Preview = memo(({ isStateLoaded, headerHeight }) => {

const [summaryViewHeight, setSummaryViewHeight] = useState(0);

const compoundHeight = `calc(100vh - ${headerHeight}px - ${theme.spacing(2)}px - ${summaryViewHeight}px - 64px)`;
const [projectHistoryHeight, setProjectHistoryHeight] = useState(0);

const compoundHeight = `calc(100vh - ${headerHeight}px - ${theme.spacing(
2
)}px - ${summaryViewHeight}px - ${projectHistoryHeight}px - 72px)`;
const [showHistory, setShowHistory] = useState(false);

useEffect(() => {
// Unmount Preview - reset NGL state
return () => {
dispatch(removeAllNglComponents());
dispatch(resetCurrentCompoundsSettings(true));
dispatch(unmountPreviewComponent(nglViewList));
};
}, [dispatch]);
}, [dispatch, nglViewList]);

return (
<>
<Grid container justify="space-between" className={classes.root} spacing={1}>
<Grid item container direction="column" spacing={1} className={classes.hitColumn}>
{/* Hit cluster selector */}
<Grid item>
<MolGroupSelector isStateLoaded={isStateLoaded} handleHeightChange={setMolGroupsHeight} />
<MolGroupSelector
isStateLoaded={isStateLoaded}
hideProjects={hideProjects}
handleHeightChange={setMolGroupsHeight}
/>
</Grid>
{/* Hit navigator */}
<Grid item>
<MoleculeList
height={moleculeListHeight}
setFilterItemsHeight={setFilterItemsHeight}
filterItemsHeight={filterItemsHeight}
hideProjects={hideProjects}
/>
</Grid>
</Grid>
Expand All @@ -129,14 +144,24 @@ const Preview = memo(({ isStateLoaded, headerHeight }) => {
<Grid item>
<CompoundList height={compoundHeight} />
</Grid>
{!hideProjects && (
<Grid item>
<ProjectHistory
setHeight={setProjectHistoryHeight}
showFullHistory={() => setShowHistory(!showHistory)}
/>
</Grid>
)}
</Grid>
{/*<Grid item xs={12} sm={6} md={4} >
<HotspotList />
</Grid>*/}
</Grid>
<ModalStateSave />
<NewSnapshotModal />
<SaveSnapshotBeforeExit />
{!hideProjects && <ProjectDetailDrawer showHistory={showHistory} setShowHistory={setShowHistory} />}
</>
);
});

export default withSessionManagement(withUpdatingTarget(withLoadingProtein(Preview)));
export default withSnapshotManagement(withUpdatingTarget(withLoadingProtein(Preview)));
Loading

0 comments on commit a421d1b

Please sign in to comment.