├── Backend ├── __init__.py ├── accounts │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ ├── views │ │ │ └── __init__.py │ │ └── serializers │ │ │ ├── __init__.py │ │ │ ├── ProfileSerializer.py │ │ │ ├── UserProfileSerializer.py │ │ │ └── UserSerializer.py │ ├── tests │ │ ├── __init__.py │ │ ├── testProfileSerializer.py │ │ ├── testProfile.py │ │ └── testUserRecovery.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ └── cdsu.py │ ├── migrations │ │ └── __init__.py │ ├── models │ │ ├── __init__.py │ │ ├── Profile.py │ │ └── UserRecovery.py │ ├── views.py │ ├── apps.py │ ├── admin.py │ └── urls.py ├── history │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ ├── serializers │ │ │ ├── __init__.py │ │ │ ├── GenericObjectField.py │ │ │ └── HistorySerializer.py │ │ └── views │ │ │ ├── __init__.py │ │ │ ├── HistoryViewSet.py │ │ │ └── FilteredHistoryListView.py │ ├── tests │ │ └── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── models │ │ ├── __init__.py │ │ └── HistoryRelated.py │ ├── views.py │ ├── apps.py │ └── admin.py ├── patients │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ ├── views │ │ │ ├── __init__.py │ │ │ ├── ClinicalVariableViewSet.py │ │ │ ├── PatientViewSet.py │ │ │ ├── PatientCVsViewSet.py │ │ │ └── AdmissionViewSet.py │ │ └── serializers │ │ │ ├── __init__.py │ │ │ ├── CVPatientSerializer.py │ │ │ ├── ClinicalVariableSerializer.py │ │ │ ├── CVGroupSerializer.py │ │ │ ├── PatientSerializer.py │ │ │ └── AdmissionSerializer.py │ ├── tests │ │ └── __init__.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ └── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── views.py │ ├── apps.py │ ├── models │ │ ├── __init__.py │ │ ├── CVOption.py │ │ ├── CVGroup.py │ │ ├── CVPatient.py │ │ ├── Patient.py │ │ ├── ClinicalVariable.py │ │ └── Admission.py │ ├── urls.py │ └── admin.py ├── protocol │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ ├── views │ │ │ ├── __init__.py │ │ │ ├── ScheduleViewSet.py │ │ │ ├── AssignedProtocolViewSet.py │ │ │ └── ExecutedProtocolViewSet.py │ │ └── serializers │ │ │ ├── __init__.py │ │ │ ├── ScheduleSerializer.py │ │ │ ├── AssignedProtocolSerializer.py │ │ │ ├── ProtocolSerializer.py │ │ │ └── ExecutedProtocolSerializer.py │ ├── tests │ │ └── __init__.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ ├── cleanAllProtocols.py │ │ │ ├── diabeticInpatients.py │ │ │ ├── surgicalDiabeticInpatient.py │ │ │ └── continuousIntravenousInfusion.py │ ├── migrations │ │ └── __init__.py │ ├── models │ │ ├── __init__.py │ │ ├── Time.py │ │ ├── Schedule.py │ │ └── AssignedProtocol.py │ ├── views.py │ ├── apps.py │ ├── urls.py │ └── admin.py ├── utils │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ └── views │ │ │ ├── LanguageView.py │ │ │ ├── __init__.py │ │ │ ├── FlatPagesView.py │ │ │ └── ConstanceView.py │ ├── tests │ │ └── __init__.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ └── populate_flatpages.py │ ├── migrations │ │ └── __init__.py │ ├── models │ │ ├── __init__.py │ │ ├── Language.py │ │ └── Multilingual.py │ ├── views.py │ ├── apps.py │ ├── time.py │ ├── hashes.py │ ├── urls.py │ └── admin.py ├── genericcdss │ ├── __init__.py │ ├── wsgi.py │ └── urls.py ├── protocol_element │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ └── serializers │ │ │ ├── __init__.py │ │ │ ├── PENextElementsSerializer.py │ │ │ ├── PEActionSerializer.py │ │ │ ├── ProtocolElementSerializer.py │ │ │ ├── PEInquirySerializer.py │ │ │ ├── PEDecisionSerializer.py │ │ │ └── PolymorphicSerializer.py │ ├── tests │ │ └── __init__.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ └── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── admin.py │ ├── views.py │ ├── apps.py │ └── models │ │ ├── __init__.py │ │ ├── PENextElements.py │ │ ├── PEInquiry.py │ │ ├── PEAction.py │ │ └── PEDecision.py └── manage.py ├── UI ├── public │ ├── favicon.ico │ ├── images │ │ ├── logo.png │ │ ├── logo_small.png │ │ └── home_wallpaper.jpg │ └── index.html ├── src │ ├── css │ │ ├── externalImportsConfigs.css │ │ ├── imports.css │ │ ├── Table.css │ │ ├── LoginComponent.css │ │ ├── Select.css │ │ └── Texts.css │ ├── externalImports │ │ └── mfglabs-iconset │ │ │ ├── img │ │ │ └── mfg_logo_r.jpg │ │ │ ├── css │ │ │ └── font │ │ │ │ ├── gibson-webfont.eot │ │ │ │ ├── gibson-webfont.ttf │ │ │ │ ├── gibson-webfont.woff │ │ │ │ ├── mfglabsiconset-webfont.eot │ │ │ │ ├── mfglabsiconset-webfont.ttf │ │ │ │ ├── gibson-semibold-webfont.eot │ │ │ │ ├── gibson-semibold-webfont.ttf │ │ │ │ ├── gibson-semibold-webfont.woff │ │ │ │ ├── mfglabsiconset-webfont.woff │ │ │ │ ├── gibson-light-italic-webfont.eot │ │ │ │ ├── gibson-light-italic-webfont.ttf │ │ │ │ └── gibson-light-italic-webfont.woff │ │ │ ├── bower.json │ │ │ └── README.md │ ├── js │ │ ├── components │ │ │ ├── globalComponents │ │ │ │ ├── History.js │ │ │ │ ├── Language.js │ │ │ │ ├── Footer.js │ │ │ │ ├── LoginButton.js │ │ │ │ ├── Header.js │ │ │ │ ├── LoginComponent.js │ │ │ │ └── Routes.js │ │ │ ├── accountManager │ │ │ │ ├── Register.js │ │ │ │ └── ForgotPass.js │ │ │ ├── errorPages │ │ │ │ ├── http500.js │ │ │ │ ├── http404.js │ │ │ │ └── http0.js │ │ │ ├── patient │ │ │ │ ├── PatientStatus.js │ │ │ │ ├── ShowPatient.js │ │ │ │ ├── PatientComplementInfo.js │ │ │ │ ├── AddPatient.js │ │ │ │ ├── ClinicalVariables.js │ │ │ │ ├── AllPatients.js │ │ │ │ └── AdmittedPatients.js │ │ │ ├── protocol │ │ │ │ ├── ProtocolType.js │ │ │ │ ├── ProtocolCostumization.js │ │ │ │ ├── ExecutedProtocols.js │ │ │ │ ├── AssignedProtocols.js │ │ │ │ ├── Schedules.js │ │ │ │ ├── Protocols.js │ │ │ │ └── SelectedProtocols.js │ │ │ ├── reusable │ │ │ │ ├── MyLink.js │ │ │ │ └── DisplayField.js │ │ │ ├── dynamicPages │ │ │ │ ├── Help.js │ │ │ │ ├── About.js │ │ │ │ └── Home.js │ │ │ ├── buttons │ │ │ │ └── PatientButtonBar.js │ │ │ └── protocolElements │ │ │ │ └── ActionElement.js │ │ ├── GlobalSettings.js │ │ ├── reflux │ │ │ ├── ScheduleReflux.js │ │ │ ├── ClinicalVariablesReflux.js │ │ │ ├── PatientClinicalVariablesReflux.js │ │ │ ├── AdmissionReflux.js │ │ │ ├── StateReflux.js │ │ │ ├── PatientReflux.js │ │ │ └── UserReflux.js │ │ └── App.js │ └── index.js └── package.json ├── Dockerfile ├── .gitignore ├── config ├── requirements.pip ├── nginx │ └── genericcdss.conf └── run_docker.sh ├── docker-compose.yml ├── Makefile └── README.md /Backend/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/accounts/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/history/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/patients/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/protocol/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/accounts/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/genericcdss/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/history/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/history/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/patients/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/protocol/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/utils/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/utils/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/accounts/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/history/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/patients/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/protocol/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/protocol_element/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/utils/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/utils/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/accounts/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/accounts/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/patients/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/patients/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/protocol/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/protocol/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/protocol_element/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/protocol_element/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/utils/api/views/LanguageView.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/accounts/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/patients/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/protocol/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/protocol_element/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/protocol_element/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/utils/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/protocol_element/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/accounts/api/views/__init__.py: -------------------------------------------------------------------------------- 1 | from .UserViewSet import UserViewSet -------------------------------------------------------------------------------- /Backend/accounts/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .Profile import Profile 2 | from .UserRecovery import UserRecovery -------------------------------------------------------------------------------- /Backend/utils/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .Language import Language 2 | from .Multilingual import Multilingual -------------------------------------------------------------------------------- /UI/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bioinformatics-ua/GenericCDSS/HEAD/UI/public/favicon.ico -------------------------------------------------------------------------------- /Backend/history/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .HistoryRelated import HistoryRelated 2 | from .History import History -------------------------------------------------------------------------------- /UI/public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bioinformatics-ua/GenericCDSS/HEAD/UI/public/images/logo.png -------------------------------------------------------------------------------- /Backend/utils/api/views/__init__.py: -------------------------------------------------------------------------------- 1 | from .ConstanceView import ConstanceView 2 | from .FlatPagesView import FlatPagesView -------------------------------------------------------------------------------- /UI/public/images/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bioinformatics-ua/GenericCDSS/HEAD/UI/public/images/logo_small.png -------------------------------------------------------------------------------- /UI/public/images/home_wallpaper.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bioinformatics-ua/GenericCDSS/HEAD/UI/public/images/home_wallpaper.jpg -------------------------------------------------------------------------------- /UI/src/css/externalImportsConfigs.css: -------------------------------------------------------------------------------- 1 | .icon-user_male{ 2 | color: #007bff; 3 | } 4 | 5 | .icon-user_female{ 6 | color: #f4b5d8; 7 | } -------------------------------------------------------------------------------- /Backend/history/api/serializers/__init__.py: -------------------------------------------------------------------------------- 1 | from .GenericObjectField import GenericObjectField 2 | from .HistorySerializer import HistorySerializer -------------------------------------------------------------------------------- /Backend/history/api/views/__init__.py: -------------------------------------------------------------------------------- 1 | from .HistoryViewSet import HistoryViewSet 2 | from .FilteredHistoryListView import FilteredHistoryListView -------------------------------------------------------------------------------- /Backend/accounts/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.shortcuts import render 5 | 6 | # Create your views here. 7 | -------------------------------------------------------------------------------- /Backend/history/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.shortcuts import render 5 | 6 | # Create your views here. 7 | -------------------------------------------------------------------------------- /Backend/patients/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.shortcuts import render 5 | 6 | # Create your views here. 7 | -------------------------------------------------------------------------------- /Backend/protocol/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .Time import Time 2 | from .Schedule import Schedule 3 | from .Protocol import Protocol 4 | from .ExecutedProtocol import ExecutedProtocol -------------------------------------------------------------------------------- /Backend/protocol/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.shortcuts import render 5 | 6 | # Create your views here. 7 | -------------------------------------------------------------------------------- /Backend/utils/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.shortcuts import render 5 | 6 | # Create your views here. 7 | -------------------------------------------------------------------------------- /UI/src/externalImports/mfglabs-iconset/img/mfg_logo_r.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bioinformatics-ua/GenericCDSS/HEAD/UI/src/externalImports/mfglabs-iconset/img/mfg_logo_r.jpg -------------------------------------------------------------------------------- /Backend/protocol_element/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.contrib import admin 5 | 6 | # Register your models here. 7 | -------------------------------------------------------------------------------- /Backend/protocol_element/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.shortcuts import render 5 | 6 | # Create your views here. 7 | -------------------------------------------------------------------------------- /UI/src/externalImports/mfglabs-iconset/css/font/gibson-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bioinformatics-ua/GenericCDSS/HEAD/UI/src/externalImports/mfglabs-iconset/css/font/gibson-webfont.eot -------------------------------------------------------------------------------- /UI/src/externalImports/mfglabs-iconset/css/font/gibson-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bioinformatics-ua/GenericCDSS/HEAD/UI/src/externalImports/mfglabs-iconset/css/font/gibson-webfont.ttf -------------------------------------------------------------------------------- /Backend/accounts/api/serializers/__init__.py: -------------------------------------------------------------------------------- 1 | from .ProfileSerializer import ProfileSerializer 2 | from .UserSerializer import UserSerializer 3 | from .UserProfileSerializer import UserProfileSerializer -------------------------------------------------------------------------------- /Backend/utils/apps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.apps import AppConfig 5 | 6 | 7 | class UtilsConfig(AppConfig): 8 | name = 'utils' 9 | -------------------------------------------------------------------------------- /UI/src/externalImports/mfglabs-iconset/css/font/gibson-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bioinformatics-ua/GenericCDSS/HEAD/UI/src/externalImports/mfglabs-iconset/css/font/gibson-webfont.woff -------------------------------------------------------------------------------- /Backend/accounts/apps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.apps import AppConfig 5 | 6 | 7 | class AccountsConfig(AppConfig): 8 | name = 'accounts' 9 | -------------------------------------------------------------------------------- /Backend/history/apps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.apps import AppConfig 5 | 6 | 7 | class HistoryConfig(AppConfig): 8 | name = 'history' 9 | -------------------------------------------------------------------------------- /Backend/patients/apps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.apps import AppConfig 5 | 6 | 7 | class PatientsConfig(AppConfig): 8 | name = 'patients' 9 | -------------------------------------------------------------------------------- /Backend/protocol/apps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.apps import AppConfig 5 | 6 | 7 | class ProtocolConfig(AppConfig): 8 | name = 'protocol' 9 | -------------------------------------------------------------------------------- /UI/src/externalImports/mfglabs-iconset/css/font/mfglabsiconset-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bioinformatics-ua/GenericCDSS/HEAD/UI/src/externalImports/mfglabs-iconset/css/font/mfglabsiconset-webfont.eot -------------------------------------------------------------------------------- /UI/src/externalImports/mfglabs-iconset/css/font/mfglabsiconset-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bioinformatics-ua/GenericCDSS/HEAD/UI/src/externalImports/mfglabs-iconset/css/font/mfglabsiconset-webfont.ttf -------------------------------------------------------------------------------- /UI/src/externalImports/mfglabs-iconset/css/font/gibson-semibold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bioinformatics-ua/GenericCDSS/HEAD/UI/src/externalImports/mfglabs-iconset/css/font/gibson-semibold-webfont.eot -------------------------------------------------------------------------------- /UI/src/externalImports/mfglabs-iconset/css/font/gibson-semibold-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bioinformatics-ua/GenericCDSS/HEAD/UI/src/externalImports/mfglabs-iconset/css/font/gibson-semibold-webfont.ttf -------------------------------------------------------------------------------- /UI/src/externalImports/mfglabs-iconset/css/font/gibson-semibold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bioinformatics-ua/GenericCDSS/HEAD/UI/src/externalImports/mfglabs-iconset/css/font/gibson-semibold-webfont.woff -------------------------------------------------------------------------------- /UI/src/externalImports/mfglabs-iconset/css/font/mfglabsiconset-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bioinformatics-ua/GenericCDSS/HEAD/UI/src/externalImports/mfglabs-iconset/css/font/mfglabsiconset-webfont.woff -------------------------------------------------------------------------------- /UI/src/externalImports/mfglabs-iconset/css/font/gibson-light-italic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bioinformatics-ua/GenericCDSS/HEAD/UI/src/externalImports/mfglabs-iconset/css/font/gibson-light-italic-webfont.eot -------------------------------------------------------------------------------- /UI/src/externalImports/mfglabs-iconset/css/font/gibson-light-italic-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bioinformatics-ua/GenericCDSS/HEAD/UI/src/externalImports/mfglabs-iconset/css/font/gibson-light-italic-webfont.ttf -------------------------------------------------------------------------------- /UI/src/externalImports/mfglabs-iconset/css/font/gibson-light-italic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bioinformatics-ua/GenericCDSS/HEAD/UI/src/externalImports/mfglabs-iconset/css/font/gibson-light-italic-webfont.woff -------------------------------------------------------------------------------- /Backend/protocol_element/apps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.apps import AppConfig 5 | 6 | 7 | class ProtocolElementConfig(AppConfig): 8 | name = 'protocol_element' 9 | -------------------------------------------------------------------------------- /UI/src/js/components/globalComponents/History.js: -------------------------------------------------------------------------------- 1 | import createBrowserHistory from 'history/createBrowserHistory'; 2 | import {base_url} from '../../../../package.json'; 3 | 4 | export default createBrowserHistory({ basename: base_url }); -------------------------------------------------------------------------------- /Backend/utils/time.py: -------------------------------------------------------------------------------- 1 | from django.utils import timezone 2 | 3 | def nextMonth(): 4 | return timezone.now() + timezone.timedelta(days=30) 5 | 6 | def sessionExpiringTime(): 7 | return 1296000 # if set to remember, keep for 2 weeks -------------------------------------------------------------------------------- /Backend/protocol_element/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .ProtocolElement import ProtocolElement 2 | from .PENextElements import PENextElements 3 | from .PEAction import PEAction 4 | from .PEInquiry import PEInquiry 5 | from .PEDecision import PEDecision -------------------------------------------------------------------------------- /Backend/patients/api/views/__init__.py: -------------------------------------------------------------------------------- 1 | from .PatientViewSet import PatientViewSet 2 | from .PatientCVsViewSet import PatientCVsViewSet 3 | from .AdmissionViewSet import AdmissionViewSet 4 | from .ClinicalVariableViewSet import ClinicalVariableViewSet -------------------------------------------------------------------------------- /Backend/patients/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .CVGroup import CVGroup 2 | from .ClinicalVariable import ClinicalVariable 3 | from .CVOption import CVOption 4 | from .Patient import Patient 5 | from .CVPatient import CVPatient 6 | from .Admission import Admission 7 | -------------------------------------------------------------------------------- /Backend/protocol/api/views/__init__.py: -------------------------------------------------------------------------------- 1 | from .AssignedProtocolViewSet import AssignedProtocolViewSet 2 | from .ExecutedProtocolViewSet import ExecutedProtocolViewSet 3 | from .ProtocolViewSet import ProtocolViewSet 4 | from .ScheduleViewSet import ScheduleViewSet 5 | -------------------------------------------------------------------------------- /UI/src/externalImports/mfglabs-iconset/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mfglabs-iconset", 3 | "homepage": "https://github.com/MfgLabs/mfglabs-iconset", 4 | "description": "Awesome web font icon by MFG Labs", 5 | "main": "css/mfglabs_iconset.css", 6 | "license": "CC BY-SA" 7 | } 8 | -------------------------------------------------------------------------------- /Backend/protocol/api/serializers/__init__.py: -------------------------------------------------------------------------------- 1 | from .AssignedProtocolSerializer import AssignedProtocolSerializer 2 | from .ExecutedProtocolSerializer import ExecutedProtocolSerializer 3 | from .ScheduleSerializer import ScheduleSerializer 4 | from .ProtocolSerializer import ProtocolSerializer -------------------------------------------------------------------------------- /Backend/utils/hashes.py: -------------------------------------------------------------------------------- 1 | from hashids import Hashids 2 | import uuid 3 | 4 | def createHash(identificator): 5 | hashids = Hashids(salt="esh2YTBZesh2YTBZ", min_length=5) 6 | 7 | return hashids.encrypt(identificator) 8 | 9 | 10 | def createUUID(): 11 | return uuid.uuid1().hex 12 | -------------------------------------------------------------------------------- /Backend/protocol/models/Time.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models 5 | 6 | class Time(models.Model): 7 | time = models.TimeField() 8 | 9 | def __unicode__(self): 10 | return self.time.strftime("%H:%M") -------------------------------------------------------------------------------- /Backend/utils/models/Language.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models 5 | 6 | class Language(models.Model): 7 | language = models.CharField(max_length=2) 8 | 9 | def __unicode__(self): 10 | return u"%s" % self.language -------------------------------------------------------------------------------- /Backend/patients/api/serializers/__init__.py: -------------------------------------------------------------------------------- 1 | from .PatientSerializer import PatientSerializer 2 | from .AdmissionSerializer import AdmissionSerializer 3 | from .CVPatientSerializer import CVPatientSerializer 4 | from .ClinicalVariableSerializer import ClinicalVariableSerializer 5 | from .CVGroupSerializer import CVGroupSerializer -------------------------------------------------------------------------------- /UI/src/js/components/accountManager/Register.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | 3 | class Register extends Component { 4 | render() { 5 | return ( 6 |
7 | Register and login (BUILDING) 8 |
9 | ) 10 | } 11 | } 12 | 13 | export default Register; -------------------------------------------------------------------------------- /UI/src/js/components/accountManager/ForgotPass.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | 3 | class ForgotPassword extends Component { 4 | render() { 5 | return ( 6 |
7 | Forgot password (BUILDING) 8 |
9 | ) 10 | } 11 | } 12 | 13 | export default ForgotPassword; -------------------------------------------------------------------------------- /Backend/accounts/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.contrib import admin 5 | from models import Profile, UserRecovery 6 | 7 | @admin.register(Profile) 8 | class ProfileAdmin(admin.ModelAdmin): 9 | pass 10 | 11 | @admin.register(UserRecovery) 12 | class RecoveryAdmin(admin.ModelAdmin): 13 | pass 14 | -------------------------------------------------------------------------------- /Backend/history/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.contrib import admin 5 | 6 | 7 | from models import History, HistoryRelated 8 | 9 | @admin.register(History) 10 | class HistoryAdmin(admin.ModelAdmin): 11 | pass 12 | 13 | @admin.register(HistoryRelated) 14 | class HistoryRelatedAdmin(admin.ModelAdmin): 15 | pass 16 | -------------------------------------------------------------------------------- /UI/src/css/imports.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Other CSS project files 3 | */ 4 | @import url("app.css"); 5 | @import url("LoginComponent.css"); 6 | @import url("Table.css"); 7 | @import url("Texts.css"); 8 | @import url("Select.css"); 9 | @import url("externalImportsConfigs.css"); 10 | 11 | /* 12 | * External libs 13 | */ 14 | @import url("../externalImports/mfglabs-iconset/css/mfglabs_iconset.css"); 15 | -------------------------------------------------------------------------------- /UI/src/js/GlobalSettings.js: -------------------------------------------------------------------------------- 1 | 2 | const getPatientTableRows = function(extraSize=0) { 3 | let headerSize = 65; 4 | let footerSize = 70 ; 5 | let tableHeaderSize = 42 + 29 + 39 + 47; 6 | let rowSize = 35; 7 | 8 | return Math.floor((window.innerHeight - headerSize - footerSize - tableHeaderSize - extraSize)/rowSize); 9 | }; 10 | 11 | 12 | export default {getPatientTableRows}; -------------------------------------------------------------------------------- /Backend/accounts/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.conf.urls import include, url 5 | from rest_framework import routers 6 | 7 | from api.views import UserViewSet 8 | 9 | router = routers.DefaultRouter() 10 | router.register(r'', UserViewSet, base_name='userview') 11 | 12 | urlpatterns = [ 13 | url(r'^', include(router.urls)) 14 | ] 15 | -------------------------------------------------------------------------------- /Backend/protocol_element/api/serializers/__init__.py: -------------------------------------------------------------------------------- 1 | from .PolymorphicSerializer import PolymorphicSerializer 2 | from .PENextElementsSerializer import PENextElementsSerializer 3 | from .PEActionSerializer import PEActionSerializer 4 | from .PEInquirySerializer import PEInquirySerializer 5 | from .PEDecisionSerializer import PEDecisionSerializer 6 | from .ProtocolElementSerializer import ProtocolElementSerializer -------------------------------------------------------------------------------- /UI/src/js/components/errorPages/http500.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | 3 | export default class httpError extends Component { 4 | render() { 5 | return ( 6 |
7 |

500 - Service Error

8 |

Oops, we appear to be having problems. Please contact the administrator.

9 |
10 | ) 11 | } 12 | } -------------------------------------------------------------------------------- /Backend/patients/api/views/ClinicalVariableViewSet.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import viewsets, filters 5 | 6 | from patients.api.serializers import ClinicalVariableSerializer 7 | from patients.models import ClinicalVariable 8 | 9 | class ClinicalVariableViewSet(viewsets.ModelViewSet): 10 | queryset = ClinicalVariable.all() 11 | serializer_class = ClinicalVariableSerializer -------------------------------------------------------------------------------- /UI/src/js/components/errorPages/http404.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | 3 | export default class httpError extends Component { 4 | render() { 5 | return ( 6 |
7 |

404 - Page not found

8 |

The page does not seem exist, if you think this is a mistake, please contact the administrator.

9 |
10 | ) 11 | } 12 | } -------------------------------------------------------------------------------- /Backend/accounts/api/serializers/ProfileSerializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import serializers 5 | from rest_framework import permissions 6 | 7 | from accounts.models import Profile 8 | 9 | class ProfileSerializer(serializers.ModelSerializer): 10 | class Meta: 11 | permission_classes = [permissions.IsAuthenticated] 12 | model = Profile 13 | exclude = ['id'] -------------------------------------------------------------------------------- /UI/src/js/components/patient/PatientStatus.js: -------------------------------------------------------------------------------- 1 | const status = { 2 | ADMITTED:1, 3 | DISCHARGED: 2 4 | }; 5 | 6 | status.get = function(value){ 7 | return Object.keys(status).find(key => status[key] === value); 8 | }; 9 | 10 | status.toString = function(value){ 11 | switch (value) 12 | { 13 | case 1: return "Admitted"; 14 | case 2: return ""; 15 | default: return ""; 16 | } 17 | }; 18 | 19 | export default status; -------------------------------------------------------------------------------- /Backend/protocol/api/serializers/ScheduleSerializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import serializers 5 | from rest_framework import permissions 6 | 7 | from protocol.models import Schedule 8 | 9 | 10 | class ScheduleSerializer(serializers.ModelSerializer): 11 | class Meta: 12 | permission_classes = [permissions.IsAuthenticated] 13 | model = Schedule 14 | fields = ("id", "title") -------------------------------------------------------------------------------- /Backend/utils/models/Multilingual.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models 5 | from utils.models import Language 6 | 7 | class Multilingual(models.Model): 8 | key = models.CharField(max_length=150) 9 | content = models.CharField(max_length=500) 10 | language = models.ForeignKey(Language) 11 | 12 | def __unicode__(self): 13 | return u"%s - %s" % (self.key, self.language) -------------------------------------------------------------------------------- /UI/src/js/components/protocol/ProtocolType.js: -------------------------------------------------------------------------------- 1 | const type = { 2 | SIMPLE:1, 3 | COMPLEX: 2 4 | }; 5 | 6 | type.get = function(value){ 7 | return Object.keys(status).find(key => status[key] === value); 8 | }; 9 | 10 | type.toString = function(value){ 11 | switch (value) 12 | { 13 | case 1: return "Simple execution"; 14 | case 2: return "Form execution"; 15 | default: return ""; 16 | } 17 | }; 18 | 19 | export default type; -------------------------------------------------------------------------------- /Backend/genericcdss/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for genericcdss project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "genericcdss.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /Backend/protocol_element/api/serializers/PENextElementsSerializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import serializers 5 | from rest_framework import permissions 6 | 7 | from protocol_element.models import PENextElements 8 | 9 | class PENextElementsSerializer(serializers.ModelSerializer): 10 | class Meta: 11 | permission_classes = [permissions.IsAuthenticated] 12 | model = PENextElements 13 | exclude = ['id'] -------------------------------------------------------------------------------- /Backend/patients/api/serializers/CVPatientSerializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import serializers 5 | from rest_framework import permissions 6 | 7 | from patients.models import CVPatient 8 | 9 | class CVPatientSerializer(serializers.ModelSerializer): 10 | class Meta: 11 | permission_classes = [permissions.IsAuthenticated] 12 | model = CVPatient 13 | fields = '__all__' 14 | read_only_fields = ('id',) 15 | 16 | -------------------------------------------------------------------------------- /Backend/accounts/api/serializers/UserProfileSerializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import serializers 5 | from rest_framework import permissions 6 | 7 | from accounts.api.serializers import UserSerializer 8 | 9 | from accounts.models import Profile 10 | 11 | class UserProfileSerializer(serializers.ModelSerializer): 12 | user = UserSerializer() 13 | class Meta: 14 | permission_classes = [permissions.IsAuthenticated] 15 | model = Profile 16 | exclude = ['id'] -------------------------------------------------------------------------------- /Backend/utils/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.conf.urls import include, url 5 | 6 | from utils.api.views import ConstanceView, FlatPagesView 7 | 8 | urlpatterns = [ 9 | url(r'^settings', ConstanceView.as_view({'get':'getSettings'}), name='settingsview'), 10 | url(r'^about', FlatPagesView.as_view({'get':'getAbout'}), name='aboutview'), 11 | url(r'^help', FlatPagesView.as_view({'get':'getHelp'}), name='helpview'), 12 | url(r'^home', FlatPagesView.as_view({'get':'getHome'}), name='homeview'), 13 | ] -------------------------------------------------------------------------------- /Backend/protocol/api/views/ScheduleViewSet.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import viewsets, filters 5 | 6 | from rest_framework.filters import OrderingFilter 7 | from django_filters.rest_framework import DjangoFilterBackend 8 | 9 | from protocol.api.serializers import ScheduleSerializer 10 | from protocol.models import Schedule 11 | 12 | from history.models import History 13 | 14 | class ScheduleViewSet(viewsets.ModelViewSet): 15 | queryset = Schedule.objects.all() 16 | serializer_class = ScheduleSerializer 17 | -------------------------------------------------------------------------------- /Backend/patients/models/CVOption.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models 5 | 6 | from patients.models import ClinicalVariable 7 | 8 | class CVOption(models.Model): 9 | variable = models.ForeignKey(ClinicalVariable) 10 | option = models.CharField(max_length=30) 11 | 12 | @staticmethod 13 | def getOptions(variable=None): 14 | tmpAll = [] 15 | for optionObj in CVOption.objects.filter(variable=variable): 16 | tmpAll += [optionObj.option] 17 | return tmpAll -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:2.7 2 | MAINTAINER Joao Almeida 3 | 4 | ADD ./Backend /GenericCDSS/Backend 5 | ADD ./UI /GenericCDSS/UI 6 | ADD ./Makefile /GenericCDSS/Makefile 7 | ADD ./config /GenericCDSS/config 8 | 9 | RUN apt-get update && \ 10 | apt-get install -y -q jq vim curl nginx uwsgi-plugin-python 11 | 12 | RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - 13 | 14 | RUN apt-get install -y nodejs 15 | 16 | WORKDIR /GenericCDSS 17 | 18 | RUN cd UI/ && npm install 19 | 20 | RUN mkdir -p /var/log/gunicorn 21 | 22 | RUN pip install -r ./config/requirements.pip --no-cache-dir 23 | -------------------------------------------------------------------------------- /UI/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | 5 | import App from './js/App.js'; 6 | import './css/imports.css'; 7 | 8 | import 'font-awesome/css/font-awesome.min.css'; 9 | import 'react-select/dist/react-select.css'; 10 | import 'semantic-ui-css/semantic.min.css'; 11 | import 'rc-tabs/assets/index.css'; 12 | import 'bootstrap/dist/css/bootstrap.min.css'; 13 | import 'bootstrap/dist/js/bootstrap.bundle.js'; 14 | 15 | ReactDOM.render( 16 | 17 | 18 | , 19 | document.getElementById('root') 20 | ); 21 | -------------------------------------------------------------------------------- /UI/src/js/components/errorPages/http0.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | 3 | export default class httpError extends Component { 4 | render() { 5 | return ( 6 |
7 |

Failed connection

8 |

The connection does not seem to be working.

9 |

It is possible there is no Internet connection, or the web page is currently offline.

10 |

If you believe this page being shown is caused by a system error, please contact the 11 | administrator.

12 |
13 | ) 14 | } 15 | } -------------------------------------------------------------------------------- /UI/src/js/components/reusable/MyLink.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {Link} from "react-router-dom"; 3 | 4 | class MyLink extends Component { 5 | render() { 6 | return ( 7 | 8 | 9 | { 10 | this.props.bold ? 11 |  {this.props.label} 12 | : 13 |  {this.props.label} 14 | } 15 | ); 16 | } 17 | } 18 | 19 | export default MyLink; -------------------------------------------------------------------------------- /Backend/history/api/serializers/GenericObjectField.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import serializers 5 | 6 | class GenericObjectField(serializers.RelatedField): 7 | ''' 8 | A custom field to use for the `object` generic relationship. 9 | ''' 10 | 11 | def to_representation(self, value): 12 | ''' 13 | Serialize objects to a simple textual representation. 14 | ''' 15 | try: 16 | return value.rpr() 17 | except: 18 | try: 19 | return str(value.hash) 20 | except: 21 | return "DUMMY" 22 | -------------------------------------------------------------------------------- /Backend/protocol/models/Schedule.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models 5 | 6 | from protocol.models import Time 7 | 8 | class Schedule(models.Model): 9 | title = models.CharField(max_length=150, unique=True) 10 | time = models.ManyToManyField(Time) 11 | removed = models.BooleanField(default=False) 12 | 13 | def __unicode__(self): 14 | return self.title 15 | 16 | def getAllScheduleTimes(self): 17 | allPossibleTimes = [] 18 | for time in self.time.all(): 19 | allPossibleTimes += [(time, self.title)] 20 | return allPossibleTimes -------------------------------------------------------------------------------- /UI/src/js/components/globalComponents/Language.js: -------------------------------------------------------------------------------- 1 | //import React, {Component} from 'react'; 2 | //import API from '../../API.js'; 3 | 4 | 5 | let Language = {}; 6 | /* DOING 7 | API.GET("language") 8 | .then(res => { 9 | Language = res.data["language"]; 10 | }); 11 | 12 | /* 13 | Language = { 14 | // English 15 | "en": { 16 | 'welcome': 'Welcome', 17 | 'description': 'This app demonstrates how to easily use a multilanguage mechanism with Office UI Fabric', 18 | } 19 | , 20 | // Deutsch 21 | "de": { 22 | 'welcome': 'Willkommen', 23 | 'description': 'Diese App demonstriert, wie man leicht einen mehrsprachigen Mechanismus mit Office UI Fabric verwendet', 24 | } 25 | }; 26 | */ 27 | 28 | export default Language; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .idea/* 3 | UI/node_modules/* 4 | env/* 5 | *.pyc 6 | 7 | #Put this files in somewhere private 8 | !docker-compose.yml 9 | 10 | #Ignore migrations 11 | Backend/accounts/migrations/* 12 | Backend/history/migrations/* 13 | Backend/patients/migrations/* 14 | Backend/protocol/migrations/* 15 | Backend/protocol_element/migrations/* 16 | Backend/utils/migrations/* 17 | 18 | #Don't ignore migrations the __init__.py 19 | !Backend/accounts/migrations/__init__.py 20 | !Backend/history/migrations/__init__.py 21 | !Backend/patients/migrations/__init__.py 22 | !Backend/protocol/migrations/__init__.py 23 | !Backend/protocol_element/migrations/__init__.py 24 | !Backend/utils/migrations/__init__.py 25 | 26 | UI/package-lock.json 27 | -------------------------------------------------------------------------------- /UI/src/css/Table.css: -------------------------------------------------------------------------------- 1 | .card-heading{ 2 | height: 40px; 3 | justify-content: center; 4 | align-items: center; 5 | position:relative; 6 | } 7 | 8 | .card-title { 9 | margin-top: 0; 10 | margin-bottom: 0; 11 | font-size: 16px; 12 | color: inherit; 13 | } 14 | 15 | .card-content{ 16 | cursor: pointer; 17 | margin: 0; 18 | } 19 | 20 | .h3-table { 21 | display: inline-block; 22 | position: absolute; 23 | left:0; 24 | right:0; 25 | font-weight: bold; 26 | font-size: 18px; 27 | } 28 | 29 | .h5-table{ 30 | font-weight: bold; 31 | font-size: 16px; 32 | } 33 | 34 | .table-button{ 35 | margin-left: .3em; 36 | position: absolute; 37 | right: 10px; 38 | top: 8px; 39 | } 40 | -------------------------------------------------------------------------------- /Backend/protocol/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.conf.urls import include, url 5 | from rest_framework import routers 6 | 7 | from api.views import ProtocolViewSet, ScheduleViewSet, AssignedProtocolViewSet, ExecutedProtocolViewSet 8 | 9 | router = routers.DefaultRouter() 10 | router.register(r'protocol', ProtocolViewSet, base_name='protocolview') 11 | router.register(r'schedule', ScheduleViewSet, base_name='scheduleview') 12 | router.register(r'assignedprotocols', AssignedProtocolViewSet, base_name='assignedprotocolsview') 13 | router.register(r'executedprotocols', ExecutedProtocolViewSet, base_name='executedprotocolsview') 14 | 15 | urlpatterns = [ 16 | url(r'^', include(router.urls)) 17 | ] 18 | -------------------------------------------------------------------------------- /Backend/patients/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.conf.urls import include, url 5 | from rest_framework import routers 6 | 7 | from api.views import PatientViewSet, PatientCVsViewSet, AdmissionViewSet, ClinicalVariableViewSet 8 | 9 | router = routers.DefaultRouter() 10 | router.register(r'patient', PatientViewSet, base_name='patientview') 11 | router.register(r'admission', AdmissionViewSet, base_name='admissionview') 12 | router.register(r'patientclinicalvariables', PatientCVsViewSet, base_name='patientclinicalvariablesview') 13 | router.register(r'clinicalvariables', ClinicalVariableViewSet, base_name='clinicalvariablesview') 14 | 15 | urlpatterns = [ 16 | url(r'^', include(router.urls)) 17 | ] 18 | -------------------------------------------------------------------------------- /config/requirements.pip: -------------------------------------------------------------------------------- 1 | Django==1.11.10 2 | certifi==2018.1.18 3 | chardet==3.0.4 4 | coreapi==2.3.3 5 | coreschema==0.0.4 6 | coverage==4.5.1 7 | django-constance==2.1.0 8 | django-cors-middleware==1.3.1 9 | django-filter==1.1.0 10 | django-jet==1.0.7 11 | django-model-utils==3.1.2 12 | django-oauth-toolkit==1.0.0 13 | django-picklefield==1.0.0 14 | django-rest-swagger==2.1.2 15 | djangorestframework==3.7.7 16 | enum34==1.1.6 17 | gunicorn==19.6.0 18 | hashids==1.2.0 19 | idna==2.6 20 | itypes==1.1.0 21 | Jinja2==2.10 22 | MarkupSafe==1.0 23 | oauthlib==2.0.6 24 | openapi-codec==1.3.2 25 | psycopg2==2.7.4 26 | psycopg2-binary==2.7.4 27 | python-dateutil==2.7.3 28 | pytz==2018.3 29 | requests==2.18.4 30 | simplejson==3.13.2 31 | six==1.11.0 32 | uritemplate==3.0.0 33 | urllib3==1.22 34 | -------------------------------------------------------------------------------- /Backend/protocol_element/models/PENextElements.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models 5 | 6 | from protocol_element.models import ProtocolElement 7 | 8 | class PENextElements(models.Model): 9 | option = models.CharField(max_length=50, null=True) 10 | nextElement = models.ForeignKey(ProtocolElement) 11 | 12 | def getNextElementId(self): 13 | return self.nextElement.internalId 14 | 15 | @staticmethod 16 | def new(nextElementId, protocol, option=None): 17 | pe = ProtocolElement.get(internalId=nextElementId, protocol=protocol) 18 | nextElement = PENextElements.objects.create(option=option, nextElement=pe) 19 | nextElement.save() 20 | return nextElement -------------------------------------------------------------------------------- /Backend/utils/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.contrib import admin 5 | from django.utils.html import format_html 6 | from django.core.urlresolvers import reverse 7 | 8 | from models import Language, Multilingual 9 | 10 | # Register your models here. 11 | @admin.register(Language) 12 | class LanguageAdmin(admin.ModelAdmin): 13 | list_display = ("language", "ckeck_translations") 14 | 15 | def ckeck_translations(self, obj): 16 | return format_html( 17 | 'Check', 18 | None#reverse('admin:', args=[obj.pk]) 19 | ) 20 | 21 | @admin.register(Multilingual) 22 | class MultilingualAdmin(admin.ModelAdmin): 23 | list_display = ("key", "content", "language") 24 | -------------------------------------------------------------------------------- /UI/src/js/components/patient/ShowPatient.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import PatientInfo from './PatientInfo.js'; 3 | import PatientComplementInfo from './PatientComplementInfo.js'; 4 | 5 | class ShowPatient extends Component { 6 | render() { 7 | let patientID = this.props.match.params.object; 8 | 9 | return ( 10 |
11 |

 Patient information

12 | 13 |

 Additional Information

14 | 15 |
16 | ); 17 | } 18 | } 19 | 20 | export default ShowPatient; -------------------------------------------------------------------------------- /UI/src/js/components/dynamicPages/Help.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import API from '../../API.js'; 3 | import ReactHtmlParser from 'react-html-parser'; 4 | 5 | class Help extends Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = { 9 | help: undefined 10 | }; 11 | } 12 | 13 | componentDidMount() { 14 | API.GET("help") 15 | .then(res => { 16 | if(this.refs.help) 17 | this.setState({help:res.data["help"]}); 18 | }) 19 | } 20 | 21 | render() { 22 | return ( 23 |
24 | {ReactHtmlParser(this.state.help)} 25 |
26 | ); 27 | } 28 | } 29 | 30 | export default Help; -------------------------------------------------------------------------------- /Backend/patients/api/serializers/ClinicalVariableSerializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import serializers 5 | from rest_framework import permissions 6 | 7 | from patients.models import ClinicalVariable, CVOption 8 | 9 | class ClinicalVariableSerializer(serializers.ModelSerializer): 10 | options = serializers.SerializerMethodField(required=False) 11 | 12 | class Meta: 13 | permission_classes = [permissions.IsAuthenticated] 14 | model = ClinicalVariable 15 | fields = ('variable', 'index_representation', 'type', 'options') 16 | read_only_fields = ('id',) 17 | 18 | def get_options(self, obj): 19 | if obj.type == ClinicalVariable.CONDITIONAL: 20 | return CVOption.getOptions(variable=obj) 21 | return [] -------------------------------------------------------------------------------- /Backend/patients/models/CVGroup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models 5 | 6 | class CVGroup(models.Model): 7 | title = models.CharField(max_length=30) 8 | description = models.CharField(max_length=100, blank=True) 9 | index_representation = models.IntegerField() 10 | display = models.BooleanField(default=True) 11 | 12 | def __unicode__(self): 13 | return u"CV group - %s" % self.title 14 | 15 | @staticmethod 16 | def all(all=False): 17 | ''' 18 | Returns all clinical variable group instances 19 | ''' 20 | tmpAll = CVGroup.objects.all() 21 | 22 | if all == False: 23 | tmpAll = tmpAll.filter(display=True) 24 | return tmpAll -------------------------------------------------------------------------------- /Backend/protocol/api/serializers/AssignedProtocolSerializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import serializers 5 | from rest_framework import permissions 6 | 7 | from protocol.models import AssignedProtocol 8 | 9 | 10 | class AssignedProtocolSerializer(serializers.ModelSerializer): 11 | pass 12 | # title = serializers.SerializerMethodField(required=False) 13 | # schedule = serializers.SerializerMethodField(required=False) 14 | # 15 | # class Meta: 16 | # permission_classes = [permissions.IsAuthenticated] 17 | # model = AssignedProtocol 18 | # fields = '__all__' 19 | # 20 | # def get_title(self, obj): 21 | # return obj.protocol.title 22 | # 23 | # def get_schedule(self, obj): 24 | # return obj.schedule.time.strftime("%H:%M") -------------------------------------------------------------------------------- /Backend/protocol/api/views/AssignedProtocolViewSet.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import viewsets, filters 5 | from rest_framework.decorators import list_route 6 | from rest_framework import generics 7 | 8 | from rest_framework.filters import OrderingFilter 9 | from django_filters.rest_framework import DjangoFilterBackend 10 | 11 | from protocol.api.serializers import ExecutedProtocolSerializer 12 | from protocol.models import ExecutedProtocol 13 | 14 | from patients.models import Patient 15 | 16 | from history.models import History 17 | 18 | class AssignedProtocolViewSet(viewsets.ModelViewSet): 19 | queryset = ExecutedProtocol.all(state=ExecutedProtocol.ASSIGNED) 20 | serializer_class = ExecutedProtocolSerializer 21 | 22 | filter_backends = [DjangoFilterBackend, OrderingFilter] 23 | filter_fields = ["patient"] 24 | -------------------------------------------------------------------------------- /Backend/protocol/api/views/ExecutedProtocolViewSet.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import viewsets, filters 5 | from rest_framework.decorators import list_route 6 | from rest_framework import generics 7 | 8 | from rest_framework.filters import OrderingFilter 9 | from django_filters.rest_framework import DjangoFilterBackend 10 | 11 | from protocol.api.serializers import ExecutedProtocolSerializer 12 | from protocol.models import ExecutedProtocol 13 | 14 | from patients.models import Patient 15 | 16 | from history.models import History 17 | 18 | class ExecutedProtocolViewSet(viewsets.ModelViewSet): 19 | queryset = ExecutedProtocol.all(state=ExecutedProtocol.EXECUTED) 20 | serializer_class = ExecutedProtocolSerializer 21 | 22 | filter_backends = [DjangoFilterBackend, OrderingFilter] 23 | filter_fields = ["patient"] 24 | -------------------------------------------------------------------------------- /Backend/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "genericcdss.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | except ImportError: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) 21 | raise 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /Backend/patients/api/serializers/CVGroupSerializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import serializers 5 | from rest_framework import permissions 6 | 7 | from patients.models import CVGroup, ClinicalVariable 8 | 9 | from patients.api.serializers import ClinicalVariableSerializer 10 | 11 | class CVGroupSerializer(serializers.ModelSerializer): 12 | clinical_variables = serializers.SerializerMethodField(required=False) 13 | class Meta: 14 | permission_classes = [permissions.IsAuthenticated] 15 | model = CVGroup 16 | fields = ('title', 'index_representation', 'clinical_variables') 17 | read_only_fields = ('id',) 18 | 19 | def get_clinical_variables(self, obj): 20 | clinicalVariables = ClinicalVariable.all(group=obj) 21 | return ClinicalVariableSerializer(clinicalVariables, many=True).data -------------------------------------------------------------------------------- /Backend/patients/api/serializers/PatientSerializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import serializers 5 | from rest_framework import permissions 6 | 7 | from patients.models import Patient 8 | 9 | 10 | class PatientSerializer(serializers.ModelSerializer): 11 | fullname = serializers.SerializerMethodField(required=False) 12 | fullgender = serializers.SerializerMethodField(required=False) 13 | 14 | class Meta: 15 | permission_classes = [permissions.IsAuthenticated] 16 | model = Patient 17 | fields = '__all__' 18 | read_only_fields = ('id', 'fullname', 'fullgender') 19 | 20 | def get_fullname(self, obj): 21 | full_name = obj.get_full_name() 22 | 23 | if full_name == "": 24 | return obj.email 25 | return full_name 26 | 27 | def get_fullgender(self, obj): 28 | return obj.get_full_gender() -------------------------------------------------------------------------------- /UI/src/js/components/globalComponents/Footer.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import '../../../css/app.css'; 3 | import API from '../../API.js'; 4 | import ReactHtmlParser from 'react-html-parser'; 5 | 6 | class Footer extends Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = { 10 | footer: undefined 11 | }; 12 | } 13 | 14 | componentDidMount() { 15 | API.GET("settings") 16 | .then(res => { 17 | if(this.refs.footer) 18 | this.setState({footer:res.data["footer"]}); 19 | }) 20 | } 21 | 22 | render() { 23 | return ( 24 | 29 | ); 30 | } 31 | } 32 | 33 | export default Footer; -------------------------------------------------------------------------------- /config/nginx/genericcdss.conf: -------------------------------------------------------------------------------- 1 | upstream web { 2 | ip_hash; 3 | server web:8000; 4 | } 5 | 6 | server { 7 | error_log /var/log/nginx/error.log error; 8 | root /frontend; 9 | listen 8000; 10 | server_name localhost; 11 | 12 | location = /favicon.ico { 13 | access_log off; 14 | log_not_found off; 15 | } 16 | 17 | location /{BASE_URL}/static2 { 18 | alias /static; 19 | } 20 | 21 | location /{BASE_URL} { 22 | root /frontend; 23 | index index.html; 24 | try_files $uri $uri/ /index.html; 25 | } 26 | 27 | location /{BASE_URL}/static { 28 | alias /frontend/static; 29 | } 30 | 31 | location /{BASE_URL}/images { 32 | alias /frontend/images; 33 | } 34 | 35 | location /{BASE_URL}/api { 36 | proxy_pass http://web/{BASE_URL}/api; 37 | } 38 | 39 | location /{BASE_URL}/admin { 40 | proxy_pass http://web/{BASE_URL}/admin; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Backend/protocol/api/serializers/ProtocolSerializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import serializers 5 | from rest_framework import permissions 6 | 7 | from protocol.models import Protocol 8 | from protocol.api.serializers import ScheduleSerializer 9 | 10 | from protocol_element.api.serializers import ProtocolElementSerializer 11 | from protocol_element.models import ProtocolElement 12 | 13 | class ProtocolSerializer(serializers.ModelSerializer): 14 | elements = serializers.SerializerMethodField(required=False) 15 | schedules = ScheduleSerializer(many=True) 16 | 17 | class Meta: 18 | permission_classes = [permissions.IsAuthenticated] 19 | model = Protocol 20 | fields = '__all__' 21 | 22 | def get_elements(self, obj): 23 | elements = ProtocolElement.all(protocol=obj) 24 | return ProtocolElementSerializer(elements, many=True).data 25 | -------------------------------------------------------------------------------- /Backend/protocol_element/api/serializers/PEActionSerializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import serializers 5 | from rest_framework import permissions 6 | 7 | from protocol_element.models import PEAction 8 | from protocol_element.api.serializers import PENextElementsSerializer 9 | 10 | class PEActionSerializer(serializers.ModelSerializer): 11 | nextElement = serializers.SerializerMethodField(required=False) 12 | type = serializers.SerializerMethodField(required=False) 13 | 14 | class Meta: 15 | permission_classes = [permissions.IsAuthenticated] 16 | model = PEAction 17 | #exclude = ['id'] 18 | fields = '__all__' 19 | 20 | def get_type(self, obj): 21 | return "Action" 22 | 23 | def get_nextElement(self, obj): 24 | if obj.nextElement: 25 | return str(obj.nextElement.nextElement.internalId) 26 | return "" -------------------------------------------------------------------------------- /Backend/protocol/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.contrib import admin 5 | 6 | from protocol.models import Protocol, Schedule, ExecutedProtocol, Time 7 | 8 | # Register your models here. 9 | 10 | @admin.register(Protocol) 11 | class ProtocolAdmin(admin.ModelAdmin): 12 | list_display = ("title", "description", "created_date", "removed") 13 | # 14 | # @admin.register(AssignedProtocol) 15 | # class AssignedProtocolAdmin(admin.ModelAdmin): 16 | # list_display = ("protocol", "patient", "schedule", "start_date", "end_date", "active") 17 | 18 | @admin.register(ExecutedProtocol) 19 | class ExecutedProtocolAdmin(admin.ModelAdmin): 20 | list_display = ("protocol", "patient", "execution_time", "physician") 21 | 22 | @admin.register(Schedule) 23 | class ScheduleAdmin(admin.ModelAdmin): 24 | list_display = ("title", "removed") 25 | 26 | @admin.register(Time) 27 | class TimeeAdmin(admin.ModelAdmin): 28 | list_display = ("time",) 29 | -------------------------------------------------------------------------------- /Backend/history/models/HistoryRelated.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models 5 | from django.contrib.contenttypes.fields import GenericForeignKey 6 | from django.contrib.contenttypes.models import ContentType 7 | 8 | class HistoryRelated(models.Model): 9 | ''' 10 | Describes an indirect relationship with the object, which makes the history relevant, not directly, but in scope. 11 | An example of related history, is for example, the history about a protocol task, being related with the history 12 | from a Protocol. 13 | It may make sense to see related history when seeing the process, but not the other way around. 14 | 15 | Attributes: 16 | :object (Model): Any model that inherits from :class:`django.models.Model` 17 | ''' 18 | 19 | object_type = models.ForeignKey(ContentType) 20 | object_id = models.PositiveIntegerField() 21 | object = GenericForeignKey('object_type', 'object_id') -------------------------------------------------------------------------------- /Backend/protocol_element/api/serializers/ProtocolElementSerializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from enum import Enum 5 | 6 | from rest_framework import serializers 7 | from rest_framework import permissions 8 | 9 | from protocol_element.models import ProtocolElement, PEInquiry, PEAction, PEDecision 10 | 11 | from protocol_element.api.serializers import PolymorphicSerializer, PEInquirySerializer, PEActionSerializer, PEDecisionSerializer 12 | 13 | class ProtocolElementSerializer(PolymorphicSerializer): 14 | class Meta: 15 | permission_classes = [permissions.IsAuthenticated] 16 | model = ProtocolElement 17 | #exclude = ['id'] 18 | fields = '__all__' 19 | 20 | def get_serializer_map(self): 21 | return { 22 | 'PEInquiry': PEInquirySerializer, 23 | 'PEAction': PEActionSerializer, 24 | 'PEDecision': PEDecisionSerializer, 25 | } 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Backend/protocol/management/commands/cleanAllProtocols.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from django.core.management.base import BaseCommand 3 | from protocol.models import Protocol, ExecutedProtocol, Schedule, Time 4 | from protocol_element.models import ProtocolElement, PEAction, PEDecision, PEInquiry, PENextElements 5 | from patients.models import CVGroup, ClinicalVariable, Patient, CVPatient, Admission 6 | 7 | class Command(BaseCommand): 8 | help = 'This command will create the hypoglycemia protocol in the database' 9 | 10 | def handle(self, *args, **options): 11 | self.stdout.write("\nClean all protocols in the system\n\n") 12 | for protocol in Protocol.objects.all(): 13 | peList = ProtocolElement.objects.filter(protocol=protocol) 14 | for pe in peList: 15 | PENextElements.objects.filter(nextElement=pe).delete() 16 | ProtocolElement.objects.filter(protocol=protocol).delete() 17 | protocol.delete() 18 | Schedule.objects.all().delete() 19 | Time.objects.all().delete() -------------------------------------------------------------------------------- /Backend/accounts/tests/testProfileSerializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.test import TestCase 5 | from django.contrib.auth.models import User 6 | 7 | from accounts.api.serializers import ProfileSerializer 8 | from accounts.models import Profile 9 | 10 | class ProfileTestCase(TestCase): 11 | def setUp(self): 12 | self.user = User.objects.create_user(username='userSerializer', 13 | email='userSerializer@ua.pt', 14 | password='12345', 15 | first_name="user", 16 | last_name="serializer") 17 | self.profile = Profile(user=self.user, role=Profile.ADMINISTRATOR) 18 | 19 | 20 | def test_serializer_composition(self): 21 | result = ProfileSerializer(self.profile) 22 | desiredFormat = { 23 | u'role': Profile.ADMINISTRATOR 24 | } 25 | 26 | self.assertEqual(result.data, desiredFormat) -------------------------------------------------------------------------------- /UI/src/js/reflux/ScheduleReflux.js: -------------------------------------------------------------------------------- 1 | import Reflux from 'reflux'; 2 | import API from '../API.js'; 3 | 4 | const ScheduleActions = Reflux.createActions([ 5 | 'load' 6 | ]); 7 | 8 | class ScheduleStore extends Reflux.Store { 9 | constructor(props) { 10 | super(props); 11 | this.listenables = ScheduleActions; 12 | this.state = { 13 | schedulesOptions:[], 14 | loading: false 15 | }; 16 | } 17 | 18 | onLoad() { 19 | this.setState({loading: true}); 20 | API.GET("schedule") 21 | .then(res => { 22 | let scheduleMap = res.data["results"].map(entry => { 23 | return { 24 | value: entry.id, 25 | label: entry.title 26 | } 27 | }); 28 | 29 | this.setState({ 30 | schedulesOptions: scheduleMap, 31 | loading: false 32 | }); 33 | }) 34 | } 35 | 36 | } 37 | 38 | export {ScheduleStore, ScheduleActions}; 39 | -------------------------------------------------------------------------------- /Backend/accounts/models/Profile.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models 5 | from django.contrib.auth.models import User 6 | 7 | class Profile(models.Model): 8 | NURSE = 1 9 | PHYSICIAN = 2 10 | ADMINISTRATOR = 3 11 | 12 | ROLES = ( 13 | (NURSE, 'This user is a nurse'), 14 | (PHYSICIAN, 'This user is a physician'), 15 | (ADMINISTRATOR, 'This user is a administrator'), 16 | ) 17 | 18 | user = models.OneToOneField(User) 19 | role = models.PositiveSmallIntegerField(choices=ROLES, default=NURSE) 20 | 21 | def __unicode__(self): 22 | return u"User profile for %s" % self.user 23 | 24 | def getFullName(self): 25 | if self.user.get_full_name() != "": 26 | return self.user.get_full_name() 27 | return self.user.username 28 | 29 | @staticmethod 30 | def getAllPhysicians(): 31 | ''' 32 | Returns all the physicians 33 | ''' 34 | return Profile.objects.all().filter(role=Profile.PHYSICIAN) 35 | -------------------------------------------------------------------------------- /UI/src/js/reflux/ClinicalVariablesReflux.js: -------------------------------------------------------------------------------- 1 | import Reflux from 'reflux'; 2 | import API from '../API.js'; 3 | 4 | const ClinicalVariablesActions = Reflux.createActions(['loadCVHeaders']); 5 | 6 | class ClinicalVariablesStore extends Reflux.Store { 7 | constructor() { 8 | super(); 9 | this.listenables = ClinicalVariablesActions; 10 | this.state = { 11 | headers: [], 12 | variablesDetails: [] 13 | }; 14 | } 15 | 16 | onLoadCVHeaders(){ 17 | API.GET("clinicalvariables") 18 | .then(res => { 19 | let headersMap = res.data["results"].map(entry => { 20 | return { 21 | value: entry.variable, 22 | label: entry.variable 23 | } 24 | }); 25 | 26 | this.setState({ 27 | headers: headersMap, 28 | variablesDetails:res.data["results"] 29 | }); 30 | }) 31 | } 32 | } 33 | 34 | export {ClinicalVariablesStore, ClinicalVariablesActions}; 35 | -------------------------------------------------------------------------------- /Backend/protocol_element/api/serializers/PEInquirySerializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import serializers 5 | from rest_framework import permissions 6 | 7 | from protocol_element.models import PEInquiry 8 | from protocol_element.api.serializers import PENextElementsSerializer 9 | 10 | from patients.api.serializers import ClinicalVariableSerializer 11 | 12 | class PEInquirySerializer(serializers.ModelSerializer): 13 | clinicalVariable = ClinicalVariableSerializer() 14 | nextElement = serializers.SerializerMethodField(required=False) 15 | type = serializers.SerializerMethodField(required=False) 16 | 17 | class Meta: 18 | permission_classes = [permissions.IsAuthenticated] 19 | model = PEInquiry 20 | #exclude = ['id'] 21 | fields = '__all__' 22 | 23 | def get_type(self, obj): 24 | return "Inquiry" 25 | 26 | def get_nextElement(self, obj): 27 | if obj.nextElement: 28 | return str(obj.nextElement.nextElement.internalId) 29 | return "" -------------------------------------------------------------------------------- /Backend/accounts/models/UserRecovery.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models 5 | from django.contrib.auth.models import User 6 | from django.utils import timezone 7 | 8 | from utils.hashes import createUUID 9 | from utils.time import nextMonth 10 | 11 | class UserRecovery(models.Model): 12 | user = models.ForeignKey(User) 13 | validity = models.DateTimeField(default=nextMonth) 14 | hash = models.CharField(max_length=50, default=createUUID) 15 | used = models.BooleanField(default=False) 16 | 17 | def __unicode__(self): 18 | return u"Password Recovery for %s" % self.user 19 | 20 | def setNewPassword(self, new_password): 21 | self.user.set_password(new_password) 22 | self.user.save() 23 | self.used = True 24 | self.save() 25 | 26 | @staticmethod 27 | def getUserRecovery(hash): 28 | try: 29 | return UserRecovery.objects.get(hash=hash, used=False, validity__gt=timezone.now()) 30 | except UserRecovery.DoesNotExist as ex: 31 | return None 32 | -------------------------------------------------------------------------------- /UI/src/js/components/dynamicPages/About.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import API from '../../API.js'; 3 | import ReactHtmlParser from 'react-html-parser'; 4 | import {version} from '../../../../package.json'; 5 | 6 | class About extends Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = { 10 | about: undefined 11 | }; 12 | } 13 | 14 | componentDidMount() { 15 | API.GET("about") 16 | .then(res => { 17 | if(this.refs.about) 18 | this.setState({about:res.data["about"]}); 19 | }) 20 | } 21 | 22 | render() { 23 | return ( 24 |
25 |
26 |

Version

27 |

The current system version is {version}

28 |
29 |
30 | {ReactHtmlParser(this.state.about)} 31 |
32 |
33 | ); 34 | } 35 | } 36 | 37 | export default About; -------------------------------------------------------------------------------- /Backend/patients/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.contrib import admin 5 | from models import Patient, ClinicalVariable, CVGroup, CVPatient, Admission, CVOption 6 | 7 | # Register your models here. 8 | 9 | @admin.register(Patient) 10 | class PatientAdmin(admin.ModelAdmin): 11 | list_display = ("id", "first_name", "last_name", "active", "status") 12 | 13 | @admin.register(ClinicalVariable) 14 | class ClinicalVariableAdmin(admin.ModelAdmin): 15 | list_display = ("group", "variable", "type", "description", "index_representation") 16 | 17 | @admin.register(CVGroup) 18 | class CVGroupAdmin(admin.ModelAdmin): 19 | list_display = ("title", "description", "index_representation") 20 | 21 | @admin.register(CVPatient) 22 | class CVPatientAdmin(admin.ModelAdmin): 23 | list_display = ("patient", "variable", "value") 24 | 25 | @admin.register(CVOption) 26 | class CVOptionAdmin(admin.ModelAdmin): 27 | list_display = ("variable", "option") 28 | 29 | @admin.register(Admission) 30 | class AdmissionAdmin(admin.ModelAdmin): 31 | pass#list_display = ("patient", "physician", "variable", "value") 32 | 33 | -------------------------------------------------------------------------------- /UI/src/css/LoginComponent.css: -------------------------------------------------------------------------------- 1 | .li-login 2 | { 3 | min-width: 300px; 4 | } 5 | 6 | .form-signin .form-control 7 | { 8 | position: relative; 9 | font-size: 16px; 10 | height: auto; 11 | padding: 5px; 12 | -webkit-box-sizing: border-box; 13 | -moz-box-sizing: border-box; 14 | box-sizing: border-box; 15 | } 16 | 17 | .form-signin .form-control:focus 18 | { 19 | z-index: 2; 20 | } 21 | 22 | .form-signin input[type="text"] 23 | { 24 | margin-bottom: -1px; 25 | border-bottom-left-radius: 0; 26 | border-bottom-right-radius: 0; 27 | } 28 | 29 | .form-signin input[type="password"] 30 | { 31 | margin-bottom: 10px; 32 | border-top-left-radius: 0; 33 | border-top-right-radius: 0; 34 | } 35 | .form-signin 36 | { 37 | max-width: 330px; 38 | padding: 15px; 39 | margin: 0 auto; 40 | } 41 | .form-signin .form-signin-heading, .form-signin .checkbox 42 | { 43 | margin-bottom: 10px; 44 | } 45 | .form-signin .checkbox 46 | { 47 | font-weight: normal; 48 | margin-left: 20px; 49 | } 50 | .need-help 51 | { 52 | margin-top: 10px; 53 | } 54 | .new-account 55 | { 56 | display: block; 57 | margin-top: 10px; 58 | } 59 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | db: 2 | image: postgres:9.3 3 | environment: 4 | - POSTGRES_USER=genericcdss 5 | - POSTGRES_PASS=12345 6 | 7 | nginx: 8 | image: nginx:1.14.0 9 | ports: 10 | - "9000:8000" 11 | volumes: 12 | - ./Backend:/src 13 | - ./config/nginx:/etc/nginx/conf.d 14 | - ./Backend/static:/static 15 | - ./UI/build:/frontend 16 | links: 17 | - web 18 | 19 | web: 20 | image: bioinformatics-ua/genericcdss:latest 21 | command: bash -c "cd /GenericCDSS/config && sh run_docker.sh" 22 | environment: 23 | - DOCKER_POSTGRES_USER=genericcdss 24 | - DOCKER_POSTGRES_PASS=12345 25 | - DOCKER_POSTGRES_DB=genericcdss 26 | - DOCKER_POSTGRES_HOST=db 27 | - DOCKER_POSTGRES_PORT=5432 28 | - DEPLOY_MODE=demo 29 | - API_URL=http://localhost:9000/genericcdss/api/ 30 | - HOMEPAGE=http://localhost:9000/genericcdss 31 | - BASE_URL=genericcdss 32 | - VERSION=0.1 33 | links: 34 | - db 35 | volumes: 36 | - ./Backend:/src 37 | - ./Backend/static:/GenericCDSS/Backend/static 38 | - ./UI/build:/GenericCDSS/UI/build 39 | expose: 40 | - "8000" 41 | -------------------------------------------------------------------------------- /Backend/protocol_element/api/serializers/PEDecisionSerializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import serializers 5 | from rest_framework import permissions 6 | 7 | from patients.api.serializers import ClinicalVariableSerializer 8 | 9 | from protocol_element.api.serializers import PENextElementsSerializer 10 | from protocol_element.models import PEDecision 11 | 12 | class PEDecisionSerializer(serializers.ModelSerializer): 13 | clinicalVariable = ClinicalVariableSerializer() 14 | nextElement = serializers.SerializerMethodField(required=False) 15 | type = serializers.SerializerMethodField(required=False) 16 | 17 | class Meta: 18 | permission_classes = [permissions.IsAuthenticated] 19 | model = PEDecision 20 | #exclude = ['id'] 21 | fields = '__all__' 22 | 23 | def get_type(self, obj): 24 | return "Decision" 25 | 26 | def get_nextElement(self, obj): 27 | string = "" 28 | for element in obj.nextElement.all(): 29 | string += element.option + ":" + str(element.nextElement.internalId) + ";" 30 | return string[:-1] 31 | 32 | -------------------------------------------------------------------------------- /UI/src/js/reflux/PatientClinicalVariablesReflux.js: -------------------------------------------------------------------------------- 1 | import Reflux from 'reflux'; 2 | import API from '../API.js'; 3 | 4 | const PatientClinicalVariablesActions = Reflux.createActions(['load', 'refresh', 'addCVData']); 5 | 6 | class PatientClinicalVariablesStore extends Reflux.Store { 7 | constructor() { 8 | super(); 9 | this.listenables = PatientClinicalVariablesActions; 10 | this.state = { 11 | data: [], 12 | headers: [] 13 | }; 14 | } 15 | 16 | onLoad(id){ 17 | API.GET("patientclinicalvariables", id) 18 | .then(res => { 19 | this.setState({ 20 | headers: res.data["headers"], 21 | data: res.data["results"] 22 | }); 23 | }) 24 | } 25 | 26 | onAddCVData(data){ 27 | API.POST("patientclinicalvariables", "addVariables", data) 28 | .then(res => { 29 | this.setState({ 30 | headers: res.data["headers"], 31 | data: res.data["results"] 32 | }); 33 | this.trigger(); 34 | }); 35 | } 36 | } 37 | 38 | export {PatientClinicalVariablesStore, PatientClinicalVariablesActions}; 39 | -------------------------------------------------------------------------------- /UI/src/js/components/globalComponents/LoginButton.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import LoginComponent from './LoginComponent.js'; 3 | import API from '../../API.js'; 4 | 5 | class LoginButton extends Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = { 9 | showDefaultHome: false 10 | }; 11 | } 12 | 13 | componentDidMount(){ 14 | API.GET("settings") 15 | .then(res => { 16 | this.setState({showDefaultHome:res.data["showDefaultHome"]}); 17 | }); 18 | } 19 | 20 | render() { 21 | if(this.state.showDefaultHome) 22 | return (); 23 | return ( 24 |
25 | 29 |
30 | 31 |
32 |
33 | ); 34 | } 35 | } 36 | 37 | export default LoginButton; -------------------------------------------------------------------------------- /UI/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "genericcdss", 3 | "version": "0.1.0", 4 | "private": true, 5 | "api_url": "http://127.0.0.1:8000/api/", 6 | "homepage": "http://127.0.0.1:3000", 7 | "base_url": "", 8 | "dependencies": { 9 | "axios": "^0.18.0", 10 | "bootstrap": "^4.1.3", 11 | "font-awesome": "^4.7.0", 12 | "history": "^4.7.2", 13 | "jquery": "^3.3.1", 14 | "rc-tabs": "^9.2.4", 15 | "react": "^16.2.0", 16 | "react-awesome-modal": "^2.0.3", 17 | "react-bootstrap": "^0.32.4", 18 | "react-bootstrap-sweetalert": "^4.4.1", 19 | "react-cookie": "^2.1.4", 20 | "react-date-picker": "^6.10.1", 21 | "react-dom": "^16.2.0", 22 | "react-html-parser": "^2.0.2", 23 | "react-router": "^4.2.0", 24 | "react-router-dom": "^4.2.2", 25 | "react-scripts": "0.9.5", 26 | "react-select": "^1.2.1", 27 | "react-simple-flowchart": "^1.2.2", 28 | "react-table": "^6.7.6", 29 | "reflux": "^6.4.1", 30 | "semantic-ui-css": "^2.3.1", 31 | "semantic-ui-react": "^0.79.1" 32 | }, 33 | "devDependencies": {}, 34 | "scripts": { 35 | "start": "react-scripts start", 36 | "build": "react-scripts build", 37 | "test": "react-scripts test --env=jsdom", 38 | "eject": "react-scripts eject" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /UI/src/externalImports/mfglabs-iconset/README.md: -------------------------------------------------------------------------------- 1 | #MFG Labs iconset 2 | =============== 3 | 4 | ##Awesome web font icon by MFG Labs 5 | 6 | How does it work? 7 | 8 | Easy as pie 9 | 10 | Start by downloading the project on this url. 11 | 12 | 1. Copy the font directory into your project 13 | 2. Copy the mfglabs_iconset.css style sheet into your project. 14 | 3. Copy the link of the stylesheet into you header 15 | 16 | 17 | `` 18 | 19 | ###Drop the markup `` anywhere 20 | 21 | 22 | `` 23 | 24 | 25 | ##Customisation 26 | 27 | Size subclass 28 | 29 | Create bevel and emboss is easy by using custom sub class 30 | 31 | 32 | ###Add the subclass icon2x or icon3x 33 | 34 | 35 | `` 36 | 37 | ###Customise your css directly in mfglabs_iconset.css 38 | 39 | 40 | `.icon2x { font-size: 2em; }` 41 | 42 | `.icon3x { font-size: 3em; }` 43 | 44 | ##Licenses 45 | All icons are distributed under 46 | [CC BY-SA](http://creativecommons.org/licenses/by/3.0/deed.en) licence. 47 | 48 | Font is distributed under 49 | [SIL](http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL) licence. 50 | -------------------------------------------------------------------------------- /Backend/history/api/views/HistoryViewSet.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import viewsets, mixins, status 5 | from rest_framework.response import Response 6 | 7 | from history.models import History 8 | from history.api.serilizer import HistorySerializer 9 | 10 | class HistoryViewSet(mixins.ListModelMixin, 11 | viewsets.GenericViewSet): 12 | """ 13 | API for History manipulation 14 | """ 15 | queryset = History.objects.none() 16 | serializer_class = HistorySerializer 17 | 18 | 19 | def get_queryset(self): 20 | return History.all(user=self.request.user).exclude(event=History.ACCESS) 21 | 22 | def list(self, request, *args, **kwargs): 23 | """ 24 | Return a list of user-related history, across all the system. 25 | 26 | """ 27 | return super(HistoryViewSet, self).list(request, args, kwargs) 28 | 29 | def __filterHistory(self, Model, pk): 30 | '''Internal class that handles the creation of the History Serializer based on the object type, and its public hash. 31 | 32 | DEPRECATED: In favor of :class:`history.api.FilteredHistory` 33 | ''' 34 | serializer = HistorySerializer(many=True, instance=History.type(Model, pk)) 35 | 36 | return Response(serializer.data, status=status.HTTP_201_CREATED) -------------------------------------------------------------------------------- /Backend/utils/api/views/FlatPagesView.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import viewsets 5 | from rest_framework.permissions import AllowAny 6 | from rest_framework.response import Response 7 | from rest_framework.decorators import detail_route 8 | 9 | from django.contrib.flatpages.models import FlatPage 10 | 11 | class FlatPagesView(viewsets.ViewSet): 12 | permission_classes = (AllowAny,) 13 | 14 | @detail_route(methods=['get']) 15 | def getAbout(self, request, *args, **kwargs): 16 | try: 17 | aboutPage = FlatPage.objects.get(title="About") 18 | return Response({"about": aboutPage.content}) 19 | except: 20 | return Response({"about": "Error"}) 21 | 22 | 23 | @detail_route(methods=['get']) 24 | def getHelp(self, request, *args, **kwargs): 25 | try: 26 | helpPage = FlatPage.objects.get(title="Help") 27 | return Response({"help": helpPage.content}) 28 | except: 29 | return Response({"help": "Error"}) 30 | 31 | 32 | @detail_route(methods=['get']) 33 | def getHome(self, request, *args, **kwargs): 34 | try: 35 | homePage = FlatPage.objects.get(title="Home") 36 | return Response({"home": homePage.content}) 37 | except: 38 | return Response({"home": "Error"}) -------------------------------------------------------------------------------- /UI/src/css/Select.css: -------------------------------------------------------------------------------- 1 | .invalid-feedback-select { 2 | display: block; 3 | width: 100%; 4 | margin-top: .25rem; 5 | font-size: 80%; 6 | color: #dc3545;; 7 | } 8 | .custom-select.is-invalid~.invalid-feedback-select, 9 | .form-control.is-invalid~.invalid-feedback-select{ 10 | display: block; 11 | } 12 | 13 | .warning-feedback-select { 14 | display: block; 15 | width: 100%; 16 | margin-top: .25rem; 17 | font-size: 80%; 18 | color: #bba70a; 19 | } 20 | .custom-select.is-warning~.warning-feedback-select, 21 | .form-control.is-warning~.warning-feedback-select{ 22 | display: block; 23 | } 24 | 25 | .Select{ 26 | z-index: 100; 27 | } 28 | 29 | .Selectx2{ 30 | z-index: 200; 31 | } 32 | 33 | .Selectx3{ 34 | z-index: 300; 35 | } 36 | 37 | 38 | .Selectx4{ 39 | z-index: 400; 40 | } 41 | 42 | .Selectx5{ 43 | z-index: 500; 44 | } 45 | 46 | .Selectx6{ 47 | z-index: 600; 48 | } 49 | 50 | .Selectx7{ 51 | z-index: 700; 52 | } 53 | 54 | .Selectx8{ 55 | z-index: 800; 56 | } 57 | 58 | .Selectx9{ 59 | z-index: 900; 60 | } 61 | 62 | .Selectx10{ 63 | z-index: 1000; 64 | } 65 | 66 | .Selectx11{ 67 | z-index: 1100; 68 | } 69 | 70 | .Selectx12{ 71 | z-index: 1200; 72 | } 73 | 74 | .Selectx13{ 75 | z-index: 1300; 76 | } 77 | 78 | .Selectx14{ 79 | z-index: 1400; 80 | } 81 | 82 | .Selectx15{ 83 | z-index: 1500; 84 | } -------------------------------------------------------------------------------- /Backend/protocol_element/models/PEInquiry.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models 5 | 6 | from protocol_element.models import ProtocolElement, PENextElements 7 | from patients.models import ClinicalVariable 8 | 9 | class PEInquiry(ProtocolElement): 10 | clinicalVariable = models.ForeignKey(ClinicalVariable) 11 | nextElement = models.ForeignKey(PENextElements, null=True) 12 | 13 | def getNextElementId(self): 14 | if self.nextElement: 15 | return self.nextElement.getNextElementId() 16 | return None 17 | 18 | @staticmethod 19 | def new(id, clinicalVariable, protocol): 20 | cv = ClinicalVariable.objects.get(variable=clinicalVariable) 21 | return PEInquiry.objects.create(clinicalVariable=cv, internalId=id, protocol=protocol).save() 22 | 23 | @staticmethod 24 | def addNextElement(id, protocol, nextElementId): 25 | pe = ProtocolElement.get(type=ProtocolElement.INQUIRY,internalId=int(id), protocol=protocol) 26 | nextElement = PENextElements.new(nextElementId=int(nextElementId), protocol=protocol) 27 | pe.nextElement = nextElement 28 | pe.save() 29 | 30 | @staticmethod 31 | def all(): 32 | ''' 33 | Returns all inquiry protocol elements 34 | ''' 35 | 36 | tmpAll = PEInquiry.objects.all() 37 | 38 | return tmpAll.order_by('internalId') -------------------------------------------------------------------------------- /Backend/protocol_element/models/PEAction.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models 5 | 6 | from protocol_element.models import ProtocolElement, PENextElements 7 | 8 | class PEAction(ProtocolElement): 9 | action = models.CharField(max_length=300) 10 | nextElement = models.ForeignKey(PENextElements, null=True) 11 | 12 | def getNextElementId(self): 13 | if self.nextElement: 14 | return self.nextElement.getNextElementId() 15 | return None 16 | 17 | @staticmethod 18 | def new(id, action, protocol): 19 | return PEAction.objects.create(action=action, internalId=id, protocol=protocol).save() 20 | 21 | @staticmethod 22 | def addNextElement(id, protocol, nextElementId): 23 | pe = ProtocolElement.get(type=ProtocolElement.ACTION, internalId=int(id), protocol=protocol) 24 | nextElement = PENextElements.new(nextElementId=int(nextElementId), protocol=protocol) 25 | pe.nextElement = nextElement 26 | pe.save() 27 | 28 | def execute(self): 29 | print "to do" 30 | 31 | if self.nextElement != None: 32 | self.nextElement.nextElement.execute() 33 | 34 | @staticmethod 35 | def all(): 36 | ''' 37 | Returns all action protocol elements 38 | ''' 39 | 40 | tmpAll = PEAction.objects.all() 41 | 42 | return tmpAll.order_by('internalId') -------------------------------------------------------------------------------- /UI/src/js/reflux/AdmissionReflux.js: -------------------------------------------------------------------------------- 1 | import Reflux from 'reflux'; 2 | import API from '../API.js'; 3 | 4 | const AdmissionActions = Reflux.createActions([ 5 | 'load', 6 | 'admitPatient', 7 | 'dischargePatient' 8 | ]); 9 | 10 | class AdmissionStore extends Reflux.Store { 11 | constructor(props) { 12 | super(props); 13 | this.listenables = AdmissionActions; 14 | this.state = { 15 | patientList: [], 16 | loading: false, 17 | } 18 | } 19 | 20 | onLoad() { 21 | this.setState({loading: true}); 22 | API.GET("admission") 23 | .then(res => { 24 | this.setState({ 25 | patientList: res.data["results"], 26 | loading: false 27 | }); 28 | }) 29 | } 30 | 31 | onDischargePatient(id) { 32 | API.POST("admission", "discharge", {patientID: id}); 33 | } 34 | 35 | onAdmitPatient(patientID, seletedProtocols, room) { 36 | this.setState({loading: true}); 37 | API.POST("admission", "new", { 38 | patientID: patientID, 39 | seletedProtocols: seletedProtocols, 40 | room:room 41 | }).then(res => { 42 | this.setState({ 43 | patientList: res.data["results"], 44 | loading: false 45 | }); 46 | }) 47 | } 48 | 49 | } 50 | 51 | export {AdmissionStore, AdmissionActions}; -------------------------------------------------------------------------------- /UI/src/js/components/protocol/ProtocolCostumization.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Reflux from 'reflux'; 3 | import Select from 'react-select'; 4 | import "react-table/react-table.css"; 5 | import {ProtocolStore, ProtocolActions} from '../../reflux/ProtocolReflux.js'; 6 | /** 7 | * NOT USED 8 | * */ 9 | class ProtocolCostumization extends Reflux.Component { 10 | constructor(props) { 11 | super(props); 12 | this.store = ProtocolStore; 13 | this.state = []; 14 | } 15 | 16 | componentDidMount() { 17 | ProtocolActions.load(); 18 | } 19 | 20 | selectHandleChange = (selectedProtocol) => { 21 | this.setState({selectedProtocol}); 22 | this.props.setProtocol({selectedProtocol}); 23 | }; 24 | 25 | render() { 26 | return ( 27 |
28 | 41 |
42 | 43 | { 44 | this.state.selectedPatient === undefined ? '' : 45 | 46 |

 Additional information

47 | 48 |
49 | } 50 |
51 | ); 52 | } 53 | } 54 | 55 | export default AddPatient; -------------------------------------------------------------------------------- /Backend/protocol/management/commands/continuousIntravenousInfusion.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from django.core.management.base import BaseCommand 3 | from protocol.models import Protocol, ExecutedProtocol 4 | from protocol_element.models import ProtocolElement, PEAction, PEDecision, PEInquiry, PENextElements 5 | from patients.models import CVGroup, ClinicalVariable, Patient, CVPatient, Admission 6 | 7 | class Command(BaseCommand): 8 | help = 'This command will create the Continuous Intravenous Infusion protocol in the database' 9 | 10 | def handle(self, *args, **options): 11 | self.stdout.write("\nCleaning the Continuous Intravenous Infusion protocol!\n\n") 12 | self.clean_protocol() 13 | self.stdout.write("\nCreating the Clinical Variables needed by this protocol!\n\n") 14 | self.create_cvs() 15 | self.stdout.write("\nCreating the Continuous Intravenous Infusion protocol!\n\n") 16 | self.create_protocol() 17 | self.stdout.write("\nSuccess:The Continuous Intravenous Infusion protocol was created with success!\n\n") 18 | 19 | def create_cvs(self): 20 | #todo 21 | self.stdout.write("\nTODO\n\n") 22 | 23 | def clean_protocol(self): 24 | try: 25 | protocol = Protocol.objects.get(title="Continuous Intravenous Infusion") 26 | peList = ProtocolElement.objects.filter(protocol=protocol) 27 | for pe in peList: 28 | PENextElements.objects.filter(nextElement=pe).delete() 29 | ProtocolElement.objects.filter(protocol=protocol).delete() 30 | protocol.delete() 31 | except: 32 | self.stdout.write("\nThe Continuous Intravenous Infusion protocol does not exist in the system!\n\n") 33 | 34 | def create_protocol(self): 35 | protocol = Protocol.objects.create(title="Continuous Intravenous Infusion", 36 | description = "This protocol is intended to be used in hyperglycemic adult patients in the intensive care unit") 37 | protocol.save() 38 | 39 | #todo 40 | PEAction.new(id=1, 41 | action="In progress", 42 | protocol=protocol) -------------------------------------------------------------------------------- /Backend/protocol/models/AssignedProtocol.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models 5 | 6 | from patients.models import Patient 7 | 8 | from protocol.models import Protocol, Schedule 9 | 10 | # class AssignedProtocol(models.Model): 11 | # protocol = models.ForeignKey(Protocol) 12 | # patient = models.ForeignKey(Patient) 13 | # schedule = models.ForeignKey(Schedule) 14 | # start_date = models.DateField() 15 | # end_date = models.DateField(null=True) 16 | # active = models.BooleanField(default=True) 17 | # 18 | # 19 | # @staticmethod 20 | # def new(protocol, patient, schedule, start_date): 21 | # assignedProtocol = AssignedProtocol.objects.create(protocol=protocol, 22 | # patient=patient, 23 | # schedule=schedule, 24 | # start_date=start_date) 25 | # # History todo 26 | # assignedProtocol.save() 27 | # 28 | # @staticmethod 29 | # def getCurrentAssignment(patient): 30 | # tmpAll = AssignedProtocol.all(patient=patient).filter(end_date__isnull=True) 31 | # 32 | # #Calculate which is the next protocol consedering the schedule 33 | # #todo, only necessary when exist more than one protocol assigned to a patient 34 | # 35 | # return tmpAll.order_by('start_date')[0] 36 | # 37 | # @staticmethod 38 | # def all(active=True, protocol=None, patient=None, schedule=None): 39 | # ''' 40 | # Returns all assigned protocol instances 41 | # ''' 42 | # tmpAll = AssignedProtocol.objects.all() 43 | # 44 | # if active == True: 45 | # tmpAll = tmpAll.filter(active=True) 46 | # 47 | # if protocol != None: 48 | # tmpAll = tmpAll.filter(protocol=protocol) 49 | # 50 | # if patient != None: 51 | # tmpAll = tmpAll.filter(patient=patient) 52 | # 53 | # if schedule != None: 54 | # tmpAll = tmpAll.filter(schedule=schedule) 55 | # 56 | # return tmpAll.order_by('start_date') -------------------------------------------------------------------------------- /UI/src/js/components/protocol/Schedules.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Reflux from 'reflux'; 4 | import {StateActions} from '../../reflux/StateReflux.js'; 5 | 6 | /** 7 | * Component to manage the protocol schedules 8 | * 9 | * todo finish the creation of this componenet. 10 | * The idea is to show all the schedules for the protocol, i. e., 11 | * when a task action to schedule the protocol is created, in this modal 12 | * will be shown the decision elements and and the condition that led to the 13 | * new scheduling. 14 | * */ 15 | class Schedules extends Reflux.Component { 16 | constructor(props) { 17 | super(props); 18 | this.state = {}; 19 | } 20 | 21 | componentDidUpdate(prevProps, prevState) { 22 | if (prevState !== this.state) 23 | StateActions.updateModal(this.modalHeader(), this.modalContent(), this.modalFooter()); 24 | } 25 | 26 | modalHeader = () => { 27 | return ( 28 |
29 |

Schedules

30 |
31 | ); 32 | }; 33 | 34 | modalContent = () => { 35 | return ( 36 |
37 | Schedules thinking how to solve the problem about the hours vs before bed/meals time 38 |
39 | ); 40 | }; 41 | 42 | modalFooter = () => { 43 | return ( 44 |
45 | 48 |
49 | ); 50 | }; 51 | 52 | openModal = (event) => { 53 | event.preventDefault(); 54 | StateActions.openModal(this.modalHeader(), this.modalContent(), this.modalFooter()); 55 | }; 56 | 57 | closeModal = () => { 58 | StateActions.closeModal(); 59 | }; 60 | 61 | render() { 62 | return ( 63 | 66 | ); 67 | } 68 | 69 | static propTypes = {}; 70 | } 71 | 72 | Schedules.defaultProps = {}; 73 | 74 | export default Schedules; 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /UI/src/js/components/globalComponents/LoginComponent.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Reflux from 'reflux'; 3 | //import {Link} from "react-router-dom"; 4 | import {UserStore, UserActions} from '../../reflux/UserReflux.js'; 5 | 6 | class LoginComponent extends Reflux.Component { 7 | constructor(props) { 8 | super(props); 9 | this.store = UserStore; 10 | } 11 | 12 | login = event => { 13 | event.preventDefault(); 14 | if (this.refs.usr !== "" && this.refs.pwd !== "") { 15 | let username = this.refs.usr.value.trim(); 16 | let password = this.refs.pwd.value.trim(); 17 | let remember = this.refs.rmb.checked; 18 | UserActions.login(username, password, remember); 19 | } 20 | }; 21 | 22 | render() { 23 | return ( 24 |
25 |
26 | 28 | 30 | 31 | {this.state.failed ? ( 32 |
Login failed
) : ''} 33 | 34 | 38 |
39 | 44 | {/*Forgot password ?*/} 45 |
46 |
47 |
48 | {/* Create an account */} 49 |
50 | ); 51 | } 52 | } 53 | 54 | export default LoginComponent; -------------------------------------------------------------------------------- /Backend/patients/models/Patient.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models 5 | 6 | class Patient(models.Model): 7 | ADMITTED = 1 8 | DISCHARGED = 2 9 | 10 | STATUS = ( 11 | (ADMITTED, 'This patient was admitted to the hospital'), 12 | (DISCHARGED, 'This patient was discharged from hospital'), 13 | ) 14 | 15 | GENDER_OPTIONS = ( 16 | ('M', 'Male'), 17 | ('F', 'Female'), 18 | ) 19 | 20 | code = models.CharField(max_length=20, blank=True) #To connect in the future to an EHR using HL7 21 | first_name = models.CharField(max_length=50) 22 | last_name = models.CharField(max_length=50) 23 | active = models.BooleanField(default=True) 24 | status = models.PositiveSmallIntegerField(choices=STATUS, default=ADMITTED) 25 | gender = models.CharField(max_length=1, choices=GENDER_OPTIONS) 26 | birthdate = models.DateField() 27 | phone = models.CharField(max_length=20, blank=True) 28 | email = models.CharField(max_length=50, blank=True) 29 | #... maybe more fields to do 30 | 31 | def __unicode__(self): 32 | return u"Patient %s" % self.get_full_name() 33 | 34 | def get_birthdate(self): 35 | return self.birthdate.strftime("%d/%m/%Y") 36 | 37 | def get_full_name(self): 38 | return self.first_name + " " + self.last_name 39 | 40 | def get_full_gender(self): #Change this to be multilingual 41 | if(self.gender.lower() == 'm'): 42 | return "Male" 43 | 44 | if(self.gender.lower() == 'f'): 45 | return "Female" 46 | 47 | def discharge(self): 48 | ''' 49 | Discharges the patient from the hospital 50 | ''' 51 | # History to do 52 | self.status = Patient.DISCHARGED 53 | self.save() 54 | 55 | def admit(self): 56 | ''' 57 | Patient admission in the hospital 58 | ''' 59 | # History to do 60 | self.status = Patient.ADMITTED 61 | self.save() 62 | 63 | @staticmethod 64 | def all(active=None, status=None): 65 | ''' 66 | Returns all patient instances 67 | ''' 68 | tmpAll = Patient.objects.all() 69 | 70 | if active != None: 71 | tmpAll = tmpAll.filter(active=active) 72 | else: 73 | tmpAll = tmpAll.filter(active=True) 74 | 75 | if status != None: 76 | tmpAll = tmpAll.filter(status=status) 77 | 78 | return tmpAll 79 | -------------------------------------------------------------------------------- /Backend/patients/models/ClinicalVariable.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models 5 | from django.utils import encoding 6 | 7 | from patients.models import Patient, CVGroup 8 | 9 | class ClinicalVariable(models.Model): 10 | STRING = 'String' 11 | NUMERIC = 'Numeric' 12 | CONDITIONAL = 'Conditional' 13 | 14 | TYPES = ( 15 | (STRING, 'String - The clinical variable can store string values'), 16 | (NUMERIC, 'Numeric - The clinical variable can store numeric values'), 17 | (CONDITIONAL, 'Conditional - The clinical variable can store one option from a list of values'), 18 | ) 19 | 20 | group = models.ForeignKey(CVGroup) 21 | variable = models.CharField(max_length=30) 22 | type = models.CharField(max_length=30, choices=TYPES, default=STRING) 23 | description = models.CharField(max_length=100, blank=True) 24 | index_representation = models.IntegerField() 25 | display = models.BooleanField(default=True) 26 | 27 | def __unicode__(self): 28 | return u"CV Group: %s, Variable: %s" % (self.group.title, self.variable) 29 | 30 | @staticmethod 31 | def new(group, variable, type, description, index_representation, options=None): 32 | from patients.models import CVOption 33 | 34 | cv = ClinicalVariable.objects.create(group=group, 35 | variable=variable, 36 | type=type, 37 | description=description, 38 | index_representation=index_representation) 39 | cv.save() 40 | 41 | if type == ClinicalVariable.CONDITIONAL: 42 | for option in options: 43 | CVOption.objects.create(variable=cv, 44 | option=option).save() 45 | 46 | return cv 47 | 48 | @staticmethod 49 | def all(group=None, all=False): 50 | ''' 51 | Returns all clinical variable instances 52 | ''' 53 | tmpAll = ClinicalVariable.objects.all() 54 | 55 | if all == False: 56 | tmpAll = tmpAll.filter(display=True) 57 | 58 | if group != None: 59 | tmpAll = tmpAll.filter(group=group) 60 | 61 | return tmpAll 62 | 63 | @staticmethod 64 | def get(variable, group): 65 | return ClinicalVariable.all(group=group).get(variable=variable) 66 | -------------------------------------------------------------------------------- /UI/src/js/reflux/PatientReflux.js: -------------------------------------------------------------------------------- 1 | import Reflux from 'reflux'; 2 | import API from '../API.js'; 3 | import History from '../components/globalComponents/History.js'; 4 | 5 | const PatientActions = Reflux.createActions([ 6 | 'load', 7 | 'loadPatient', 8 | 'loadDischargedPatients', 9 | 'addPatient' 10 | ]); 11 | 12 | class PatientStore extends Reflux.Store { 13 | constructor(props) { 14 | super(props); 15 | this.listenables = PatientActions; 16 | this.state = { 17 | patient: this.getPatientInitialState(), 18 | patientList: [], 19 | patientListKeyValue: [], 20 | loading: false, 21 | }; 22 | } 23 | 24 | getPatientInitialState = () => { 25 | return { 26 | first_name: '', 27 | last_name: '', 28 | gender: undefined, 29 | birthdate: '', 30 | phone: '', 31 | email: '', 32 | status: undefined, 33 | fullgender: '' 34 | } 35 | }; 36 | 37 | onLoad() { 38 | this.setState({loading: true}); 39 | API.GET("patient") 40 | .then(res => { 41 | this.setState({ 42 | patientList: res.data["results"], 43 | loading: false 44 | }); 45 | }) 46 | } 47 | 48 | onLoadDischargedPatients() { 49 | this.setState({loading: true}); 50 | API.GET("patient", "listDischarged") 51 | .then(res => { 52 | let patientMap = res.data["results"].map(entry => { 53 | return { 54 | value: entry.id, 55 | label: entry.fullname 56 | } 57 | }); 58 | this.setState({ 59 | //patientList: res.data["results"], 60 | patientListKeyValue: patientMap, 61 | loading: false 62 | }); 63 | }) 64 | } 65 | 66 | onLoadPatient(id) { 67 | if (id !== undefined) 68 | API.GET("patient", id) 69 | .then(res => { 70 | this.setState({patient: res.data}); 71 | }); 72 | else 73 | this.setState({patient: this.getPatientInitialState()}); 74 | } 75 | 76 | onAddPatient() { 77 | API.POST("patient", null, this.state.patient) 78 | .then(res => { 79 | History.push('/assignprotocol/' + res.data.id); 80 | }); 81 | } 82 | 83 | } 84 | 85 | export {PatientStore, PatientActions}; -------------------------------------------------------------------------------- /Backend/protocol_element/api/serializers/PolymorphicSerializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from enum import Enum 5 | 6 | from rest_framework import serializers 7 | 8 | class PolymorphicSerializer(serializers.ModelSerializer): 9 | def get_serializer_map(self): 10 | """ 11 | Return a dict to map class names to their respective serializer classes 12 | 13 | To be implemented by all PolymorphicSerializer subclasses 14 | """ 15 | raise NotImplementedError 16 | 17 | def to_representation(self, obj): 18 | """ 19 | Translate object to internal data representation 20 | 21 | Override to allow polymorphism 22 | """ 23 | obj = obj.__class__.objects.get_subclass(id=obj.id) 24 | type_str = obj.__class__.__name__ 25 | 26 | try: 27 | serializer = self.get_serializer_map()[type_str] 28 | except KeyError: 29 | raise ValueError('Serializer for "{}" does not exist'.format(type_str), ) 30 | 31 | data = serializer(obj, context=self.context).to_representation(obj) 32 | 33 | #data['type'] = type_str 34 | return data 35 | 36 | # def to_internal_value(self, data): 37 | # """ 38 | # Validate data and initialize primitive types 39 | # 40 | # Override to allow polymorphism 41 | # """ 42 | # try: 43 | # type_str = data['type'] 44 | # except KeyError: 45 | # raise serializers.ValidationError({ 46 | # 'type': 'This field is required', 47 | # }) 48 | # 49 | # try: 50 | # serializer = self.get_serializer_map()[type_str] 51 | # except KeyError: 52 | # raise serializers.ValidationError({ 53 | # 'type': 'Serializer for "{}" does not exist'.format(type_str), 54 | # }) 55 | # 56 | # validated_data = serializer(context=self.context).to_internal_value(data) 57 | # validated_data['type'] = type_str 58 | # return validated_data 59 | # 60 | # def create(self, validated_data): 61 | # """ 62 | # Translate validated data representation to object 63 | # 64 | # Override to allow polymorphism 65 | # """ 66 | # serializer = self.get_serializer_map()[validated_data['type']] 67 | # validated_data.pop('type') 68 | # return serializer(context=self.context).create(validated_data) 69 | # 70 | # def update(self, instance, validated_data): 71 | # serializer = self.get_serializer_map()[validated_data['type']] 72 | # validated_data.pop('type') 73 | # return serializer(context=self.context).update(instance, validated_data) -------------------------------------------------------------------------------- /Backend/protocol_element/models/PEDecision.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models 5 | 6 | from protocol_element.models import ProtocolElement, PENextElements 7 | from patients.models import ClinicalVariable 8 | 9 | import operator 10 | 11 | class PEDecision(ProtocolElement): 12 | clinicalVariable = models.ForeignKey(ClinicalVariable) 13 | condition = models.CharField(max_length=150) 14 | nextElement = models.ManyToManyField(PENextElements) 15 | 16 | operation = { 17 | "<": operator.lt, 18 | ">": operator.gt, 19 | "=": operator.eq 20 | } 21 | 22 | def run(self, inquiryData): 23 | cvData = inquiryData[self.clinicalVariable.variable] 24 | result = self.operation[self.condition[0]](cvData, self.condition[1:]) 25 | nextElementOptions = self.nextElement.all() 26 | for nextElement in nextElementOptions: 27 | if nextElement.option == str(result): 28 | return nextElement.getNextElementId() 29 | return None 30 | 31 | @staticmethod 32 | def new(id, clinicalVariable, protocol, condition): 33 | cv = ClinicalVariable.objects.get(variable=clinicalVariable) 34 | decision = PEDecision.objects.create(clinicalVariable=cv, condition=condition, internalId=id, protocol=protocol) 35 | return decision.save() 36 | 37 | @staticmethod 38 | def addNextElements(id, protocol, nextElements): 39 | pe = ProtocolElement.get(type=ProtocolElement.DECISION, internalId=int(id), protocol=protocol) 40 | for option, id in nextElements.iteritems(): 41 | nextElement = PENextElements.new(option=option, nextElementId=int(id), protocol=protocol) 42 | pe.nextElement.add(nextElement) 43 | pe.save() 44 | 45 | @staticmethod 46 | def dealWithOptions(conditionString, conditionType): 47 | nextElementOptions = {} 48 | #Condition with true or false output 49 | if(conditionType[0] in PEDecision.operation): 50 | conditions = conditionString.split(";") 51 | for condition in conditions: 52 | splitedCondition = condition.split(":") 53 | if splitedCondition[0] == 'True' or splitedCondition[0] == True: 54 | nextElementOptions[True] = splitedCondition[1] 55 | else: 56 | nextElementOptions[False] = splitedCondition[1] 57 | #elif switch todo 58 | return nextElementOptions 59 | 60 | @staticmethod 61 | def all(): 62 | ''' 63 | Returns all decision protocol elements 64 | ''' 65 | 66 | tmpAll = PEDecision.objects.all() 67 | 68 | return tmpAll.order_by('internalId') -------------------------------------------------------------------------------- /Backend/patients/api/views/PatientCVsViewSet.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import viewsets, filters 5 | from rest_framework.decorators import list_route 6 | from rest_framework.response import Response 7 | 8 | from django.db import transaction 9 | from django.utils import timezone 10 | 11 | from patients.api.serializers import CVPatientSerializer, CVGroupSerializer, PatientSerializer 12 | 13 | from patients.models import CVPatient, CVGroup, ClinicalVariable, Patient 14 | 15 | from history.models import History 16 | 17 | from itertools import groupby 18 | 19 | class PatientCVsViewSet(viewsets.ModelViewSet): 20 | queryset = Patient.all(active=True) 21 | serializer_class = PatientSerializer 22 | 23 | def retrieve(self, request, *args, **kwargs): 24 | patient = self.get_object() 25 | return self.buildResponse(patient) 26 | 27 | #Refactor this URGENT 28 | def buildResponse(self, patient): 29 | headers = CVGroup.all() 30 | headerSerialized = CVGroupSerializer(headers, many=True) 31 | 32 | results = [] 33 | for group in headers: 34 | obj = {"group": group.title} 35 | cvs = CVPatient.all(patient=patient, group=group) 36 | content = [] 37 | cvsSplitedByDate = [list(grp) for i, grp in 38 | groupby(sorted(cvs.values()), key=lambda item: item["measure_date"])] 39 | 40 | # These nested fors hurts my soul, but i don't know a better solution 41 | for cvsInThatDate in cvsSplitedByDate: 42 | cvToAddInResponse = {"measure_date": cvsInThatDate[0]["measure_date"].strftime("%Y-%m-%d %H:%M")} 43 | for cv in cvsInThatDate: 44 | cvName = ClinicalVariable.objects.get(id=cv["variable_id"]).variable 45 | cvToAddInResponse[cvName] = cv["value"] 46 | content += [cvToAddInResponse] 47 | 48 | obj["content"] = content 49 | results += [obj] 50 | 51 | return Response({ 52 | "headers": headerSerialized.data, 53 | "results": results 54 | }) 55 | 56 | @list_route(methods=['post']) 57 | @transaction.atomic 58 | def addVariables(self, request, *args, **kwargs): 59 | measure_date = timezone.now() 60 | group = CVGroup.objects.get(title=request.data["group"]) 61 | patient = Patient.objects.get(id=request.data["patient"]) 62 | 63 | for cv in request.data: 64 | if(cv != "group" and cv != "patient"): 65 | variable = cv 66 | value = request.data[cv] 67 | CVPatient.new(patient, group, variable, value, measure_date) 68 | 69 | return self.buildResponse(patient) -------------------------------------------------------------------------------- /UI/src/js/components/patient/ClinicalVariables.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Reflux from 'reflux'; 3 | import {PatientClinicalVariablesStore, PatientClinicalVariablesActions} from '../../reflux/PatientClinicalVariablesReflux.js'; 4 | import CVRepresentationGroup from './CVRepresentationGroup.js'; 5 | 6 | import Tabs, {TabPane} from 'rc-tabs'; 7 | import TabContent from 'rc-tabs/lib/TabContent'; 8 | import ScrollableInkTabBar from 'rc-tabs/lib/ScrollableInkTabBar'; 9 | 10 | class ClinicalVariables extends Reflux.Component { 11 | constructor(props) { 12 | super(props); 13 | this.store = PatientClinicalVariablesStore; 14 | this.state = { 15 | patientID:this.props.patientID 16 | } 17 | } 18 | 19 | componentDidUpdate(prevProps, prevState) { 20 | if (prevProps.patientID !== this.props.patientID) { 21 | PatientClinicalVariablesActions.load(this.props.patientID); 22 | this.setState({patientID: this.props.patientID}); 23 | } 24 | } 25 | 26 | buildDataComponents = () => { 27 | let listOfComponents = []; 28 | let receivedList = this.state.headers; 29 | receivedList.sort(function (a, b) { 30 | return a.index_representation - b.index_representation; 31 | }); 32 | 33 | for (let index = 0; index < receivedList.length; index++) { 34 | let content = this.state.data.filter(function (obj) { 35 | return obj.group === receivedList[index]["title"]; 36 | }); 37 | listOfComponents.push(
38 | 43 |
); 44 | } 45 | 46 | return listOfComponents; 47 | }; 48 | 49 | componentDidMount() { 50 | PatientClinicalVariablesActions.load(this.state.patientID); 51 | } 52 | 53 | render() { 54 | let listOfComponents = this.buildDataComponents(); 55 | 56 | return ( 57 |
58 | } 61 | renderTabContent={() => } 62 | > 63 | {listOfComponents} 64 | 65 |
66 | 67 | ); 68 | } 69 | } 70 | 71 | export default ClinicalVariables; 72 | -------------------------------------------------------------------------------- /UI/src/js/reflux/UserReflux.js: -------------------------------------------------------------------------------- 1 | import Reflux from 'reflux'; 2 | import API from '../API.js'; 3 | import History from '../components/globalComponents/History.js'; 4 | 5 | const UserActions = Reflux.createActions([ 6 | 'login', 7 | 'logout', 8 | 'loginSuccess', 9 | 'loginFailed', 10 | 'getUserData', 11 | 'updateUserData' 12 | ]); 13 | 14 | class UserStore extends Reflux.Store { 15 | constructor() { 16 | super(); 17 | this.listenables = UserActions; 18 | this.state = { 19 | authenticated: false, 20 | failed: false, 21 | user: undefined 22 | }; 23 | } 24 | 25 | onLogin(user, password, remember) { 26 | API.POST("account", "login", { 27 | "username": user, 28 | "password": password, 29 | "remember": remember 30 | }).then(res => { 31 | UserActions.loginSuccess(res.data, res.data["authenticated"]); 32 | }) 33 | } 34 | 35 | onGetUserData() { 36 | API.GET("account", "personalAccountDetails") 37 | .then(res => { 38 | this.setState({ 39 | authenticated: res.data["authenticated"], 40 | user: res.data 41 | }); 42 | this.trigger(); 43 | }) 44 | } 45 | 46 | onUpdateUserData() { 47 | let userData = { 48 | "first_name": this.state.user.first_name, 49 | "last_name": this.state.user.last_name, 50 | }; 51 | 52 | if(this.state.user.password !== undefined) 53 | userData["password"] = this.state.user.password; 54 | 55 | API.PATCH("account", "personalAccountDetails", userData).then(res => { 56 | this.setState({ 57 | authenticated: res.data["authenticated"], 58 | user: res.data 59 | }); 60 | History.push('/'); 61 | }) 62 | } 63 | 64 | onLogout() { 65 | API.GET("account", "logout") 66 | .then(res => { 67 | this.setState({authenticated: res.data["authenticated"]}); 68 | History.push('/'); 69 | window.location.reload(); 70 | }) 71 | } 72 | 73 | onLoginSuccess(data, authenticated) { 74 | if (authenticated === false) { 75 | UserActions.loginFailed(); 76 | } else { 77 | this.setState({ 78 | authenticated: authenticated, 79 | failed: !authenticated, 80 | user: data 81 | }); 82 | History.push('/admittedpatients'); 83 | } 84 | this.trigger(); 85 | } 86 | 87 | onLoginFailed() { 88 | this.setState({failed: true}); 89 | this.trigger(); 90 | } 91 | } 92 | 93 | export {UserStore, UserActions}; -------------------------------------------------------------------------------- /Backend/patients/api/views/AdmissionViewSet.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from rest_framework import viewsets, filters 5 | from rest_framework.decorators import detail_route, list_route 6 | from rest_framework.response import Response 7 | 8 | from rest_framework.filters import OrderingFilter 9 | from django_filters.rest_framework import DjangoFilterBackend 10 | from django.db import transaction 11 | from django.utils import timezone 12 | 13 | from patients.api.serializers import AdmissionSerializer 14 | from patients.models import Admission, Patient 15 | 16 | from accounts.models import Profile 17 | 18 | from protocol.models import ExecutedProtocol, Protocol, Schedule 19 | 20 | from history.models import History 21 | 22 | import dateutil.parser 23 | 24 | class AdmissionViewSet(viewsets.ModelViewSet): 25 | queryset = Admission.all() 26 | serializer_class = AdmissionSerializer 27 | 28 | filter_backends = [DjangoFilterBackend, OrderingFilter] 29 | filter_fields = ["physician", "patient", "start_date"] 30 | 31 | def list(self, request, *args, **kwargs): 32 | ''' 33 | Return a list of patients admited in the hospital and all these informations 34 | ''' 35 | self.queryset = Admission.all(active=True) 36 | return super(AdmissionViewSet, self).list(request, *args, **kwargs) 37 | 38 | @list_route(methods=['post']) 39 | @transaction.atomic 40 | def new(self, request, *args, **kwargs): 41 | ''' 42 | Admission of a patient registed in the system and all the assigned protocols. 43 | ''' 44 | #Create admission 45 | patient = Patient.objects.get(id=request.data.get('patientID')) 46 | physician = Profile.objects.get(user=request.user) 47 | Admission.new(patient=patient, 48 | physician=physician, 49 | room=request.data.get('room')) 50 | #Assign protocols 51 | selectedProtocols = request.data.get('seletedProtocols') #For now it is only one 52 | for selectedProtocol in selectedProtocols: 53 | protocol = Protocol.objects.get(id=selectedProtocol.get("id")) 54 | ExecutedProtocol.new(protocol=protocol, patient=patient, physician=physician) 55 | 56 | self.queryset = Admission.all(active=True) 57 | return super(AdmissionViewSet, self).list(request, *args, **kwargs) 58 | 59 | @list_route(methods=['post']) 60 | @transaction.atomic 61 | def discharge(self, request, *args, **kwargs): 62 | patientID = request.data.get('patientID', None) 63 | 64 | if patientID != None: 65 | patient = Patient.objects.get(id=patientID) 66 | Admission.dischargePatient(patient) 67 | 68 | return Response({ 69 | 'success': True 70 | }) 71 | 72 | return Response({ 73 | 'error': "The patient was not found" 74 | }) -------------------------------------------------------------------------------- /UI/src/js/App.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import Reflux from 'reflux'; 3 | import Header from './components/globalComponents/Header.js'; 4 | import Footer from './components/globalComponents/Footer.js'; 5 | import Routes from './components/globalComponents/Routes.js'; 6 | import {Router} from "react-router-dom"; 7 | import {UserStore, UserActions} from './reflux/UserReflux.js'; 8 | import {StateStore} from './reflux/StateReflux.js'; 9 | import History from './components/globalComponents/History.js'; 10 | import Modal from 'react-awesome-modal'; 11 | import API from './API.js'; 12 | 13 | class LoadingBar extends Component { 14 | render() { 15 | return ( 16 |
17 |
18 |

Loading, please wait...

19 |
); 20 | } 21 | } 22 | 23 | class App extends Reflux.Component { 24 | constructor(props) { 25 | super(props); 26 | this.stores = [StateStore, UserStore]; 27 | } 28 | 29 | componentDidMount() { 30 | UserActions.getUserData(); 31 | this.setAppTitle(); 32 | } 33 | 34 | setAppTitle = () => { 35 | API.GET("settings") 36 | .then(res => { 37 | if(res.data["title"] !== "") 38 | document.title = res.data["title"]; 39 | }); 40 | }; 41 | 42 | closeModal = () => {/*To remove the warning*/}; 43 | 44 | render() { 45 | if (this.state.loading && this.state.user === undefined) 46 | return (); 47 | 48 | return ( 49 | 50 |
51 |
52 |
53 | 54 |
55 |
56 | 57 | 64 |
65 | {this.state.modalHeader === undefined ? '' : this.state.modalHeader} 66 |
67 |
68 | {this.state.modalContent === undefined ? '' : this.state.modalContent} 69 |
70 |
71 | {this.state.modalFooter === undefined ? '' : this.state.modalFooter} 72 |
73 |
74 | 75 |
76 |
77 | ); 78 | } 79 | } 80 | 81 | export default App; 82 | -------------------------------------------------------------------------------- /config/run_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "------------------------------------------" 4 | echo "-------------- Check DB UP ---------------" 5 | echo "------------------------------------------" 6 | 7 | check_up() { 8 | service=$1 9 | host=$2 10 | port=$3 11 | 12 | max=13 # 1 minute 13 | 14 | counter=1 15 | while true;do 16 | python -c "import socket;s = socket.socket(socket.AF_INET, socket.SOCK_STREAM);s.connect(('$host', $port))" \ 17 | >/dev/null 2>/dev/null && break || \ 18 | echo "Waiting that $service on $host:${port} is started (sleeping for 5) on counter ${counter}" 19 | 20 | if [ $counter = $max ]; then 21 | echo "Could not connect to ${service} after some time" 22 | echo "Investigate locally the logs with fig logs" 23 | exit 1 24 | fi 25 | 26 | sleep 5 27 | 28 | counter=$(expr "$counter" + "1") 29 | done 30 | } 31 | 32 | check_up "postgres" db 5432 33 | 34 | echo "------------------------------------------" 35 | echo "------------- Backend Deploy -------------" 36 | echo "------------------------------------------" 37 | cd /GenericCDSS 38 | 39 | if [ ${DEPLOY_MODE} = "demo" ]; then 40 | make defaultDemo 41 | else 42 | make setUpSystem 43 | fi 44 | 45 | cd /GenericCDSS/Backend 46 | 47 | python manage.py collectstatic --noinput 48 | 49 | exec gunicorn genericcdss.wsgi:application --bind 0.0.0.0:8000 --workers 3 & 50 | 51 | echo "------------------------------------------" 52 | echo "------------- Frontend Deploy ------------" 53 | echo "------------------------------------------" 54 | cd /GenericCDSS/UI 55 | 56 | echo "Defing api url..." 57 | apiURL=$(echo $API_URL) 58 | if [ -z "$apiURL" ] 59 | then 60 | echo "API_URL not defined" 61 | else 62 | echo "Change API URL in package" 63 | jq -c 'del(.api_url)' package.json > tmp.json && mv tmp.json package.json 64 | 65 | jq -c '. + { "api_url": "'$apiURL'" }' package.json > tmp.json && mv tmp.json package.json 66 | fi 67 | 68 | echo "Defining homepage url..." 69 | homepage=$(echo $HOMEPAGE) 70 | if [ -z "$homepage" ] 71 | then 72 | echo "Homepage url not defined" 73 | else 74 | echo "Change homepage in package" 75 | jq -c 'del(.homepage)' package.json > tmp.json && mv tmp.json package.json 76 | jq -c '. + { "homepage": "'$homepage'" }' package.json > tmp.json && mv tmp.json package.json 77 | fi 78 | 79 | echo "Defining baseurl..." 80 | base_url=$(echo $BASE_URL) 81 | if [ -z "$base_url" ] 82 | then 83 | echo "Homepage url not defined" 84 | else 85 | echo "Change homepage in package" 86 | jq -c 'del(.baseurl)' package.json > tmp.json && mv tmp.json package.json 87 | jq -c '. + { "base_url": "'$base_url'" }' package.json > tmp.json && mv tmp.json package.json 88 | fi 89 | 90 | echo "Defining version..." 91 | version=$(echo $VERSION) 92 | if [ -z "$version" ] 93 | then 94 | echo "Version not defined" 95 | else 96 | echo "Change version in package" 97 | jq -c 'del(.version)' package.json > tmp.json && mv tmp.json package.json 98 | jq -c '. + { "version": "'$version'" }' package.json > tmp.json && mv tmp.json package.json 99 | fi 100 | 101 | npm run build 102 | 103 | tail -f /dev/null 104 | -------------------------------------------------------------------------------- /UI/src/js/components/protocol/Protocols.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Reflux from 'reflux'; 3 | import PropTypes from 'prop-types'; 4 | import ReactTable from 'react-table' 5 | import "react-table/react-table.css"; 6 | import {Link} from "react-router-dom"; 7 | import {ProtocolStore, ProtocolActions} from '../../reflux/ProtocolReflux.js'; 8 | import Settings from '../../GlobalSettings.js'; 9 | 10 | class Protocols extends Reflux.Component { 11 | constructor(props) { 12 | super(props); 13 | this.store = ProtocolStore; 14 | } 15 | 16 | componentDidMount() { 17 | ProtocolActions.load(); 18 | } 19 | 20 | render() { 21 | let columns = []; 22 | if (this.props.selectColumn === true) 23 | columns.push({ 24 | Header: () =>
, 25 | id: "selection", 26 | maxWidth: 33, 27 | filterable: false, 28 | accessor: obj => obj.id, 29 | Cell: props => 32 | }); 33 | 34 | columns.push({ 35 | Header: () =>
Title
, 36 | id: "title", 37 | accessor: obj => obj.title, 38 | Cell: props => {props.value} 39 | }); 40 | columns.push({ 41 | Header: () =>
Description
, 42 | id: "description", 43 | accessor: obj => obj.description, 44 | Cell: props => {props.value} 45 | }); 46 | 47 | return ( 48 |
49 |
50 |
51 | 52 |

Protocols

53 | 54 | Insert new protocol 55 |
56 |
57 | 64 | 65 |
66 |
67 |
68 | ); 69 | } 70 | 71 | static propTypes = { 72 | /** 73 | * Number of row in the table 74 | * */ 75 | rows: PropTypes.number 76 | }; 77 | } 78 | 79 | 80 | Protocols.defaultProps = { 81 | rows: Settings.getPatientTableRows() 82 | }; 83 | 84 | export default Protocols; 85 | -------------------------------------------------------------------------------- /UI/src/js/components/buttons/PatientButtonBar.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {PatientActions} from '../../reflux/PatientReflux.js'; 3 | import {AdmissionActions} from '../../reflux/AdmissionReflux.js'; 4 | import History from '../globalComponents/History.js'; 5 | import PatientStatus from '../patient/PatientStatus.js'; 6 | import ButtonWithMsg from '../reusable/ButtonWithMsg.js'; 7 | 8 | class PatientButtonBar extends Component { 9 | addPatient = () => { 10 | if(this.props.patientIsValid()) 11 | PatientActions.addPatient(); 12 | }; 13 | 14 | admitPatient = () => { 15 | History.push('/assignprotocol/' + this.props.patient.id); 16 | }; 17 | 18 | dischargePatient = () => { 19 | AdmissionActions.dischargePatient(this.props.patient.id); 20 | }; 21 | 22 | render() { 23 | let patientStatus = this.props.patient.status; 24 | 25 | switch (this.props.mode){ 26 | case "show": 27 | if(patientStatus === PatientStatus.DISCHARGED) 28 | return( 29 |
30 |
31 | 33 |
34 |
35 | ); 36 | if(patientStatus === PatientStatus.ADMITTED) 37 | return( 38 |
39 | 48 |
49 | ); 50 | break; 51 | 52 | case "add": 53 | return( 54 |
55 |
56 | 58 |
59 |
60 | ); 61 | //break; 62 | 63 | case "edit": 64 | return( 65 |
TO DO 66 |
67 | ); 68 | //break; 69 | case "admitting": return(
); 70 | 71 | default: return(
); 72 | } 73 | return(
); 74 | } 75 | } 76 | 77 | export default PatientButtonBar; 78 | -------------------------------------------------------------------------------- /UI/src/js/components/patient/AllPatients.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Reflux from 'reflux'; 3 | import ReactTable from 'react-table' 4 | import "react-table/react-table.css"; 5 | import {Link} from "react-router-dom"; 6 | import {PatientStore, PatientActions} from '../../reflux/PatientReflux.js'; 7 | import Settings from '../../GlobalSettings.js'; 8 | import PatientStatus from './PatientStatus.js'; 9 | 10 | class AllPatients extends Reflux.Component { 11 | constructor(props) { 12 | super(props); 13 | this.store = PatientStore; 14 | } 15 | 16 | componentDidMount() { 17 | PatientActions.load(); 18 | } 19 | 20 | render() { 21 | const columns = [{ 22 | Header: () =>
, 23 | id: "gender", 24 | maxWidth: 33, 25 | filterable: false, 26 | accessor: obj => obj.gender, 27 | Cell: props => props.value === "M" ? : 28 | },{ 29 | Header: () =>
Name
, 30 | id: "fullname", 31 | accessor: obj => obj.fullname, 32 | Cell: props => {props.value} 33 | },{ 34 | Header:() =>
Condition
, 35 | id: "status", 36 | accessor: obj => obj.status, 37 | Cell: props => {PatientStatus.toString(props.value)} 38 | },{ 39 | Header:() =>
Contact
, 40 | id: "contacto", 41 | accessor: obj => obj.phone, 42 | Cell: props => {props.value} 43 | },{ 44 | Header: () =>
Email
, 45 | id: "email", 46 | accessor: obj => obj.email, 47 | Cell: props => {props.value} 48 | }]; 49 | 50 | 51 | return ( 52 |
53 |
54 |
55 | 56 |

Patients

57 | 58 | 59 | Insert new patient 60 |
61 |
62 | 73 | 74 |
75 |
76 |
77 | ); 78 | } 79 | } 80 | 81 | export default AllPatients; 82 | -------------------------------------------------------------------------------- /UI/src/js/components/globalComponents/Routes.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {Route, Switch, Redirect} from "react-router-dom"; 3 | 4 | import Home from '../dynamicPages/Home.js'; 5 | import Help from '../dynamicPages/Help.js'; 6 | import About from '../dynamicPages/About.js'; 7 | 8 | import AdmittedPatients from '../patient/AdmittedPatients.js'; 9 | import AllPatients from '../patient/AllPatients.js'; 10 | import ShowPatient from '../patient/ShowPatient.js'; 11 | import AddPatient from '../patient/AddPatient.js'; 12 | 13 | import Protocols from '../protocol/Protocols.js'; 14 | import AddProtocol from '../protocol/CRUDProtocol.js'; 15 | import ShowProtocol from '../protocol/CRUDProtocol.js'; 16 | import AssignProtocolToPatient from '../protocol/AssignProtocolToPatient.js'; 17 | 18 | import Register from '../accountManager/Register.js'; 19 | import Profile from '../accountManager/Profile.js'; 20 | import ForgotPassword from '../accountManager/ForgotPass.js'; 21 | 22 | import http404 from '../errorPages/http404.js'; 23 | import http500 from '../errorPages/http500.js'; 24 | import http0 from '../errorPages/http0.js'; 25 | 26 | 27 | 28 | const PrivateRoute = ({component: Component, ...rest}) => ( 29 | ( 30 | rest.authenticated ? ( 31 | 32 | ) : ( 33 | 37 | ) 38 | )}/> 39 | ); 40 | 41 | class Routes extends Component { 42 | render() { 43 | if (!this.props.user) 44 | return (); 45 | 46 | let authenticated = this.props.user.authenticated; 47 | return ( 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | ); 71 | } 72 | } 73 | 74 | export default Routes; -------------------------------------------------------------------------------- /Backend/accounts/api/serializers/UserSerializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.contrib.auth.models import User 5 | from django.db import transaction 6 | from django.contrib.auth import login 7 | 8 | from rest_framework import serializers 9 | from rest_framework import permissions 10 | 11 | #from oauth2_provider.ext.rest_framework import TokenHasScope 12 | 13 | from accounts.models import Profile 14 | from accounts.api.serializers import ProfileSerializer 15 | 16 | class UserSerializer(serializers.ModelSerializer): 17 | fullname = serializers.SerializerMethodField(required=False) 18 | last_login = serializers.SerializerMethodField(required=False) 19 | profile = ProfileSerializer() 20 | 21 | class Meta: 22 | permission_classes = [permissions.IsAuthenticated, permissions.IsAdminUser]#, TokenHasScope] 23 | model = User 24 | fields = ('username', 'first_name', 'last_name', 'fullname', 'email', 'last_login', 'is_staff', 'id', 'profile') 25 | read_only_fields = ('id', 'fullname', 'last_login', 'is_staff') 26 | 27 | def get_fullname(self, obj): 28 | full_name = obj.get_full_name() 29 | 30 | if full_name == "": 31 | return obj.email 32 | return full_name 33 | 34 | def get_last_login(self, obj): 35 | if isinstance(obj.last_login, basestring): 36 | return obj.last_login 37 | elif obj.last_login: 38 | return obj.last_login.strftime("%Y-%m-%d %H:%M") 39 | return None 40 | 41 | @transaction.atomic 42 | def create(self, validated_data): 43 | ''' 44 | This handles the custom user creation, serializating validated data from the web services input 45 | into the proper object, plus also inserting profile information, all in the same serialization. 46 | ''' 47 | profile_data = None 48 | validated_data['is_active'] = False 49 | 50 | try: 51 | profile_data = validated_data.pop('profile') 52 | except KeyError: 53 | pass 54 | 55 | user = User.objects.create(**validated_data) 56 | 57 | # create profile data 58 | if profile_data: 59 | try: 60 | profile_instance = user.profile 61 | serializer = ProfileSerializer(profile_instance, partial=True) 62 | serializer.update(profile_instance, profile_data) 63 | except Profile.DoesNotExist: 64 | Profile.objects.create(user=user, **profile_data) 65 | 66 | return user 67 | 68 | @transaction.atomic 69 | def update(self, instance, validated_data): 70 | ''' 71 | This handles the custom user update, serializating validated data from the web services input 72 | into the proper object, plus also updating profile information, all in the same serialization. 73 | ''' 74 | profile_data = None 75 | 76 | try: 77 | profile_data = validated_data.pop('profile') 78 | except KeyError: 79 | pass 80 | 81 | if profile_data: 82 | p_instance = instance.profile 83 | serializer = ProfileSerializer(p_instance, partial=True) 84 | serializer.update(p_instance, profile_data) 85 | 86 | for attr, value in validated_data.items(): 87 | setattr(instance, attr, value) 88 | 89 | instance.save() 90 | 91 | return instance -------------------------------------------------------------------------------- /Backend/utils/management/commands/populate_flatpages.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from django.core.management.base import BaseCommand 3 | from django.contrib.flatpages.models import FlatPage 4 | from django.contrib.sites.models import Site 5 | 6 | class Command(BaseCommand): 7 | help = 'This command will populate the db with random data to create a demo installation' 8 | 9 | def add_arguments(self, parser): 10 | parser.add_argument( 11 | '--force', 12 | action='store_true', 13 | dest='force', 14 | help='Force the deletion and the creation of the flat pages', 15 | ) 16 | 17 | def handle(self, *args, **options): 18 | siteDomain = "AutoPages" 19 | if options['force']: 20 | self.stdout.write("\nCleaning the old auto flat pages!\n\n") 21 | self.remove_FlatPages(siteDomain) 22 | self.stdout.write("\nCreating new auto flat pages!\n\n") 23 | self.create_FlatPages(siteDomain) 24 | else: 25 | try: 26 | Site.objects.get(domain=siteDomain) 27 | self.stdout.write("\nERROR:The flat pages are already populated!\n\n") 28 | except: 29 | self.create_FlatPages(siteDomain) 30 | 31 | def remove_FlatPages(self, siteDomain): 32 | try: 33 | site = Site.objects.get(domain=siteDomain) 34 | FlatPage.objects.filter(sites=site).delete() 35 | except: 36 | self.stdout.write("\nThe auto flat pages do not exist in the system!\n\n") 37 | 38 | 39 | def create_FlatPages(self, siteDomain): 40 | try: 41 | site = Site.objects.get(domain=siteDomain) 42 | except: 43 | site = Site.objects.create(domain=siteDomain, 44 | name=siteDomain) 45 | site.save() 46 | 47 | FlatPage.objects.create(url="/home/", 48 | title="Home", 49 | content='').sites.add(site) 50 | FlatPage.objects.create(url="/about/", 51 | title="About", 52 | content='

About

\ 53 |

\ 54 | This system is a web-based application, which provides the main dashboard where professionals (e.g, practitioners, nurses) can follow all the patients that are under their responsibility and some details about the state of each one.\ 55 |

\ 56 |

Available features

\ 57 |
    \ 58 |
  • Manage all the patient information dynamically
  • \ 59 |
  • Create and manage clinical protocols
  • \ 60 |
  • Assign protocols to the patients and execute them
  • \ 61 |
  • Easly costumize the patient information
  • \ 62 |
  • Keep track of all the patient data
  • \ 63 |
  • Be reminder about the next measurement for each patient admitted in the system
  • \ 64 |
').sites.add(site) 65 | FlatPage.objects.create(url="/help/", 66 | title="Help", 67 | content="TO DO").sites.add(site) 68 | 69 | self.stdout.write("Success: The flat pages populated with success!\n") -------------------------------------------------------------------------------- /UI/src/js/components/protocolElements/ActionElement.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Reflux from 'reflux'; 4 | import DisplayField from '../reusable/DisplayField.js'; 5 | 6 | class ActionElement extends Reflux.Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = { 10 | nextElementId: "",//(this.props.elementID + 1).toString(), 11 | action: "" 12 | }; 13 | } 14 | 15 | componentDidMount() { 16 | this.loadingDetails(); 17 | } 18 | 19 | loadingDetails = () => { 20 | if(this.props.mode === "edit"){ 21 | this.props.addElementConfigurations("action", this.props.elementData.action); 22 | this.props.addElementConfigurations("nextElement", this.props.elementData.nextElement); 23 | this.setState({ 24 | action: this.props.elementData.action, 25 | nextElementId: this.props.elementData.nextElement 26 | }) 27 | } 28 | }; 29 | 30 | isValid = () => { 31 | this.setState({validated: true}); 32 | return (this.state.action !== "" && (this.state.nextElementId === "" || this.state.nextElementId > this.props.elementID)); 33 | }; 34 | 35 | actionHandleChange = (event) => { 36 | event.preventDefault(); 37 | this.props.addElementConfigurations("action", event.target.value); 38 | this.setState({action: event.target.value}); 39 | }; 40 | 41 | nextElementIdHandleChange = (event) => { 42 | event.preventDefault(); 43 | this.props.addElementConfigurations("nextElement", event.target.value); 44 | this.setState({nextElementId: event.target.value}); 45 | }; 46 | 47 | render() { 48 | return ( 49 |
50 | 56 | 64 |
65 | ); 66 | } 67 | 68 | static propTypes = { 69 | /** 70 | * Next element id 71 | * */ 72 | nextElementId: PropTypes.number, 73 | /** 74 | * Send the protocol configurations to the parent 75 | * 76 | * @param key 77 | * @param value 78 | * */ 79 | addElementConfigurations: PropTypes.func, 80 | /** 81 | * Object with the element data (important in the edition mode) 82 | * */ 83 | elementData: PropTypes.object 84 | }; 85 | } 86 | 87 | ActionElement.defaultProps = { 88 | elementID: 1 89 | }; 90 | 91 | export default ActionElement; -------------------------------------------------------------------------------- /Backend/patients/models/Admission.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models 5 | from django.utils import timezone 6 | 7 | from accounts.models import Profile 8 | 9 | from patients.models import Patient 10 | 11 | from protocol.models import AssignedProtocol, ExecutedProtocol 12 | 13 | class Admission(models.Model): 14 | patient = models.ForeignKey(Patient) 15 | physician = models.ForeignKey(Profile, limit_choices_to={'role': Profile.PHYSICIAN}) 16 | room = models.CharField(max_length=10, blank=True) 17 | start_date = models.DateTimeField(auto_now_add=True) 18 | end_date = models.DateTimeField(null=True, blank=True) 19 | 20 | def __unicode__(self): 21 | return u"Admission %s - %s" % (self.patient, self.room) 22 | 23 | def discharge(self): 24 | ''' 25 | Insert the admission finished date of the patient that was discharged from the hospital 26 | ''' 27 | self.patient.discharge() 28 | self.end_date = timezone.now() 29 | self.save() 30 | ExecutedProtocol.cancelAllAssigned(patient=self.patient) 31 | 32 | def getLastProtocolAssignedMeasure(self): 33 | ''' 34 | Retrieves when was made the last protocol measurement 35 | :return: datetime in string format 36 | ''' 37 | lastProtocolExecution = ExecutedProtocol.getLastExecution(patient=self.patient, admissionDate=self.start_date) 38 | if(lastProtocolExecution): 39 | return lastProtocolExecution.execution_time.strftime("%Y-%m-%d %H:%M") 40 | return "" 41 | 42 | def getLastProtocolAssignedMeasurePhysician(self): 43 | ''' 44 | Retrieves who made the last protocol measurement 45 | :return: datetime in string format 46 | ''' 47 | lastProtocolExecution = ExecutedProtocol.getLastExecution(patient=self.patient, admissionDate=self.start_date) 48 | if(lastProtocolExecution): 49 | return lastProtocolExecution.physician 50 | return "" 51 | 52 | def getNextProtocolAssignedMeasure(self): 53 | ''' 54 | It returns when the patient information should be measured 55 | :return: tuple (datetime, schedule title) 56 | ''' 57 | nextExecution = ExecutedProtocol.getNextExecution(patient=self.patient) 58 | return (nextExecution.schedule_time.strftime("%Y-%m-%d %H:%M"), nextExecution.schedule.title) 59 | 60 | @staticmethod 61 | def new(patient, physician, room): 62 | admission = Admission.objects.create(patient=patient, 63 | physician=physician, 64 | room=room) 65 | patient.admit() 66 | # History to do 67 | admission.save() 68 | 69 | @staticmethod 70 | def all(active=None): 71 | ''' 72 | Returns all admission instances 73 | ''' 74 | tmpAll = Admission.objects.all() 75 | 76 | if active == True: 77 | tmpAll = tmpAll.filter(end_date__isnull=True) 78 | 79 | return tmpAll.order_by('start_date') 80 | 81 | @staticmethod 82 | def dischargePatient(patient): 83 | admission = Admission.getLatestAdmission(patient) 84 | admission.discharge() 85 | 86 | @staticmethod 87 | def getLatestAdmission(patient): 88 | listOfActiveAdmissionsFromPatient = Admission.all(active=True).filter(patient=patient) 89 | #this should be one (TO DO something to ensure that in the future) 90 | return listOfActiveAdmissionsFromPatient[0] -------------------------------------------------------------------------------- /UI/src/js/components/protocol/SelectedProtocols.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import ReactTable from 'react-table' 3 | import "react-table/react-table.css"; 4 | import {Link} from "react-router-dom"; 5 | import Settings from '../../GlobalSettings.js'; 6 | import $ from 'jquery'; 7 | 8 | class SelectedProtocols extends Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { 12 | selectedProtocols: this.props.selectedProtocols 13 | } 14 | } 15 | 16 | componentDidUpdate(prevProps, prevState) { 17 | if (prevProps.selectedProtocols.length !== this.props.selectedProtocols.length) { 18 | this.setState({selectedProtocols: this.props.selectedProtocols}); 19 | } 20 | } 21 | 22 | removeSelectedProtocol = (state, rowInfo, column, instance) => { 23 | return { 24 | onClick: (e, handleOriginal) => { 25 | let temporarySelectedProtocols = this.state.selectedProtocols; 26 | temporarySelectedProtocols.splice(rowInfo.index, 1) 27 | this.setState({selectedProtocols: temporarySelectedProtocols}); 28 | } 29 | }; 30 | }; 31 | 32 | 33 | render() { 34 | const columns = [{ 35 | Header: () =>
, 36 | id: "selection", 37 | maxWidth: 33, 38 | filterable: false, 39 | accessor: obj => obj.id, 40 | Cell: props => 42 | }, { 43 | Header: () =>
Title
, 44 | id: "title", 45 | accessor: obj => obj.title, 46 | Cell: props => {props.value} 47 | }, { 48 | Header: () =>
Start
, 49 | id: "start_date", 50 | accessor: obj => obj.start_date, 51 | Cell: props => {props.value} 52 | }, { 53 | Header: () =>
End
, 54 | id: "end_date", 55 | accessor: obj => obj.end_date, 56 | Cell: props => {props.value} 57 | }, { 58 | Header: () =>
Schedule
, 59 | id: "schedule", 60 | accessor: obj => obj.schedule, 61 | Cell: props => {props.value} 62 | }]; 63 | 64 | return ( 65 |
66 |
67 |
68 | 69 |

Selected protocols

70 |
71 |
72 | 79 | 80 |
81 |
82 |
83 | ); 84 | } 85 | } 86 | 87 | export default SelectedProtocols; -------------------------------------------------------------------------------- /UI/src/js/components/patient/AdmittedPatients.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Reflux from 'reflux'; 3 | import ReactTable from 'react-table' 4 | import "react-table/react-table.css"; 5 | import {Link} from "react-router-dom"; 6 | import {AdmissionStore, AdmissionActions} from '../../reflux/AdmissionReflux.js'; 7 | import {ProtocolStore} from '../../reflux/ProtocolReflux.js'; 8 | import Settings from '../../GlobalSettings.js'; 9 | import RunProtocolButton from '../buttons/RunProtocolButton.js'; 10 | 11 | class AdmittedPatients extends Reflux.Component { 12 | constructor(props) { 13 | super(props); 14 | this.stores = [AdmissionStore, ProtocolStore]; 15 | this.state = { 16 | }; 17 | } 18 | 19 | componentDidMount() { 20 | AdmissionActions.load(); 21 | } 22 | 23 | render() { 24 | let i = 0; 25 | const columns = [{ 26 | Header: () =>
, 27 | id: "gender", 28 | maxWidth: 33, 29 | filterable: false, 30 | accessor: obj => obj.patient.gender, 31 | Cell: props => props.value === "M" ? : 32 | },{ 33 | Header: () =>
Name
, 34 | id: "fullname", 35 | accessor: obj => obj.patient.fullname, 36 | Cell: props => {props.value} 37 | }, { 38 | Header: () =>
Room
, 39 | id: "room", 40 | accessor: obj => obj.room, 41 | Cell: props => {props.value} 42 | }, { 43 | Header: () =>
Last Measurement
, 44 | id: "last_measure", 45 | accessor: obj => obj.last_measure, 46 | Cell: props => {props.value} 47 | }, { 48 | Header: () =>
Next Measurement
, 49 | id: "next_measure", 50 | accessor: obj => obj.next_measure, 51 | width: 250, 52 | Cell: props => {props.value} 53 | }, { 54 | Header: () =>
Physician
, 55 | id: "doctor", 56 | accessor: obj => obj.last_measure_physician, 57 | Cell: props => {props.value} 58 | }, { 59 | Header: () =>
, 60 | id: "actions", 61 | filterable: false, 62 | accessor: obj => obj.patient.id, 63 | Cell: props => 64 | }]; 65 | 66 | return ( 67 |
68 |
69 |
70 | 71 |

Admitted Patients

72 | 73 | 74 | Insert new patient 75 |
76 |
77 | 84 | 85 |
86 | 87 |
88 |
89 | ); 90 | } 91 | } 92 | 93 | export default AdmittedPatients; 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GenericCDSS - Generic Clinical Decision Support System 2 | 3 | 4 | [![Build Status](https://travis-ci.org/joemccann/dillinger.svg?branch=master)](https://github.com/bioinformatics-ua/GenericCDSS/tree/master) 5 | 6 | GenericCDSS is a web-based application, which provides the main dashboard where professionals (e.g., practitioners, nurses) can follow all the patients that are under their responsibility and some details about the state of each one. 7 | 8 | # New Features! 9 | 10 | - Manage all the patient information dynamically 11 | - Create and manage clinical protocols 12 | - Assign protocols to the patients and execute them 13 | 14 | You can also: 15 | - Easily customise the patient information 16 | - Keep track of all the patient data 17 | - Be reminder about the next measurement for each patient admitted in the system 18 | 19 | GenericCDSS follows a Client-Server model, in which each side is sub-divided in several layers. 20 | 21 | The Client-side encapsulate most of the presentation part of the system. It is divided into two layers, the presentation, and the controller layer. The presentation layer is responsible for the user interfaces. The controller layer consumes the backend web services and provides the data to the presentation layer. 22 | 23 | The Server, which is mainly the backend core, is subdivided into three sub-layers: 1) business; 2) persistence; and 3) service provider. The persistence layer is responsible for storing and maintaining the system’s data. The business layer contains most of the application’s logic. Finally, the service layer provides a RESTful API with services prepared to interact with all the system’s functions with or without the client. This layer will be used by the client to access all the core features. 24 | 25 | ### Tech 26 | 27 | GenericCDSS uses a number of open source projects to work properly: 28 | 29 |
30 | * [ReactJS] - HTML enhanced for web apps!
31 | * [NPM] - The frontend package manager
32 | * [Django] - Web framework python-based
33 | * [Django Rest Framework] - Toolkit for building Web APIs in Django projects
34 | * [PostgreSQL] - The object-relational database management system
35 | * [Docker] - The computer program that performs operating-system-level virtualization
36 | * [Make] -  Utility for building and maintaining groups of programs
37 | 
38 | 39 | ### Installation 40 | 41 | GenericCDSS requires Docker, Docker-compose and Make to run. 42 | 43 | Install Docker 44 | 45 |
46 | * Full instructions here https://docs.docker.com/install/
47 | 
48 | 49 | Install docker-compose 50 | 51 |
52 | * Full instructions here https://docs.docker.com/compose/install/
53 | 
54 | 55 | Edit docker-compose.yml with deploy specific details. The variables that should be configured are the following: 56 | 57 | ```sh 58 | 4 - POSTGRES_USER= user used in the PostgreSQL. This user should match with the user used in row 23 59 | 5 - POSTGRES_PASS= password used in the PostgreSQL. This password should match with the user used in row 23 60 | ... 61 | 10 - "xxxx:8000" change the xxxx for the port to access the container 62 | ... 63 | 23 - DOCKER_POSTGRES_USER= user used in the PostgreSQL. 64 | 24 - DOCKER_POSTGRES_PASS= password used in the PostgreSQL. 65 | 25 - DOCKER_POSTGRES_DB= database name 66 | 26 - DOCKER_POSTGRES_HOST= hostname, the default configuration is the container defined in the docker-compose file 67 | 27 - DOCKER_POSTGRES_PORT= DB container port 68 | 28 - DEPLOY_MODE= the execution mode, if demo the database will be fulfilled with random data 69 | 29 - API_URL= the API URL, that ends with api/ 70 | 30 - HOMEPAGE= the system homepage URL 71 | 31 - BASE_URL= the base URL is used when existing a prefix in the homepage URL. For instance, www.page.com/genericcdss, in this case, it is necessary to define the genericcdss in this variable 72 | ``` 73 | 74 | After the customisation of the docker-compose file, it is only necessary to perform the following commands to have the installation running. (This process can take a few minutes) 75 | 76 | ```sh 77 | $ make build 78 | $ make docker-run 79 | ``` 80 | 81 | 82 | 83 | ---- 84 | -------------------------------------------------------------------------------- /UI/src/js/components/reusable/DisplayField.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | /** 5 | * Input/output data component 6 | * */ 7 | class DisplayField extends Component { 8 | render() { 9 | let validation = this.props.isWarning ? "is-warning": this.props.isInvalid ? "is-invalid": ""; 10 | let readOnly = typeof this.props.readOnly === "boolean" ? this.props.readOnly : this.props.readOnly(); 11 | return ( 12 |
13 |
14 | 15 | {this.props.label} 16 | 17 |
18 | { 19 | readOnly ? 20 | 21 | : 22 | 29 | } 30 | { 31 | this.props.isInvalid ? 32 |
33 | {this.props.invalidMessage} 34 |
: '' 35 | } 36 | { 37 | this.props.isWarning ? 38 |
39 | {this.props.invalidMessage} 40 |
: '' 41 | } 42 |
43 | ); 44 | } 45 | 46 | static propTypes = { 47 | /** 48 | * Label of the grey box 49 | * */ 50 | label: PropTypes.string.isRequired, 51 | /** 52 | * Value to be shown 53 | * */ 54 | value: PropTypes.oneOfType([ 55 | PropTypes.string, 56 | PropTypes.object 57 | ]), 58 | /** 59 | * Key data to help the input identification when data is changed 60 | * */ 61 | keydata: PropTypes.string, 62 | /** 63 | * Function comming for the parent component to handle with the selecting change 64 | * 65 | * @param event 66 | * */ 67 | onChange: PropTypes.func, 68 | /** 69 | * Boolean to block the display to only show data (as a normal input) 70 | * */ 71 | readOnly: PropTypes.oneOfType([ 72 | PropTypes.func, 73 | PropTypes.bool 74 | ]), 75 | /** 76 | * Input type 77 | * */ 78 | type: PropTypes.string, 79 | /** 80 | * Input min when number 81 | * */ 82 | min: PropTypes.string, 83 | /** 84 | * Input max when number 85 | * */ 86 | max: PropTypes.string, 87 | /** 88 | * Class for the component in general 89 | * */ 90 | className: PropTypes.string, 91 | /** 92 | * Message to show if the message is valid 93 | * */ 94 | invalidMessage: PropTypes.string, 95 | /** 96 | * Boolean to trigger the invalid message 97 | * */ 98 | isInvalid: PropTypes.bool, 99 | /** 100 | * Boolean to trigger the warning message 101 | * */ 102 | isWarning: PropTypes.bool, 103 | }; 104 | 105 | } 106 | 107 | DisplayField.defaultProps = { 108 | readOnly: false, 109 | type: "text", 110 | className: "", 111 | invalidMessage: "", 112 | isInvalid: false, 113 | isWarning: false 114 | }; 115 | 116 | export default DisplayField; 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | --------------------------------------------------------------------------------