Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/institutions #61

Merged
merged 5 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading