Skip to content

Commit

Permalink
Merge pull request #61 from omics-datascience/feature/institutions
Browse files Browse the repository at this point in the history
Feature/institutions
  • Loading branch information
Genarito authored Jan 13, 2025
2 parents 2e01e5e + 7d6432d commit 3d08f38
Show file tree
Hide file tree
Showing 20 changed files with 1,008 additions and 438 deletions.
22 changes: 22 additions & 0 deletions src/api_service/websocket_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,25 @@ def send_update_cluster_label_set_command(user_id: int):
'command': 'update_cluster_labels_sets'
}
send_message(user_group_name, message)

def send_update_institutions_command(user_id: int):
"""
Sends a message indicating that a Institution state update has occurred
@param user_id: Institution's user's id to send the WS message
"""
user_group_name = f'notifications_{user_id}'
message = {
'command': 'update_institutions'
}
send_message(user_group_name, message)

def send_update_user_for_institution_command(user_id: int):
"""
Sends a message indicating that a Institution_user state update has occurred
@param user_id: Institution's user's id to send the WS message
"""
user_group_name = f'notifications_{user_id}'
message = {
'command': 'update_user_for_institution'
}
send_message(user_group_name, message)
23 changes: 13 additions & 10 deletions src/frontend/static/frontend/src/components/MainNavbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ const MainNavbar = (props: MainNavbarProps) => {
<Menu.Item style={{ padding: '0 1.7rem' }}>
<Loader active inline='centered' />
</Menu.Item>
<Menu.Item style={{ padding: '0 1.7rem' }}>
<Loader active inline='centered' />
</Menu.Item>
</Menu.Menu>
}
{/* Analysis menu */}
Expand Down Expand Up @@ -173,21 +176,21 @@ const MainNavbar = (props: MainNavbarProps) => {
/>
</React.Fragment>
}

{/* Institutions panel (only for user who are admin of at least one institution) */}
{currentUser.is_institution_admin &&
<Dropdown.Item
text='Institutions'
icon='building'
as='a' href={urlInstitutions}
active={props.activeItem === 'institutions'}
/>
}
</Dropdown.Menu>
</Dropdown>
</Menu.Menu>
}

{/* Institutions */}
{currentUser &&
<Menu.Menu as='h2'>
<Menu.Item as='a' href={urlInstitutions} style={{ fontSize: '1rem' }}>
Institutions
</Menu.Item>
</Menu.Menu>

}

{/* About us */}
<Menu.Menu as='h2'>
<Menu.Item as='a' href={urlAboutUs} style={{ fontSize: '1rem' }}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { Header, Button, Modal, Table, DropdownItemProps, Icon, Confirm, Form }
import { DjangoCGDSStudy, DjangoSurvivalColumnsTupleSimple, DjangoTag, DjangoUserFile, TagType } from '../../utils/django_interfaces'
import ky, { Options } from 'ky'
import { getDjangoHeader, alertGeneralError, formatDateLocale, cleanRef, getFilenameFromSource, makeSourceAndAppend, getDefaultSource } from '../../utils/util_functions'
import { NameOfCGDSDataset, Nullable, CustomAlert, CustomAlertTypes, SourceType, OkResponse } from '../../utils/interfaces'
import { Biomarker, BiomarkerType, BiomarkerOrigin, ConfirmModal, FormBiomarkerData, MoleculesSectionData, MoleculesTypeOfSelection, SaveBiomarkerStructure, SaveMoleculeStructure, FeatureSelectionPanelData, SourceStateBiomarker, FeatureSelectionAlgorithm, FitnessFunction, FitnessFunctionParameters, BiomarkerState, AdvancedAlgorithm as AdvancedAlgorithmParameters, BBHAVersion, BiomarkerSimple, CrossValidationParameters } from './types'
import { NameOfCGDSDataset, Nullable, CustomAlert, CustomAlertTypes, SourceType, OkResponse, ConfirmModal } from '../../utils/interfaces'
import { Biomarker, BiomarkerType, BiomarkerOrigin, FormBiomarkerData, MoleculesSectionData, MoleculesTypeOfSelection, SaveBiomarkerStructure, SaveMoleculeStructure, FeatureSelectionPanelData, SourceStateBiomarker, FeatureSelectionAlgorithm, FitnessFunction, FitnessFunctionParameters, BiomarkerState, AdvancedAlgorithm as AdvancedAlgorithmParameters, BBHAVersion, BiomarkerSimple, CrossValidationParameters } from './types'
import { ManualForm } from './modalContentBiomarker/manualForm/ManualForm'
import { PaginatedTable, PaginationCustomFilter } from '../common/PaginatedTable'
import { TableCellWithTitle } from '../common/TableCellWithTitle'
Expand Down Expand Up @@ -1704,15 +1704,15 @@ export class BiomarkersPanel extends React.Component<{}, BiomarkersPanelState> {
{/* Stop button */}
{isInProcess &&
<StopExperimentButton
title='Stop experiment'
title='Stop biomarker'
onClick={() => this.setState({ biomarkerToStop: biomarker })}
/>
}

{/* Delete button */}
{!isInProcess &&
<DeleteExperimentButton
title='Delete experiment'
title='Delete biomarker'
disabled={currentBiomarkerIsLoading}
onClick={() => this.confirmBiomarkerDeletion(biomarker)}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,6 @@ interface Biomarker extends BiomarkerSimple {
mrnas: SaveMoleculeStructure[]
}

interface ConfirmModal {
confirmModal: boolean,
headerText: string,
contentText: string,
onConfirm: Function,
}

/** Represents a molecule info to show in molecules Dropdown. */
type MoleculeSymbol = {
key: string,
Expand Down Expand Up @@ -630,7 +623,6 @@ export {
MoleculesMultipleSelection,
MoleculesSectionData,
MoleculeSectionItem,
ConfirmModal,
MoleculeSymbol,
MoleculesSymbolFinder,
ClusteringScoringMethod,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import ky from 'ky'
import React, { useEffect, useState } from 'react'
import { Form, Button } from 'semantic-ui-react'
import { getDjangoHeader } from '../../utils/util_functions'
import { CustomAlertTypes, Nullable } from '../../utils/interfaces'
import { DjangoInstitution } from '../../utils/django_interfaces'

const defaultForm: {
id: undefined | number;
name: string;
location: string;
email: string;
telephone_number: string;
isLoading: boolean;
} = {
id: undefined,
name: '',
location: '',
email: '',
telephone_number: '',
isLoading: false
}

declare const urlCreateInstitution: string
declare const urlEditInstitution: string

interface Props {
institutionToEdit: Nullable<DjangoInstitution>,
handleResetInstitutionToEdit: (callbackToCancel: () => void) => void,
handleUpdateAlert(isOpen: boolean, type: CustomAlertTypes, message: string, callback: Nullable<Function>, isEdit?: boolean): void,
}

const InstitutionForm = (props: Props) => {
const [formData, setFormData] = useState(defaultForm)

/**
* Handle form state data
* @param e event
* @param param.name key name to edit field in form
* @param param.value key value to edit field in form
*/

const handleChange = (e, { name, value }) => {
setFormData({ ...formData, [name]: value })
}

/**
* Handle user form to edit or create
*/
const handleSubmit = () => {
const myHeaders = getDjangoHeader()

if (props.institutionToEdit?.id) {
const jsonParams = {
id: formData.id,
name: formData.name,
location: formData.location,
email: formData.email,
telephone_number: formData.telephone_number
}
const editUrl = `${urlEditInstitution}/${formData.id}/`

ky.patch(editUrl, { headers: myHeaders, json: jsonParams }).then((response) => {
setFormData(prevState => ({ ...prevState, isLoading: true }))
response.json().then((jsonResponse: DjangoInstitution) => {
props.handleUpdateAlert(true, CustomAlertTypes.SUCCESS, `Institution ${jsonResponse.name} Updated!`, () => setFormData(defaultForm), true)
}).catch((err) => {
setFormData(prevState => ({ ...prevState, isLoading: false }))
props.handleUpdateAlert(true, CustomAlertTypes.ERROR, 'Error creating an institution!', () => setFormData(prevState => ({ ...prevState, isLoading: false })))
console.error('Error parsing JSON ->', err)
})
}).catch((err) => {
props.handleUpdateAlert(true, CustomAlertTypes.ERROR, 'Error creating an institution!', () => setFormData(prevState => ({ ...prevState, isLoading: false })))
console.error('Error adding new Institution ->', err)
})
} else {
const jsonParams = {
name: formData.name,
location: formData.location,
email: formData.email,
telephone_number: formData.telephone_number
}
ky.post(urlCreateInstitution, { headers: myHeaders, json: jsonParams }).then((response) => {
setFormData(prevState => ({ ...prevState, isLoading: true }))
response.json().then((jsonResponse: DjangoInstitution) => {
props.handleUpdateAlert(true, CustomAlertTypes.SUCCESS, `Institution ${jsonResponse.name} created!`, () => setFormData(defaultForm))
}).catch((err) => {
props.handleUpdateAlert(true, CustomAlertTypes.ERROR, 'Error creating an institution!', () => setFormData(prevState => ({ ...prevState, isLoading: false })))
setFormData(prevState => ({ ...prevState, isLoading: false }))
console.error('Error parsing JSON ->', err)
})
}).catch((err) => {
props.handleUpdateAlert(true, CustomAlertTypes.ERROR, 'Error creating an institution!', () => setFormData(prevState => ({ ...prevState, isLoading: false })))
console.error('Error adding new Institution ->', err)
})
}
}

/**
* Handle if user Reset or cancel edit
*/
const handleCancelForm = () => {
if (props.institutionToEdit) {
props.handleResetInstitutionToEdit(() => setFormData(defaultForm))
} else {
setFormData(defaultForm)
}
}

/**
* use effect to handle if a institution for edit is sent
*/
useEffect(() => {
if (props.institutionToEdit) {
setFormData({
...props.institutionToEdit,
id: props.institutionToEdit?.id,
isLoading: false
})
}
}, [props.institutionToEdit])
return (
<>
<Form onSubmit={handleSubmit}>
<Form.Input
name="name"
value={formData.name}
onChange={handleChange}
placeholder="Institution name"
/>
<Form.Input
name="location"
value={formData.location}
onChange={handleChange}
placeholder="Institution location"
/>
<Form.Input
name="email"
type="email"
value={formData.email}
onChange={handleChange}
placeholder="Institution email"
/>
<Form.Input
name="telephone_number"
type="tel"
value={formData.telephone_number}
onChange={handleChange}
placeholder="Institution phone number"
/>
<Button
type="submit"
fluid
primary
disabled={(!formData.email.trim() && !formData.location.trim() && !formData.name.trim() && !formData.telephone_number.trim()) || formData.isLoading}
color='green'
loading={formData.isLoading}
>
{props.institutionToEdit ? 'Edit institution' : 'Create institution'}
</Button>
</Form>
<Button
className='margin-top-5'
fluid
primary
disabled={formData.isLoading}
color='red'
onClick={handleCancelForm}
>
{props.institutionToEdit ? 'Cancel edit' : 'Reset form'}
</Button>
</>
)
}

export default InstitutionForm
Loading

0 comments on commit 3d08f38

Please sign in to comment.