Initial commit

This commit is contained in:
Nathan Chapman 2022-05-03 19:07:12 -06:00
commit 94572e3d64
256 changed files with 137534 additions and 0 deletions

37
.editorconfig Normal file
View File

@ -0,0 +1,37 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.py]
indent_size = 4
continuation_indent_size = 8
combine_as_imports = true
max_line_length = 88
multi_line_output = 4
quote_type = single
[*.js]
indent_size = 4
[*.jsx]
indent_size = 4
[*.scss]
indent_size = 4
[*.ts]
indent_size = 4
[*.tsx]
indent_size = 4
[*.yml]
indent_size = 4
[*.html]
indent_size = 4

125
.gitignore vendored Normal file
View File

@ -0,0 +1,125 @@
# Django #
*.log
*.pot
*.pyc
__pycache__
db.sqlite3
media
# Backup files #
*.bak
# If you are using PyCharm #
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/gradle.xml
.idea/**/libraries
*.iws /out/
# Python #
*.py[cod]
*$py.class
# Distribution / packaging
.Python build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
.pytest_cache/
nosetests.xml
coverage.xml
*.cover
.hypothesis/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# envrc
.envrc
# celery
celerybeat-schedule.*
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# mkdocs documentation
/site
# mypy
.mypy_cache/
# Sublime Text #
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache
*.sublime-workspace
*.sublime-project
# Static CACHE
/static/CACHE/
# sftp configuration file
sftp-config.json
# Package control specific files Package
Control.last-run
Control.ca-list
Control.ca-bundle
Control.system-ca-bundle
GitHub.sublime-settings
# Visual Studio Code #
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history
# IntelliJ
/.idea

15
Pipfile Normal file
View File

@ -0,0 +1,15 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
django = "*"
gunicorn = "*"
psycopg2-binary = "*"
[dev-packages]
django-debug-toolbar = "*"
[requires]
python_version = "3.9.5"

135
Pipfile.lock generated Normal file
View File

@ -0,0 +1,135 @@
{
"_meta": {
"hash": {
"sha256": "a25ffd32da539432bfe9eeba64580611772b13e21bf846ae7c17252b834260b2"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.9.5"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"asgiref": {
"hashes": [
"sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9",
"sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"
],
"markers": "python_version >= '3.6'",
"version": "==3.4.1"
},
"django": {
"hashes": [
"sha256:7f92413529aa0e291f3be78ab19be31aefb1e1c9a52cd59e130f505f27a51f13",
"sha256:f27f8544c9d4c383bbe007c57e3235918e258364577373d4920e9162837be022"
],
"index": "pypi",
"version": "==3.2.6"
},
"gunicorn": {
"hashes": [
"sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e",
"sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"
],
"index": "pypi",
"version": "==20.1.0"
},
"psycopg2-binary": {
"hashes": [
"sha256:0b7dae87f0b729922e06f85f667de7bf16455d411971b2043bbd9577af9d1975",
"sha256:0f2e04bd2a2ab54fa44ee67fe2d002bb90cee1c0f1cc0ebc3148af7b02034cbd",
"sha256:123c3fb684e9abfc47218d3784c7b4c47c8587951ea4dd5bc38b6636ac57f616",
"sha256:1473c0215b0613dd938db54a653f68251a45a78b05f6fc21af4326f40e8360a2",
"sha256:14db1752acdd2187d99cb2ca0a1a6dfe57fc65c3281e0f20e597aac8d2a5bd90",
"sha256:1e3a362790edc0a365385b1ac4cc0acc429a0c0d662d829a50b6ce743ae61b5a",
"sha256:1e85b74cbbb3056e3656f1cc4781294df03383127a8114cbc6531e8b8367bf1e",
"sha256:20f1ab44d8c352074e2d7ca67dc00843067788791be373e67a0911998787ce7d",
"sha256:2f62c207d1740b0bde5c4e949f857b044818f734a3d57f1d0d0edc65050532ed",
"sha256:3242b9619de955ab44581a03a64bdd7d5e470cc4183e8fcadd85ab9d3756ce7a",
"sha256:35c4310f8febe41f442d3c65066ca93cccefd75013df3d8c736c5b93ec288140",
"sha256:4235f9d5ddcab0b8dbd723dca56ea2922b485ea00e1dafacf33b0c7e840b3d32",
"sha256:5ced67f1e34e1a450cdb48eb53ca73b60aa0af21c46b9b35ac3e581cf9f00e31",
"sha256:7360647ea04db2e7dff1648d1da825c8cf68dc5fbd80b8fb5b3ee9f068dcd21a",
"sha256:8c13d72ed6af7fd2c8acbd95661cf9477f94e381fce0792c04981a8283b52917",
"sha256:988b47ac70d204aed01589ed342303da7c4d84b56c2f4c4b8b00deda123372bf",
"sha256:995fc41ebda5a7a663a254a1dcac52638c3e847f48307b5416ee373da15075d7",
"sha256:a36c7eb6152ba5467fb264d73844877be8b0847874d4822b7cf2d3c0cb8cdcb0",
"sha256:aed4a9a7e3221b3e252c39d0bf794c438dc5453bc2963e8befe9d4cd324dff72",
"sha256:aef9aee84ec78af51107181d02fe8773b100b01c5dfde351184ad9223eab3698",
"sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773",
"sha256:b4d7679a08fea64573c969f6994a2631908bb2c0e69a7235648642f3d2e39a68",
"sha256:c250a7ec489b652c892e4f0a5d122cc14c3780f9f643e1a326754aedf82d9a76",
"sha256:ca86db5b561b894f9e5f115d6a159fff2a2570a652e07889d8a383b5fae66eb4",
"sha256:cfc523edecddaef56f6740d7de1ce24a2fdf94fd5e704091856a201872e37f9f",
"sha256:da113b70f6ec40e7d81b43d1b139b9db6a05727ab8be1ee559f3a69854a69d34",
"sha256:f6fac64a38f6768e7bc7b035b9e10d8a538a9fadce06b983fb3e6fa55ac5f5ce",
"sha256:f8559617b1fcf59a9aedba2c9838b5b6aa211ffedecabca412b92a1ff75aac1a",
"sha256:fbb42a541b1093385a2d8c7eec94d26d30437d0e77c1d25dae1dcc46741a385e"
],
"index": "pypi",
"version": "==2.9.1"
},
"pytz": {
"hashes": [
"sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da",
"sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"
],
"version": "==2021.1"
},
"sqlparse": {
"hashes": [
"sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0",
"sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"
],
"markers": "python_version >= '3.5'",
"version": "==0.4.1"
}
},
"develop": {
"asgiref": {
"hashes": [
"sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9",
"sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"
],
"markers": "python_version >= '3.6'",
"version": "==3.4.1"
},
"django": {
"hashes": [
"sha256:7f92413529aa0e291f3be78ab19be31aefb1e1c9a52cd59e130f505f27a51f13",
"sha256:f27f8544c9d4c383bbe007c57e3235918e258364577373d4920e9162837be022"
],
"index": "pypi",
"version": "==3.2.6"
},
"django-debug-toolbar": {
"hashes": [
"sha256:8c5b13795d4040008ee69ba82dcdd259c49db346cf7d0de6e561a49d191f0860",
"sha256:d7bab7573fab35b0fd029163371b7182f5826c13da69734beb675c761d06a4d3"
],
"index": "pypi",
"version": "==3.2.2"
},
"pytz": {
"hashes": [
"sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da",
"sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"
],
"version": "==2021.1"
},
"sqlparse": {
"hashes": [
"sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0",
"sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"
],
"markers": "python_version >= '3.5'",
"version": "==0.4.1"
}
}
}

30
README.md Normal file
View File

@ -0,0 +1,30 @@
# CNA Charting Application
### Prerequisites
- Python 3.9.5
- Pipenv
- Install [direnv](https://direnv.net/)
- In the project root create a file `.envrc` with the following two lines:
-- `export SECRET_KEY="longrandomstringofcharacters"`
-- `export DEBUG=True`
### Python environment and packages:
- In the project root run: `pipenv shell` to get into the python virtualenvironment
- Run: `pipenv install` to install dependencies (e.g. Django)
### Setting up the database and dev-server:
- Run: `python manage.py makemigrations` to create database migrations
- Run: `python manage.py migrate` to create the database with migration schemas
- Run: `python manage.py createsuperuser` and follow prompts to create a superuser in the database.
### Running the server:
- Run: `python manage.py runserver` and navigate to [localhost](http://localhost:8000/admin/)
### Running the server with livereload for Django template files:
- Run: `python manage.py livereload` in one terminal process
- Run: `python manage.py runserver` in another terminal process and navigate to [localhost](http://localhost:8000/admin/)

0
charts/__init__.py Normal file
View File

21
charts/admin.py Normal file
View File

@ -0,0 +1,21 @@
from django.contrib import admin
from . import models
admin.site.register(models.Chart)
admin.site.register(models.Entry)
admin.site.register(models.OralCare)
admin.site.register(models.Bathing)
admin.site.register(models.Toileting)
admin.site.register(models.Dressing)
admin.site.register(models.Meal)
admin.site.register(models.RangeOfMotion)
admin.site.register(models.Ambulation)
admin.site.register(models.Vitals)
admin.site.register(models.FluidIntake)
admin.site.register(models.Urine)
admin.site.register(models.Emesis)
admin.site.register(models.BowelMovement)
admin.site.register(models.PainLevel)
admin.site.register(models.OxygenLevel)
admin.site.register(models.Restraint)
admin.site.register(models.Safety)

6
charts/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class ChartsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'charts'

467
charts/forms.py Normal file
View File

@ -0,0 +1,467 @@
from django import forms
from . import models
class ChartForm(forms.ModelForm):
name = "Chart"
class Meta:
model = models.Chart
fields = (
'patient',
'assignment',
'student_name',
'student_id'
)
labels = {
'student_id': 'Student ID'
}
widgets = {
'patient': forms.TextInput(attrs = {
'autofocus': 'autofocus'
})
}
class EntryForm(forms.ModelForm):
name = "Note"
class Meta:
model = models.Entry
fields = (
'time',
'notes',
)
widgets = {
'time': forms.TextInput(attrs = {
'autofocus': 'autofocus',
'placeholder': 'e.g. 15:00',
'pattern': '^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-5][0-9]:[0-5][0-9])$',
}),
'notes': forms.Textarea(attrs = {
'placeholder': 'Please explain…',
})
}
class OralCareForm(forms.ModelForm):
name = "Oral care"
class Meta:
model = models.OralCare
fields = (
'time',
'assistance',
'dentures_upper',
'dentures_lower',
'notes',
)
widgets = {
'time': forms.TextInput(attrs = {
'autofocus': 'autofocus',
'placeholder': 'e.g. 15:00',
'pattern': '^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-5][0-9]:[0-5][0-9])$',
}),
'notes': forms.Textarea(attrs = {
'placeholder': 'Please explain…',
})
}
class BathingForm(forms.ModelForm):
name = "Bathing"
class Meta:
model = models.Bathing
fields = (
'time',
'method',
'method_other',
'notes',
)
widgets = {
'time': forms.TextInput(attrs = {
'autofocus': 'autofocus',
'placeholder': 'e.g. 15:00',
'pattern': '^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-5][0-9]:[0-5][0-9])$',
}),
'notes': forms.Textarea(attrs = {
'placeholder': 'Please explain…',
}),
'method_other': forms.TextInput(attrs = {
'placeholder': 'Please explain…',
})
}
class ToiletingForm(forms.ModelForm):
name = "Toileting"
class Meta:
model = models.Toileting
fields = (
'time',
'assistance',
'method',
'brief_change',
'perineal_care',
'catheter_care',
'notes',
)
widgets = {
'time': forms.TextInput(attrs = {
'autofocus': 'autofocus',
'placeholder': 'e.g. 15:00',
'pattern': '^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-5][0-9]:[0-5][0-9])$',
}),
'notes': forms.Textarea(attrs = {
'placeholder': 'Please explain…',
})
}
class DressingForm(forms.ModelForm):
name = "Dressing"
class Meta:
model = models.Dressing
fields = (
'time',
'time_of_day',
'assistance',
'notes',
)
widgets = {
'time': forms.TextInput(attrs = {
'autofocus': 'autofocus',
'placeholder': 'e.g. 15:00',
'pattern': '^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-5][0-9]:[0-5][0-9])$',
}),
'notes': forms.Textarea(attrs = {
'placeholder': 'Please explain…',
})
}
class MealForm(forms.ModelForm):
name ="Meal"
class Meta:
model = models.Meal
fields = (
'time',
'kind',
'amount_consumed',
'notes',
)
labels = {
'amount_consumed': 'Amount consumed (as %)'
}
widgets = {
'time': forms.TextInput(attrs = {
'autofocus': 'autofocus',
'placeholder': 'e.g. 15:00',
'pattern': '^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-5][0-9]:[0-5][0-9])$',
}),
'notes': forms.Textarea(attrs = {
'placeholder': 'Please explain…',
}),
'amount_consumed': forms.NumberInput(attrs = {
'min': 0,
'max': 100,
})
}
class RangeOfMotionForm(forms.ModelForm):
name = "Range of motion"
class Meta:
model = models.RangeOfMotion
fields = (
'time',
'motion_kind',
'antiembolic_stockings',
'turn_or_position_change',
'notes',
)
widgets = {
'time': forms.TextInput(attrs = {
'autofocus': 'autofocus',
'placeholder': 'e.g. 15:00',
'pattern': '^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-5][0-9]:[0-5][0-9])$',
}),
'notes': forms.Textarea(attrs = {
'placeholder': 'Please explain…',
})
}
class AmbulationForm(forms.ModelForm):
name = "Ambulation"
class Meta:
model = models.Ambulation
fields = (
'time',
'method',
'assistance',
'devices_used',
'notes',
)
widgets = {
'time': forms.TextInput(attrs = {
'autofocus': 'autofocus',
'placeholder': 'e.g. 15:00',
'pattern': '^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-5][0-9]:[0-5][0-9])$',
}),
'notes': forms.Textarea(attrs = {
'placeholder': 'Please explain…',
})
}
class VitalsForm(forms.ModelForm):
name = "Vitals"
class Meta:
model = models.Vitals
fields = (
'time',
'o2_saturation',
'temperature',
'temperature_units',
'pulse',
'respirations',
'blood_pressure_systolic',
'blood_pressure_diastolic',
'weight',
'weight_units',
'notes',
)
labels = {
'o2_saturation': 'O2 saturation (as %)',
'respirations': 'Respirations per min'
}
widgets = {
'time': forms.TextInput(attrs = {
'autofocus': 'autofocus',
'placeholder': 'e.g. 15:00',
'pattern': '^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-5][0-9]:[0-5][0-9])$',
}),
'notes': forms.Textarea(attrs = {
'placeholder': 'Please explain…',
}),
'o2_saturation': forms.NumberInput(attrs = {
'min': 0,
'max': 100,
}),
'temperature': forms.NumberInput(attrs = {
'min': 30,
'max': 110,
}),
'pulse': forms.NumberInput(attrs = {
'max': 250,
})
}
class FluidIntakeForm(forms.ModelForm):
name = "Fluid intake"
class Meta:
model = models.FluidIntake
fields = (
'time',
'fluid_type',
'intake_amount',
'npo',
'notes',
)
labels = {
'intake_amount': 'Intake amout (in mL)',
'npo': 'NPO',
}
widgets = {
'time': forms.TextInput(attrs = {
'autofocus': 'autofocus',
'placeholder': 'e.g. 15:00',
'pattern': '^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-5][0-9]:[0-5][0-9])$',
}),
'notes': forms.Textarea(attrs = {
'placeholder': 'Please explain…',
})
}
class UrineForm(forms.ModelForm):
name = "Urine"
class Meta:
model = models.Urine
fields = (
'time',
'assistance',
'amount',
'color',
'notes',
)
labels = {
'amount': 'Amount (in mL)'
}
widgets = {
'time': forms.TextInput(attrs = {
'autofocus': 'autofocus',
'placeholder': 'e.g. 15:00',
'pattern': '^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-5][0-9]:[0-5][0-9])$',
}),
'notes': forms.Textarea(attrs = {
'placeholder': 'Please explain…',
})
}
class EmesisForm(forms.ModelForm):
name = "Emesis"
class Meta:
model = models.Emesis
fields = (
'time',
'amount',
'color',
'notes',
)
labels = {
'amount': 'Amount (in mL)'
}
widgets = {
'time': forms.TextInput(attrs = {
'autofocus': 'autofocus',
'placeholder': 'e.g. 15:00',
'pattern': '^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-5][0-9]:[0-5][0-9])$',
}),
'notes': forms.Textarea(attrs = {
'placeholder': 'Please explain…',
})
}
class BowelMovementForm(forms.ModelForm):
name = "Bowel movement"
class Meta:
model = models.BowelMovement
fields = (
'time',
'assistance',
'amount',
'color',
'consistency',
'notes',
)
widgets = {
'time': forms.TextInput(attrs = {
'autofocus': 'autofocus',
'placeholder': 'e.g. 15:00',
'pattern': '^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-5][0-9]:[0-5][0-9])$',
}),
'notes': forms.Textarea(attrs = {
'placeholder': 'Please explain…',
})
}
class PainLevelForm(forms.ModelForm):
name = "Pain level"
class Meta:
model = models.PainLevel
fields = (
'time',
'score',
'reported_to_nurse',
'notes',
)
labels = {
'score': 'Score (out of 10)'
}
widgets = {
'time': forms.TextInput(attrs = {
'autofocus': 'autofocus',
'placeholder': 'e.g. 15:00',
'pattern': '^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-5][0-9]:[0-5][0-9])$',
}),
'notes': forms.Textarea(attrs = {
'placeholder': 'Please explain…',
}),
'score': forms.NumberInput(attrs= {
'min': 0,
'max': 10,
})
}
class OxygenLevelForm(forms.ModelForm):
name = "Oxygen level"
class Meta:
model = models.OxygenLevel
fields = (
'time',
'oxygen_level',
'delivery_method',
'notes',
)
labels = {
'oxygen_level': 'Oxygen (LPM)'
}
widgets = {
'time': forms.TextInput(attrs = {
'autofocus': 'autofocus',
'placeholder': 'e.g. 15:00',
'pattern': '^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-5][0-9]:[0-5][0-9])$',
}),
'notes': forms.Textarea(attrs = {
'placeholder': 'Please explain…',
}),
'oxygen_level': forms.NumberInput(attrs= {
'max': 21,
})
}
class RestraintForm(forms.ModelForm):
name = "Restraint"
class Meta:
model = models.Restraint
fields = (
'time',
'restraints_used',
'restraints_soft',
'notes',
)
widgets = {
'time': forms.TextInput(attrs = {
'autofocus': 'autofocus',
'placeholder': 'e.g. 15:00',
'pattern': '^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-5][0-9]:[0-5][0-9])$',
}),
'notes': forms.Textarea(attrs = {
'placeholder': 'Please explain…',
})
}
class SafetyForm(forms.ModelForm):
name = "Safety"
class Meta:
model = models.Safety
fields = (
'time',
'call_light_in_reach',
'bed_low',
'brakes_on',
'falls',
'nausea',
'vomiting',
'confusion',
'combative',
'notes',
)
widgets = {
'time': forms.TextInput(attrs = {
'autofocus': 'autofocus',
'placeholder': 'e.g. 15:00',
'pattern': '^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-5][0-9]:[0-5][0-9])$',
}),
'notes': forms.Textarea(attrs = {
'placeholder': 'Please explain…',
})
}

View File

@ -0,0 +1,218 @@
# Generated by Django 3.2.2 on 2021-08-18 23:46
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Chart',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('patient', models.CharField(max_length=250)),
('assignment', models.CharField(max_length=250)),
('student_name', models.CharField(max_length=250)),
('student_id', models.CharField(max_length=250)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'ordering': ('-created_at',),
},
),
migrations.CreateModel(
name='Entry',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('time', models.TimeField()),
('notes', models.TextField(blank=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('chart', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='charts.chart')),
],
options={
'verbose_name_plural': 'entries',
'ordering': ('time',),
},
),
migrations.CreateModel(
name='Ambulation',
fields=[
('entry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='charts.entry')),
('method', models.CharField(blank=True, choices=[('CH', 'Chair'), ('RO', 'Up in room'), ('HA', 'Ambulate in hall'), ('PT', 'To PT')], max_length=2)),
('assistance', models.CharField(blank=True, choices=[('IN', 'Independent'), ('ON', 'One assist with gait belt'), ('TW', 'Two assist'), ('LI', 'Lift')], max_length=2)),
('devices_used', models.CharField(blank=True, choices=[('WA', 'Walker'), ('CR', 'Crutches'), ('WH', 'Wheelchair'), ('NO', 'None')], max_length=2)),
],
bases=('charts.entry',),
),
migrations.CreateModel(
name='Bathing',
fields=[
('entry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='charts.entry')),
('method', models.CharField(blank=True, choices=[('SH', 'Shower'), ('TU', 'Tub'), ('PA', 'Partial'), ('BB', 'Bed bath'), ('SP', 'Specialty')], max_length=2)),
('method_other', models.CharField(blank=True, max_length=250)),
],
options={
'verbose_name_plural': 'bathing',
},
bases=('charts.entry',),
),
migrations.CreateModel(
name='BowelMovement',
fields=[
('entry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='charts.entry')),
('assistance', models.CharField(blank=True, choices=[('IN', 'Independent'), ('AS', 'Assisted'), ('DE', 'Totally dependent')], default='IN', max_length=2)),
('amount', models.CharField(blank=True, choices=[('SM', 'Small'), ('MD', 'Moderate'), ('LG', 'Large')], max_length=2)),
('color', models.CharField(blank=True, max_length=100)),
('consistency', models.CharField(blank=True, choices=[('SF', 'Soft'), ('HD', 'Hard'), ('LQ', 'Liquid'), ('FM', 'Formed')], max_length=2)),
],
bases=('charts.entry',),
),
migrations.CreateModel(
name='Dressing',
fields=[
('entry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='charts.entry')),
('time_of_day', models.CharField(blank=True, choices=[('AM', 'Morning'), ('PM', 'Evening')], max_length=2)),
('assistance', models.CharField(blank=True, choices=[('IN', 'Independent'), ('AS', 'Assisted'), ('DE', 'Totally dependent')], default='IN', max_length=2)),
],
bases=('charts.entry',),
),
migrations.CreateModel(
name='Emesis',
fields=[
('entry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='charts.entry')),
('amount', models.PositiveIntegerField()),
('color', models.CharField(blank=True, max_length=100)),
],
bases=('charts.entry',),
),
migrations.CreateModel(
name='FluidIntake',
fields=[
('entry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='charts.entry')),
('fluid_type', models.CharField(max_length=100)),
('intake_amount', models.PositiveIntegerField()),
('npo', models.BooleanField(default=False)),
],
bases=('charts.entry',),
),
migrations.CreateModel(
name='Meal',
fields=[
('entry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='charts.entry')),
('kind', models.CharField(blank=True, choices=[('BR', 'Breakfast'), ('LU', 'Lunch'), ('DI', 'Dinner'), ('SN', 'Snack')], max_length=2)),
('amount_consumed', models.FloatField(blank=True, null=True)),
],
bases=('charts.entry',),
),
migrations.CreateModel(
name='OralCare',
fields=[
('entry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='charts.entry')),
('assistance', models.CharField(blank=True, choices=[('IN', 'Independent'), ('AS', 'Assisted'), ('DE', 'Totally dependent')], default='IN', max_length=2, null=True)),
('dentures_upper', models.BooleanField(default=False)),
('dentures_lower', models.BooleanField(default=False)),
],
options={
'verbose_name_plural': 'oral care',
},
bases=('charts.entry',),
),
migrations.CreateModel(
name='OxygenLevel',
fields=[
('entry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='charts.entry')),
('oxygen_level', models.FloatField(blank=True, null=True)),
('delivery_method', models.CharField(blank=True, choices=[('NA', 'Per nasal cannula'), ('MA', 'Per mask'), ('RE', 'Refused to wear')], max_length=2)),
],
bases=('charts.entry',),
),
migrations.CreateModel(
name='PainLevel',
fields=[
('entry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='charts.entry')),
('score', models.PositiveIntegerField(blank=True, null=True)),
('reported_to_nurse', models.BooleanField(default=False)),
],
bases=('charts.entry',),
),
migrations.CreateModel(
name='RangeOfMotion',
fields=[
('entry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='charts.entry')),
('motion_kind', models.CharField(blank=True, choices=[('PA', 'Passive'), ('AC', 'Active'), ('CO', 'Combination')], max_length=2)),
('antiembolic_stockings', models.BooleanField(default=False)),
('turn_or_position_change', models.BooleanField(default=False)),
],
bases=('charts.entry',),
),
migrations.CreateModel(
name='Restraint',
fields=[
('entry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='charts.entry')),
('restraints_used', models.CharField(blank=True, choices=[('TW', 'Side rails x2'), ('FO', 'Side rails x4'), ('NO', 'None')], max_length=2)),
('restraints_soft', models.BooleanField(default=False)),
],
bases=('charts.entry',),
),
migrations.CreateModel(
name='Safety',
fields=[
('entry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='charts.entry')),
('call_light_in_reach', models.BooleanField(default=False)),
('bed_low', models.BooleanField(default=False)),
('brakes_on', models.BooleanField(default=False)),
('falls', models.CharField(blank=True, max_length=999)),
('nausea', models.CharField(blank=True, max_length=999)),
('vomiting', models.CharField(blank=True, max_length=999)),
('confusion', models.CharField(blank=True, max_length=999)),
('combative', models.CharField(blank=True, max_length=999)),
],
bases=('charts.entry',),
),
migrations.CreateModel(
name='Toileting',
fields=[
('entry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='charts.entry')),
('assistance', models.CharField(blank=True, choices=[('IN', 'Independent'), ('AS', 'Assisted'), ('DE', 'Totally dependent')], default='IN', max_length=2)),
('method', models.CharField(blank=True, choices=[('CO', 'Commode'), ('BP', 'Bed pan'), ('CA', 'Catheter')], max_length=2)),
('brief_change', models.BooleanField(default=False)),
('perineal_care', models.BooleanField(default=False)),
('catheter_care', models.BooleanField(default=False)),
],
bases=('charts.entry',),
),
migrations.CreateModel(
name='Urine',
fields=[
('entry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='charts.entry')),
('assistance', models.CharField(blank=True, choices=[('IN', 'Independent'), ('AS', 'Assisted'), ('DE', 'Totally dependent')], default='IN', max_length=2)),
('color', models.CharField(blank=True, choices=[('YE', 'Yellow'), ('DA', 'Dark yellow'), ('BR', 'Brown')], max_length=2)),
('amount', models.PositiveIntegerField()),
],
bases=('charts.entry',),
),
migrations.CreateModel(
name='Vitals',
fields=[
('entry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='charts.entry')),
('o2_saturation', models.PositiveIntegerField(blank=True, null=True)),
('temperature', models.FloatField(blank=True, null=True)),
('temperature_units', models.CharField(blank=True, choices=[('CE', 'C'), ('FA', 'F')], default='FA', max_length=2, null=True)),
('pulse', models.IntegerField(blank=True, null=True)),
('respirations', models.IntegerField(blank=True, null=True)),
('blood_pressure_systolic', models.IntegerField(blank=True, null=True)),
('blood_pressure_diastolic', models.IntegerField(blank=True, null=True)),
('weight', models.FloatField(blank=True, null=True)),
('weight_units', models.CharField(blank=True, choices=[('LB', 'LBS'), ('KG', 'KG')], default='LB', max_length=2, null=True)),
],
bases=('charts.entry',),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.6 on 2021-08-19 22:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('charts', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='bathing',
name='assistance',
field=models.CharField(blank=True, choices=[('IN', 'Independent'), ('AS', 'Assisted'), ('DE', 'Totally dependent')], default='IN', max_length=2),
),
migrations.AlterField(
model_name='oralcare',
name='assistance',
field=models.CharField(blank=True, choices=[('IN', 'Independent'), ('AS', 'Assisted'), ('DE', 'Totally dependent')], default='IN', max_length=2),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-08-20 22:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('charts', '0002_auto_20210819_2253'),
]
operations = [
migrations.AlterField(
model_name='ambulation',
name='devices_used',
field=models.CharField(blank=True, choices=[('WA', 'Walker'), ('CR', 'Crutches'), ('WH', 'Wheelchair')], max_length=2),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-08-25 15:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('charts', '0003_alter_ambulation_devices_used'),
]
operations = [
migrations.AlterField(
model_name='bowelmovement',
name='consistency',
field=models.CharField(blank=True, choices=[('SO', 'Soft'), ('HD', 'Hard'), ('LQ', 'Liquid'), ('FM', 'Formed'), ('SF', 'Soft and formed')], max_length=2),
),
]

View File

333
charts/models.py Normal file
View File

@ -0,0 +1,333 @@
from django.db import models
from django.urls import reverse
INDEPENDENT = 'IN'
ASSISTED = 'AS'
TOTALLY_DEPENDENT = 'DE'
ASSISTANCE_CHOICES = [
(INDEPENDENT, 'Independent'),
(ASSISTED, 'Assisted'),
(TOTALLY_DEPENDENT, 'Totally dependent'),
]
class Chart(models.Model):
class Meta:
ordering = ('-created_at',)
patient = models.CharField(max_length=250)
assignment = models.CharField(max_length=250)
student_name = models.CharField(max_length=250)
student_id = models.CharField(max_length=250)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def get_absolute_url(self):
return reverse('chart-detail', kwargs={'pk': self.pk})
def __str__(self):
return f"Chart {self.pk}, for {self.patient}"
class Entry(models.Model):
class Meta:
ordering = ('time',)
verbose_name_plural = 'entries'
time = models.TimeField()
chart = models.ForeignKey(Chart, on_delete=models.CASCADE)
notes = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def get_absolute_url(self):
return reverse('chart-detail', kwargs={'pk': self.chart_id})
def __str__(self):
return f"{self.time}"
class OralCare(Entry):
class Meta:
verbose_name_plural = 'oral care'
assistance = models.CharField(
max_length=2,
choices=ASSISTANCE_CHOICES,
default=INDEPENDENT,
blank=True
)
dentures_upper = models.BooleanField(default=False)
dentures_lower = models.BooleanField(default=False)
class Bathing(Entry):
class Meta:
verbose_name_plural = 'bathing'
METHOD_CHOICES = [
('SH', 'Shower'),
('TU', 'Tub'),
('PA', 'Partial'),
('BB', 'Bed bath'),
('SP', 'Specialty'),
]
method = models.CharField(
max_length=2,
choices=METHOD_CHOICES,
blank=True
)
method_other = models.CharField(max_length=250, blank=True)
assistance = models.CharField(
max_length=2,
choices=ASSISTANCE_CHOICES,
default=INDEPENDENT,
blank=True
)
class Toileting(Entry):
assistance = models.CharField(
max_length=2,
choices=ASSISTANCE_CHOICES,
default=INDEPENDENT,
blank=True
)
TOILETING_METHOD_CHOICES = [
('CO', 'Commode'),
('BP', 'Bed pan'),
('CA', 'Catheter'),
]
method = models.CharField(
max_length=2,
choices=TOILETING_METHOD_CHOICES,
blank=True
)
brief_change = models.BooleanField(default=False)
perineal_care = models.BooleanField(default=False)
catheter_care = models.BooleanField(default=False)
class Dressing(Entry):
DRESSING_TIME_CHOICES = [
('AM', 'Morning'),
('PM', 'Evening'),
]
time_of_day = models.CharField(
max_length=2,
choices=DRESSING_TIME_CHOICES,
blank=True
)
assistance = models.CharField(
max_length=2,
choices=ASSISTANCE_CHOICES,
default=INDEPENDENT,
blank=True
)
class Meal(Entry):
KIND_CHOICES = [
('BR', 'Breakfast'),
('LU', 'Lunch'),
('DI', 'Dinner'),
('SN', 'Snack'),
]
kind = models.CharField(
max_length=2,
choices=KIND_CHOICES,
blank=True
)
amount_consumed = models.FloatField(blank=True, null=True)
class RangeOfMotion(Entry):
MOTION_KIND_CHOICES = [
('PA', 'Passive'),
('AC', 'Active'),
('CO', 'Combination'),
]
motion_kind = models.CharField(
max_length=2,
choices=MOTION_KIND_CHOICES,
blank=True
)
antiembolic_stockings = models.BooleanField(default=False)
turn_or_position_change = models.BooleanField(default=False)
class Ambulation(Entry):
METHOD_CHOICES = [
('CH', 'Chair'),
('RO', 'Up in room'),
('HA', 'Ambulate in hall'),
('PT', 'To PT'),
]
method = models.CharField(
max_length=2,
choices=METHOD_CHOICES,
blank=True
)
ASSISTANCE_CHOICES = [
('IN', 'Independent'),
('ON', 'One assist with gait belt'),
('TW', 'Two assist'),
('LI', 'Lift'),
]
assistance = models.CharField(
max_length=2,
choices=ASSISTANCE_CHOICES,
blank=True
)
DEVICES_USED_CHOICES = [
('WA', 'Walker'),
('CR', 'Crutches'),
('WH', 'Wheelchair'),
]
devices_used = models.CharField(
max_length=2,
choices=DEVICES_USED_CHOICES,
blank=True
)
class Vitals(Entry):
o2_saturation = models.PositiveIntegerField(blank=True, null=True)
temperature = models.FloatField(blank=True, null=True)
TEMPERATURE_UNITS_CHOICES = [
('CE', 'C'),
('FA', 'F'),
]
temperature_units = models.CharField(
max_length=2,
choices=TEMPERATURE_UNITS_CHOICES,
default='FA',
blank=True,
null=True
)
pulse = models.IntegerField(blank=True, null=True)
respirations = models.IntegerField(blank=True, null=True)
blood_pressure_systolic = models.IntegerField(blank=True, null=True)
blood_pressure_diastolic = models.IntegerField(blank=True, null=True)
weight = models.FloatField(blank=True, null=True)
WEIGHT_UNITS_CHOICES = [
('LB', 'LBS'),
('KG', 'KG'),
]
weight_units = models.CharField(
max_length=2,
choices=WEIGHT_UNITS_CHOICES,
default='LB',
blank=True,
null=True
)
class FluidIntake(Entry):
fluid_type = models.CharField(max_length=100)
intake_amount = models.PositiveIntegerField()
npo = models.BooleanField(default=False)
class Urine(Entry):
URINE_COLOR_CHOICES = [
('YE', 'Yellow'),
('DA', 'Dark yellow'),
('BR', 'Brown'),
]
assistance = models.CharField(
max_length=2,
choices=ASSISTANCE_CHOICES,
default=INDEPENDENT,
blank=True
)
color = models.CharField(
max_length=2,
choices=URINE_COLOR_CHOICES,
blank=True
)
amount = models.PositiveIntegerField()
class Emesis(Entry):
amount = models.PositiveIntegerField()
color = models.CharField(max_length=100, blank=True)
class BowelMovement(Entry):
assistance = models.CharField(
max_length=2,
choices=ASSISTANCE_CHOICES,
default=INDEPENDENT,
blank=True
)
AMOUNT_CHOICES = [
('SM', 'Small'),
('MD', 'Moderate'),
('LG', 'Large'),
]
amount = models.CharField(
max_length=2,
choices=AMOUNT_CHOICES,
blank=True
)
color = models.CharField(max_length=100, blank=True)
CONSISTENCY_CHOICES = [
('SO', 'Soft'),
('HD', 'Hard'),
('LQ', 'Liquid'),
('FM', 'Formed'),
('SF', 'Soft and formed'),
]
consistency = models.CharField(
max_length=2,
choices=CONSISTENCY_CHOICES,
blank=True
)
class PainLevel(Entry):
score = models.PositiveIntegerField(blank=True, null=True)
reported_to_nurse = models.BooleanField(default=False)
class OxygenLevel(Entry):
oxygen_level = models.FloatField(blank=True, null=True)
DELIVERY_METHOD_CHOICES = [
('NA', 'Per nasal cannula'),
('MA', 'Per mask'),
('RE', 'Refused to wear'),
]
delivery_method = models.CharField(
max_length=2,
choices=DELIVERY_METHOD_CHOICES,
blank=True
)
class Restraint(Entry):
RESTRAINTS_USED_CHOICES = [
('TW', 'Side rails x2'),
('FO', 'Side rails x4'),
('NO', 'None'),
]
restraints_used = models.CharField(
max_length=2,
choices=RESTRAINTS_USED_CHOICES,
blank=True
)
restraints_soft = models.BooleanField(default=False)
class Safety(Entry):
call_light_in_reach = models.BooleanField(default=False)
bed_low = models.BooleanField(default=False)
brakes_on = models.BooleanField(default=False)
falls = models.CharField(max_length=999, blank=True)
nausea = models.CharField(max_length=999, blank=True)
vomiting = models.CharField(max_length=999, blank=True)
confusion = models.CharField(max_length=999, blank=True)
combative = models.CharField(max_length=999, blank=True)

View File

@ -0,0 +1,13 @@
{% extends "base.html" %}
{% block content %}
<article class="chart">
<h1>Delete chart</h1>
<form method="post" action="{% url 'chart-delete' chart.pk %}">
{% csrf_token %}
<p class="form__submit">
<input class="action-button destroy" type="submit" value="Confirm delete chart"> or <a href="{% url 'chart-detail' chart.pk %}">cancel</a>
</p>
</form>
</article>
{% endblock %}

View File

@ -0,0 +1,16 @@
{% extends "base.html" %}
{% block content %}
<article class="chart">
<h1>Create chart</h1>
<section>
<form method="POST" action="{% url 'chart-create' %}">
{% csrf_token %}
{{form.as_p}}
<p class="form__submit">
<input class="action-button" type="submit" value="Create chart"> or <a href="{% url 'chart-list' %}">cancel</a>
</p>
</form>
</section>
</article>
{% endblock %}

View File

@ -0,0 +1,105 @@
{% extends "base.html" %}
{% block content %}
<article class="chart">
<header>
<div class="chart__heading">
<h1><i class="fas fa-notes-medical"></i>&ensp;Chart for {{chart.patient}}</h1>
<a class="chart__edit action-button" href="{% url 'chart-update' chart.pk %}" class="action-button">Edit chart</a>
</div>
<p>
<strong>Student</strong>: {{chart.student_id}} {{chart.student_name}}<br>
<strong>Assignment</strong>: {{chart.assignment}}
</p>
<p>
<a class="open-modal action-button"><i class="fas fa-plus"></i>&ensp;New entry</a>
</p>
<div class="modal-menu">
<div class="modal-menu__content">
<div class="modal-menu__header">
<span><i class="fas fa-plus"></i>&ensp;<strong>New entry</strong></span>
<span class="close-modal">&times;</span>
</div>
<div class="modal-menu__list">
<ul>
<li class="modal-menu__item"><i class="fas fa-edit"></i> <a href="{% url 'entry-create' chart.pk %}">Note &rarr;</a></li>
<li><i class="fas fa-user"></i> ADL
<ul>
<li class="modal-menu__item"><i class="fas fa-broom"></i> <a href="{% url 'oral-create' chart.pk %}">Oral care &rarr;</a></li>
<li class="modal-menu__item"><i class="fas fa-shower"></i> <a href="{% url 'bathing-create' chart.pk %}">Bathing &rarr;</a></li>
<li class="modal-menu__item"><i class="fas fa-bath"></i> <a href="{% url 'toileting-create' chart.pk %}">Toileting &rarr;</a></li>
<li class="modal-menu__item"><i class="fas fa-tshirt"></i> <a href="{% url 'dressing-create' chart.pk %}">Dressing &rarr;</a></li>
<li class="modal-menu__item"><i class="fas fa-utensils"></i> <a href="{% url 'meal-create' chart.pk %}">Meal &rarr;</a></li>
<li class="modal-menu__item"><i class="fas fa-tint"></i> <a href="{% url 'urine-create' chart.pk %}">Urine &rarr;</a></li>
<li class="modal-menu__item"><i class="fas fa-poo"></i> <a href="{% url 'bm-create' chart.pk %}">Bowel movement &rarr;</a></li>
</ul>
</li>
<li><i class="fas fa-walking"></i> Activity
<ul>
<li class="modal-menu__item"><i class="fas fa-sign-language"></i> <a href="{% url 'rom-create' chart.pk %}">Range of motion &rarr;</a></li>
<li class="modal-menu__item"><i class="fab fa-accessible-icon"></i> <a href="{% url 'ambulation-create' chart.pk %}">Ambulation &rarr;</a></li>
</ul>
</li>
<li class="modal-menu__item"><i class="fas fa-heartbeat"></i> <a href="{% url 'vitals-create' chart.pk %}">Vitals &rarr;</a></li>
<li class="modal-menu__item"><i class="fas fa-lungs"></i> <a href="{% url 'oxygen-create' chart.pk %}">Oxygen level &rarr;</a></li>
<li class="modal-menu__item"><i class="fas fa-syringe"></i> <a href="{% url 'fluid-create' chart.pk %}">Fluid intake &rarr;</a></li>
<li class="modal-menu__item"><i class="fas fa-frown"></i> <a href="{% url 'emesis-create' chart.pk %}">Emesis &rarr;</a></li>
<li class="modal-menu__item"><i class="fas fa-meh"></i> <a href="{% url 'pain-create' chart.pk %}">Pain level &rarr;</a></li>
<li class="modal-menu__item"><i class="fas fa-bed"></i> <a href="{% url 'restraint-create' chart.pk %}">Restraints &rarr;</a></li>
<li class="modal-menu__item"><i class="far fa-life-ring"></i> <a href="{% url 'safety-create' chart.pk %}">Safety &rarr;</a></li>
</ul>
</div>
</div>
</div>
</header>
<section>
{% regroup chart.entry_set.all by time as entry_set %}
{% for item in entry_set %}
<div class="entry">
<strong>{{item.grouper|time:"H:i"}}</strong>
<ul>
{% for entry in item.list %}
<li class="entry__item">
{% if entry.oralcare %}
{% include "charts/details/oral_care.html" with entry=entry %}
{% elif entry.bathing %}
{% include "charts/details/bathing.html" with entry=entry %}
{% elif entry.toileting %}
{% include "charts/details/toileting.html" with entry=entry %}
{% elif entry.dressing %}
{% include "charts/details/dressing.html" with entry=entry %}
{% elif entry.meal %}
{% include "charts/details/meal.html" with entry=entry %}
{% elif entry.rangeofmotion %}
{% include "charts/details/rom.html" with entry=entry %}
{% elif entry.ambulation %}
{% include "charts/details/ambulation.html" with entry=entry %}
{% elif entry.vitals %}
{% include "charts/details/vitals.html" with entry=entry %}
{% elif entry.fluidintake %}
{% include "charts/details/fluid_intake.html" with entry=entry %}
{% elif entry.urine %}
{% include "charts/details/urine.html" with entry=entry %}
{% elif entry.emesis %}
{% include "charts/details/emesis.html" with entry=entry %}
{% elif entry.bowelmovement %}
{% include "charts/details/bowel_movement.html" with entry=entry %}
{% elif entry.painlevel %}
{% include "charts/details/pain_level.html" with entry=entry %}
{% elif entry.oxygenlevel %}
{% include "charts/details/oxygen_level.html" with entry=entry %}
{% elif entry.restraint %}
{% include "charts/details/restraint.html" with entry=entry %}
{% elif entry.safety %}
{% include "charts/details/safety.html" with entry=entry %}
{% else %}
{% include "charts/details/entry.html" with entry=entry %}
{% endif %}
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
</section>
</article>
{% endblock %}

View File

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block content %}
<article class="chart">
<header class="chart__heading">
<h1>Update chart</h1>
<p>
<a class="action-button destroy" href="{% url 'chart-delete' chart.pk %}">Delete</a>
</p>
</header>
<section>
<form method="POST" action="{% url 'chart-update' chart.pk %}">
{% csrf_token %}
{{form.as_p}}
<p class="form__submit">
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'chart-detail' chart.pk %}">cancel</a>
</p>
</form>
</section>
</article>
{% endblock %}

View File

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block content %}
<article class="chart">
<h1>Charts</h1>
<p>
<a href="{% url 'chart-create' %}" class="action-button"><i class="fas fa-plus"></i>&ensp;New chart</a>
</p>
<section>
{% for chart in chart_list %}
<div class="chart__item">
<h4><i class="fas fa-notes-medical"></i> <a href="{% url 'chart-detail' chart.pk %}">Chart for {{chart.patient}}</a></h4>
<p>
<strong>Student</strong>: {{chart.student_id}}&emsp;{{chart.student_name}}&emsp;
<strong>Assignment</strong>: {{chart.assignment}}<br>
</p>
</div>
{% endfor %}
</section>
</article>
{% endblock %}

View File

@ -0,0 +1,21 @@
<div class="entry__details">
<strong>
<i class="fab fa-accessible-icon"></i> Ambulation
{% if entry.ambulation.devices_used and not None %}
via {{entry.ambulation.get_devices_used_display}}
{% endif %}
</strong><br>
{% if entry.ambulation.method %}
{{entry.ambulation.get_method_display}}&emsp;
{% endif %}
{% if entry.ambulation.assistance %}
{{entry.ambulation.get_assistance_display}}&emsp;
{% endif %}
{% if entry.notes %}
<br>
<strong>Notes</strong>: {{entry.notes}}
{% endif %}
</div>
<div class="entry__actions">
<a href="{% url "ambulation-update" chart.pk entry.ambulation.pk %}">Edit</a>
</div>

View File

@ -0,0 +1,17 @@
<div class="entry__details">
<strong>
<i class="fas fa-shower"></i>
{% if entry.bathing.method %}
Cleaned via {{entry.bathing.get_method_display}}
{% endif %}
{% if entry.bathing.method_other %}
<strong>Other</strong>: {{entry.bathing.method_other}}
{% endif %}
</strong><br>
{% if entry.notes %}
<br>
<strong>Notes</strong>: {{entry.notes}}
{% endif %}
</div>
<a href="{% url "bathing-update" chart.pk entry.bathing.pk %}">Edit</a>

View File

@ -0,0 +1,20 @@
<div class="entry__details">
<strong><i class="fas fa-poo"></i> Bowel movement</strong><br>
{% if entry.bowelmovement.assistance %}
Patient was {{entry.bowelmovement.get_assistance_display}}&emsp;
{% endif %}
{% if entry.bowelmovement.color %}
<strong>Color</strong>: {{entry.bowelmovement.color}}&emsp;
{% endif %}
<strong>Amount</strong>: {{entry.bowelmovement.get_amount_display}}&emsp;
<strong>Consistency</strong>: {{entry.bowelmovement.get_consistency_display}}
{% if entry.notes %}
<br>
<strong>Notes</strong>: {{entry.notes}}
{% endif %}
</div>
<div class="entry__actions">
<a href="{% url "bm-update" chart.pk entry.bowelmovement.pk %}">Edit</a>
</div>

View File

@ -0,0 +1,14 @@
<div class="entry__details">
<strong><i class="fas fa-tshirt"></i> Dressing</strong><br>
<strong>{{entry.dressing.get_time_of_day_display}}</strong>&emsp;
{% if entry.dressing.assistance %}
Patient was {{entry.dressing.get_assistance_display}}&emsp;
{% endif %}
{% if entry.notes %}
<br>
<strong>Notes</strong>: {{entry.notes}}
{% endif %}
</div>
<div class="entry__actions">
<a href="{% url "dressing-update" chart.pk entry.dressing.pk %}">Edit</a>
</div>

View File

@ -0,0 +1,14 @@
<div class="entry__details">
<strong><i class="fas fa-frown"></i> Emesis</strong><br>
{% if entry.emesis.color %}
<strong>Color</strong>: {{entry.emesis.color}}&emsp;
{% endif %}
<strong>Amount</strong>: {{entry.emesis.amount}} mL
{% if entry.notes %}
<br>
<strong>Notes</strong>: {{entry.notes}}
{% endif %}
</div>
<div class="entry__actions">
<a href="{% url "emesis-update" chart.pk entry.emesis.pk %}">Edit</a>
</div>

View File

@ -0,0 +1,6 @@
<div class="entry__details">
<i class="fas fa-edit"></i> {{entry.notes}}
</div>
<div class="entry__actions">
<a href="{% url "entry-update" chart.pk entry.pk %}">Edit</a>
</div>

View File

@ -0,0 +1,12 @@
<div class="entry__details">
<strong><i class="fas fa-syringe"></i> Fluid intake</strong>&emsp;
{{entry.fluidintake.intake_amount}}mL of {{entry.fluidintake.fluid_type}}&emsp;
<strong>NPO?</strong> {{entry.fluidintake.npo|yesno:"Yes,No"}}
{% if entry.notes %}
<br>
<strong>Notes</strong>: {{entry.notes}}
{% endif %}
</div>
<div class="entry__actions">
<a href="{% url "fluid-update" chart.pk entry.fluidintake.pk %}">Edit</a>
</div>

View File

@ -0,0 +1,11 @@
<div class="entry__details">
<strong><i class="fas fa-utensils"></i> {{entry.meal.get_kind_display}}</strong><br>
<strong>Amount consumed</strong>: {{entry.meal.amount_consumed}} %
{% if entry.notes %}
<br>
<strong>Notes</strong>: {{entry.notes}}
{% endif %}
</div>
<div class="entry__actions">
<a href="{% url "meal-update" chart.pk entry.meal.pk %}">Edit</a>
</div>

View File

@ -0,0 +1,17 @@
<div class="entry__details">
<strong><i class="fas fa-broom"></i> Oral care</strong><br>
{% if entry.oralcare.assistance %}
Patient was {{entry.oralcare.get_assistance_display}}&emsp;
{% endif %}
{% if entry.oralcare.dentures_upper %}
Has upper dentures&emsp;
{% endif %}
{% if entry.oralcare.dentures_lower %}
Has lower dentures&emsp;
{% endif %}
{% if entry.notes %}
<br>
<strong>Notes</strong>: {{entry.notes}}
{% endif %}
</div>
<a href="{% url "oral-update" chart.pk entry.oralcare.pk %}">Edit</a>

View File

@ -0,0 +1,14 @@
<div class="entry__details">
<strong>
<i class="fas fa-lungs"></i>
Oxygen {{entry.oxygenlevel.oxygen_level}} LPM&emsp;
</strong>
{{entry.oxygenlevel.get_delivery_method_display}}
{% if entry.notes %}
<br>
<strong>Notes</strong>: {{entry.notes}}
{% endif %}
</div>
<div class="entry__actions">
<a href="{% url "oxygen-update" chart.pk entry.oxygenlevel.pk %}">Edit</a>
</div>

View File

@ -0,0 +1,12 @@
<div class="entry__details">
<strong><i class="fas fa-meh"></i> Pain level</strong>:&ensp;
<sup>{{entry.painlevel.score}}</sup> &frasl; <sub>10</sub>&emsp;
<strong>Reported to nurse?</strong> {{entry.painlevel.reported_to_nurse|yesno:"Yes,No"}}
{% if entry.notes %}
<br>
<strong>Notes</strong>: {{entry.notes}}
{% endif %}
</div>
<div class="entry__actions">
<a href="{% url "pain-update" chart.pk entry.painlevel.pk %}">Edit</a>
</div>

View File

@ -0,0 +1,14 @@
<div class="entry__details">
<strong><i class="fas fa-bed"></i> Restraints</strong>:
{{entry.restraint.get_restraints_used_display}}&emsp;
{% if entry.restraint.restraints_soft %}
Soft restraints used
{% endif %}
{% if entry.notes %}
<br>
<strong>Notes</strong>: {{entry.notes}}
{% endif %}
</div>
<div class="entry__actions">
<a href="{% url "restraint-update" chart.pk entry.restraint.pk %}">Edit</a>
</div>

View File

@ -0,0 +1,17 @@
<div class="entry__details">
<strong><i class="fas fa-sign-language"></i> Range of motion</strong><br>
<strong>Motion</strong>: {{entry.rangeofmotion.get_motion_kind_display}}&emsp;
{% if entry.rangeofmotion.antiembolic_stockings %}
Has antiembolic stockings
{% endif %}
{% if entry.rangeofmotion.turn_or_position_change %}
Patient had a turn or position change
{% endif %}
{% if entry.notes %}
<br>
<strong>Notes</strong>: {{entry.notes}}
{% endif %}
</div>
<div class="entry__actions">
<a href="{% url "rom-update" chart.pk entry.rangeofmotion.pk %}">Edit</a>
</div>

View File

@ -0,0 +1,28 @@
<div class="entry__details">
<strong><i class="far fa-life-ring"></i> Safety</strong><br>
<strong>Call light in reach?</strong> {{entry.safety.call_light_in_reach|yesno:"Yes,No"}}&emsp;
<strong>Bed low?</strong> {{entry.safety.bed_low|yesno:"Yes,No"}}&emsp;
<strong>Brakes on?</strong> {{entry.safety.brakes_on|yesno:"Yes,No"}}&emsp;
{% if entry.safety.falls %}
<strong>Falls</strong>: {{entry.safety.falls}}&emsp;
{% endif %}
{% if entry.safety.nausea %}
<strong>Nausea</strong>: {{entry.safety.nausea}}&emsp;
{% endif %}
{% if entry.safety.vomiting %}
<strong>Vomiting</strong>: {{entry.safety.vomiting}}&emsp;
{% endif %}
{% if entry.safety.confusion %}
<strong>Confusion</strong>: {{entry.safety.confusion}}&emsp;
{% endif %}
{% if entry.safety.combative %}
<strong>Combative</strong>: {{entry.safety.combative}}
{% endif %}
{% if entry.notes %}
<br>
<strong>Notes</strong>: {{entry.notes}}
{% endif %}
</div>
<div class="entry__actions">
<a href="{% url "safety-update" chart.pk entry.safety.pk %}">Edit</a>
</div>

View File

@ -0,0 +1,27 @@
<div class="entry__details">
<strong>
<i class="fas fa-bath"></i> Toileting
{% if entry.toileting.method %}
via {{entry.toileting.get_method_display}}&emsp;
{% endif %}
</strong><br>
{% if entry.toileting.assistance %}
Patient was {{entry.toileting.get_assistance_display}}&emsp;
{% endif %}
{% if entry.toileting.brief_change %}
Had a brief change&emsp;
{% endif %}
{% if entry.toileting.perineal_care %}
Had perineal care&emsp;
{% endif %}
{% if entry.toileting.catheter_care %}
Had catheter care&emsp;
{% endif %}
{% if entry.notes %}
<br>
<strong>Notes</strong>: {{entry.notes}}
{% endif %}
</div>
<div class="entry__actions">
<a href="{% url "toileting-update" chart.pk entry.toileting.pk %}">Edit</a>
</div>

View File

@ -0,0 +1,15 @@
<div class="entry__details">
<strong><i class="fas fa-tint"></i> Urine</strong><br>
{% if entry.urine.assistance %}
Patient was {{entry.urine.get_assistance_display}}&emsp;
{% endif %}
<strong>Color</strong>: {{entry.urine.get_color_display}}&emsp;
<strong>Amount</strong>: {{entry.urine.amount}}mL
{% if entry.notes %}
<br>
<strong>Notes</strong>: {{entry.notes}}
{% endif %}
</div>
<div class="entry__actions">
<a href="{% url "urine-update" chart.pk entry.urine.pk %}">Edit</a>
</div>

View File

@ -0,0 +1,63 @@
<div class="entry__details">
<strong><i class="fas fa-heartbeat"></i> Vitals</strong><br>
<table>
<thead>
<tr>
{% if entry.vitals.o2_saturation %}
<td>O2</td>
{% endif %}
{% if entry.vitals.temperature %}
<td>T</td>
{% endif %}
{% if entry.vitals.pulse %}
<td>P</td>
{% endif %}
{% if entry.vitals.respirations %}
<td>R</td>
{% endif %}
{% if entry.vitals.blood_pressure_systolic %}
<td>BP</td>
{% endif %}
{% if entry.vitals.weight %}
<td>W</td>
{% endif %}
</tr>
</thead>
<tbody>
<tr>
{% if entry.vitals.o2_saturation %}
<td>{{entry.vitals.o2_saturation}}%</td>
{% endif %}
{% if entry.vitals.temperature %}
<td>
{{entry.vitals.temperature}}&deg;{{entry.vitals.get_temperature_units_display}}
</td>
{% endif %}
{% if entry.vitals.pulse %}
<td><sup>{{entry.vitals.pulse}}</sup>&frasl;<sub>min</sub></td>
{% endif %}
{% if entry.vitals.respirations %}
<td><sup>{{entry.vitals.respirations}}</sup>&frasl;<sub>min</sub></td>
{% endif %}
{% if entry.vitals.blood_pressure_systolic %}
<td>
<sup>{{entry.vitals.blood_pressure_systolic}}</sup>&frasl;
<sub>{{entry.vitals.blood_pressure_diastolic}}</sub>
</td>
{% endif %}
{% if entry.vitals.weight %}
<td>
{{entry.vitals.weight}}
{{entry.vitals.get_weight_units_display}}
</td>
{% endif %}
</tr>
</tbody>
</table>
{% if entry.notes %}
<strong>Notes</strong>: {{entry.notes}}
{% endif %}
</div>
<div class="entry__actions">
<a href="{% url "vitals-update" chart.pk entry.vitals.pk %}">Edit</a>
</div>

View File

@ -0,0 +1,13 @@
{% extends "base.html" %}
{% block content %}
<article class="chart">
<h1>Delete entry</h1>
<form method="post" action="{{request.get_full_path}}">
{% csrf_token %}
<p class="form__submit">
<input class="action-button destroy" type="submit" value="Confirm delete entry"> or <a href="{% url 'chart-detail' object.chart_id %}">cancel</a>
</p>
</form>
</article>
{% endblock %}

View File

@ -0,0 +1,16 @@
{% extends "base.html" %}
{% block content %}
<article class="chart">
<h1><i class="fas fa-plus"></i> New {{form.name}} entry</h1>
<section>
<form method="POST" action="{{request.get_full_path}}">
{% csrf_token %}
{{form.as_p}}
<p class="form__submit">
<input class="action-button" type="submit" value="Create entry"> or <a href="{% url 'chart-detail' chart.pk %}">cancel</a>
</p>
</form>
</section>
</article>
{% endblock %}

View File

@ -0,0 +1,19 @@
{% extends "base.html" %}
{% block content %}
<article class="chart">
<header class="chart__heading">
<h1>Update entry</h1>
<a class="action-button destroy" href="{% url 'entry-delete' chart.pk object.pk %}">Delete</a>
</header>
<section>
<form method="POST" action="{{request.get_full_path}}">
{% csrf_token %}
{{form.as_p}}
<p class="form__submit">
<input class="action-button destroy" type="submit" value="Save changes"> or <a href="{% url 'chart-detail' chart.pk %}">cancel</a>
</p>
</form>
</section>
</article>
{% endblock %}

3
charts/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

168
charts/urls.py Normal file
View File

@ -0,0 +1,168 @@
from django.urls import path, include
from . import views
urlpatterns = [
path('', views.chart.ListView.as_view(), name='chart-list'),
path('new/', views.chart.CreateView.as_view(), name='chart-create'),
path('<int:pk>/', include([
path('', views.chart.DetailView.as_view(), name='chart-detail'),
path('update/', views.chart.UpdateView.as_view(), name='chart-update'),
path('delete/', views.chart.DeleteView.as_view(), name='chart-delete'),
# path('download/', views.DownloadPDFView.as_view(), name="chart-download"),
# path('preview/', views.PreviewPDFView.as_view(), name="chart-preview"),
path('entries/', views.entry.ListView.as_view(), name='entry-list'),
path('entries/new/', views.entry.CreateView.as_view(), name='entry-create'),
path('entries/<int:entry_pk>/', include([
path('', views.entry.DetailView.as_view(), name='entry-detail'),
path('update/', views.entry.UpdateView.as_view(), name='entry-update'),
path('delete/', views.entry.DeleteView.as_view(), name='entry-delete'),
])),
path('oral-care/', views.oral_care.ListView.as_view(), name='oral-list'),
path('oral-care/new/', views.oral_care.CreateView.as_view(), name='oral-create'),
path('oral-care/<int:entry_pk>/', include([
path('', views.oral_care.DetailView.as_view(), name='oral-detail'),
path('update/', views.oral_care.UpdateView.as_view(), name='oral-update'),
path('delete/', views.oral_care.DeleteView.as_view(), name='oral-delete'),
])),
path('bathing/', views.bathing.ListView.as_view(), name='bathing-list'),
path('bathing/new/', views.bathing.CreateView.as_view(), name='bathing-create'),
path('bathing/<int:entry_pk>/', include([
path('', views.bathing.DetailView.as_view(), name='bathing-detail'),
path('update/', views.bathing.UpdateView.as_view(), name='bathing-update'),
path('delete/', views.bathing.DeleteView.as_view(), name='bathing-delete'),
])),
path('toileting/', views.toileting.ListView.as_view(), name='toileting-list'),
path('toileting/new/', views.toileting.CreateView.as_view(), name='toileting-create'),
path('toileting/<int:entry_pk>/', include([
path('', views.toileting.DetailView.as_view(), name='toileting-detail'),
path('update/', views.toileting.UpdateView.as_view(), name='toileting-update'),
path('delete/', views.toileting.DeleteView.as_view(), name='toileting-delete'),
])),
path('dressing/', views.dressing.ListView.as_view(), name='dressing-list'),
path('dressing/new/', views.dressing.CreateView.as_view(), name='dressing-create'),
path('dressing/<int:entry_pk>/', include([
path('', views.dressing.DetailView.as_view(), name='dressing-detail'),
path('update/', views.dressing.UpdateView.as_view(), name='dressing-update'),
path('delete/', views.dressing.DeleteView.as_view(), name='dressing-delete'),
])),
path('meals/', views.meal.ListView.as_view(), name='meal-list'),
path('meals/new/', views.meal.CreateView.as_view(), name='meal-create'),
path('meals/<int:entry_pk>/', include([
path('', views.meal.DetailView.as_view(), name='meal-detail'),
path('update/', views.meal.UpdateView.as_view(), name='meal-update'),
path('delete/', views.meal.DeleteView.as_view(), name='meal-delete'),
])),
path('range-of-motion/', views.rom.ListView.as_view(), name='rom-list'),
path('range-of-motion/new/', views.rom.CreateView.as_view(), name='rom-create'),
path('range-of-motion/<int:entry_pk>/', include([
path('', views.rom.DetailView.as_view(), name='rom-detail'),
path('update/', views.rom.UpdateView.as_view(), name='rom-update'),
path('delete/', views.rom.DeleteView.as_view(), name='rom-delete'),
])),
path('ambulations/', views.ambulation.ListView.as_view(), name='ambulation-list'),
path('ambulations/new/', views.ambulation.CreateView.as_view(), name='ambulation-create'),
path('ambulations/<int:entry_pk>/', include([
path('', views.ambulation.DetailView.as_view(), name='ambulation-detail'),
path('update/', views.ambulation.UpdateView.as_view(), name='ambulation-update'),
path('delete/', views.ambulation.DeleteView.as_view(), name='ambulation-delete'),
])),
path('vitals/', views.vitals.ListView.as_view(), name='vitals-list'),
path('vitals/new/', views.vitals.CreateView.as_view(), name='vitals-create'),
path('vitals/<int:entry_pk>/', include([
path('', views.vitals.DetailView.as_view(), name='vitals-detail'),
path('update/', views.vitals.UpdateView.as_view(), name='vitals-update'),
path('delete/', views.vitals.DeleteView.as_view(), name='vitals-delete'),
])),
path('fluid-intake/', views.fluid_intake.ListView.as_view(), name='fluid-list'),
path('fluid-intake/new/', views.fluid_intake.CreateView.as_view(), name='fluid-create'),
path('fluid-intake/<int:entry_pk>/', include([
path('', views.fluid_intake.DetailView.as_view(), name='fluid-detail'),
path('update/', views.fluid_intake.UpdateView.as_view(), name='fluid-update'),
path('delete/', views.fluid_intake.DeleteView.as_view(), name='fluid-delete'),
])),
path('urine/', views.urine.ListView.as_view(), name='urine-list'),
path('urine/new/', views.urine.CreateView.as_view(), name='urine-create'),
path('urine/<int:entry_pk>/', include([
path('', views.urine.DetailView.as_view(), name='urine-detail'),
path('update/', views.urine.UpdateView.as_view(), name='urine-update'),
path('delete/', views.urine.DeleteView.as_view(), name='urine-delete'),
])),
path('emesis/', views.emesis.ListView.as_view(), name='emesis-list'),
path('emesis/new/', views.emesis.CreateView.as_view(), name='emesis-create'),
path('emesis/<int:entry_pk>/', include([
path('', views.emesis.DetailView.as_view(), name='emesis-detail'),
path('update/', views.emesis.UpdateView.as_view(), name='emesis-update'),
path('delete/', views.emesis.DeleteView.as_view(), name='emesis-delete'),
])),
path('movements/', views.bowel_movement.ListView.as_view(), name='bm-list'),
path('movements/new/', views.bowel_movement.CreateView.as_view(), name='bm-create'),
path('movements/<int:entry_pk>/', include([
path('', views.bowel_movement.DetailView.as_view(), name='bm-detail'),
path('update/', views.bowel_movement.UpdateView.as_view(), name='bm-update'),
path('delete/', views.bowel_movement.DeleteView.as_view(), name='bm-delete'),
])),
path('pain-level/', views.pain_level.ListView.as_view(), name='pain-list'),
path('pain-level/new/', views.pain_level.CreateView.as_view(), name='pain-create'),
path('pain-level/<int:entry_pk>/', include([
path('', views.pain_level.DetailView.as_view(), name='pain-detail'),
path('update/', views.pain_level.UpdateView.as_view(), name='pain-update'),
path('delete/', views.pain_level.DeleteView.as_view(), name='pain-delete'),
])),
path('oxygen-level/', views.oxygen_level.ListView.as_view(), name='oxygen-list'),
path('oxygen-level/new/', views.oxygen_level.CreateView.as_view(), name='oxygen-create'),
path('oxygen-level/<int:entry_pk>/', include([
path('', views.oxygen_level.DetailView.as_view(), name='oxygen-detail'),
path('update/', views.oxygen_level.UpdateView.as_view(), name='oxygen-update'),
path('delete/', views.oxygen_level.DeleteView.as_view(), name='oxygen-delete'),
])),
path('restaints/', views.restraint.ListView.as_view(), name='restraint-list'),
path('restaints/new/', views.restraint.CreateView.as_view(), name='restraint-create'),
path('restaints/<int:entry_pk>/', include([
path('', views.restraint.DetailView.as_view(), name='restraint-detail'),
path('update/', views.restraint.UpdateView.as_view(), name='restraint-update'),
path('delete/', views.restraint.DeleteView.as_view(), name='restraint-delete'),
])),
path('safety/', views.safety.ListView.as_view(), name='safety-list'),
path('safety/new/', views.safety.CreateView.as_view(), name='safety-create'),
path('safety/<int:entry_pk>/', include([
path('', views.safety.DetailView.as_view(), name='safety-detail'),
path('update/', views.safety.UpdateView.as_view(), name='safety-update'),
path('delete/', views.safety.DeleteView.as_view(), name='safety-delete'),
])),
])),
]

18
charts/views/__init__.py Normal file
View File

@ -0,0 +1,18 @@
from .chart import *
from .entry import *
from .oral_care import *
from .bathing import *
from .toileting import *
from .dressing import *
from .meal import *
from .rom import *
from .ambulation import *
from .vitals import *
from .fluid_intake import *
from .urine import *
from .emesis import *
from .bowel_movement import *
from .pain_level import *
from .oxygen_level import *
from .restraint import *
from .safety import *

View File

@ -0,0 +1,64 @@
from django.shortcuts import render
from django.urls import reverse_lazy, reverse
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from charts.models import Chart, Ambulation
from charts.forms import AmbulationForm
from django.db.models import (Exists, OuterRef,
Prefetch, Subquery, Count, Sum, F, Q, Value)
from django.db.models.functions import Length, Upper
# Entries
class ListView(ListView):
model = Ambulation
pk_url_kwarg = 'entry_pk'
class CreateView(CreateView):
model = Ambulation
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_create_form.html'
form_class = AmbulationForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.chart = Chart.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})
class DetailView(DetailView):
model = Ambulation
pk_url_kwarg = 'entry_pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class UpdateView(UpdateView):
model = Ambulation
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_form.html'
form_class = AmbulationForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class DeleteView(DeleteView):
model = Ambulation
pk_url_kwarg = 'entry_pk'
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})

64
charts/views/bathing.py Normal file
View File

@ -0,0 +1,64 @@
from django.shortcuts import render
from django.urls import reverse_lazy, reverse
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from charts.models import Chart, Bathing
from charts.forms import BathingForm
from django.db.models import (Exists, OuterRef,
Prefetch, Subquery, Count, Sum, F, Q, Value)
from django.db.models.functions import Length, Upper
# Entries
class ListView(ListView):
model = Bathing
pk_url_kwarg = 'entry_pk'
class CreateView(CreateView):
model = Bathing
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_create_form.html'
form_class = BathingForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.chart = Chart.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})
class DetailView(DetailView):
model = Bathing
pk_url_kwarg = 'entry_pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class UpdateView(UpdateView):
model = Bathing
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_form.html'
form_class = BathingForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class DeleteView(DeleteView):
model = Bathing
pk_url_kwarg = 'entry_pk'
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})

View File

@ -0,0 +1,64 @@
from django.shortcuts import render
from django.urls import reverse_lazy, reverse
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from charts.models import Chart, BowelMovement
from charts.forms import BowelMovementForm
from django.db.models import (Exists, OuterRef,
Prefetch, Subquery, Count, Sum, F, Q, Value)
from django.db.models.functions import Length, Upper
# Entries
class ListView(ListView):
model = BowelMovement
pk_url_kwarg = 'entry_pk'
class CreateView(CreateView):
model = BowelMovement
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_create_form.html'
form_class = BowelMovementForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.chart = Chart.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})
class DetailView(DetailView):
model = BowelMovement
pk_url_kwarg = 'entry_pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class UpdateView(UpdateView):
model = BowelMovement
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_form.html'
form_class = BowelMovementForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class DeleteView(DeleteView):
model = BowelMovement
pk_url_kwarg = 'entry_pk'
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})

60
charts/views/chart.py Normal file
View File

@ -0,0 +1,60 @@
from django.shortcuts import render
from django.urls import reverse_lazy, reverse
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from charts.models import Chart, Entry
from charts.forms import ChartForm
from django.db.models import (Exists, OuterRef,
Prefetch, Subquery, Count, Sum, F, Q, Value)
from django.db.models.functions import Length, Upper
class ListView(ListView):
model = Chart
class CreateView(CreateView):
model = Chart
template_name_suffix = '_create_form'
form_class = ChartForm
class DetailView(DetailView):
model = Chart
def get_object(self):
queryset = Chart.objects.filter(
pk=self.kwargs.get(self.pk_url_kwarg)
).prefetch_related(
Prefetch('entry_set', queryset=Entry.objects.select_related(
'oralcare',
'bathing',
'toileting',
'dressing',
'meal',
'rangeofmotion',
'ambulation',
'vitals',
'fluidintake',
'urine',
'emesis',
'bowelmovement',
'painlevel',
'oxygenlevel',
'restraint',
'safety',
))
)
obj = queryset.get()
return obj
class UpdateView(UpdateView):
model = Chart
form_class = ChartForm
class DeleteView(DeleteView):
model = Chart
success_url = reverse_lazy('chart-list')

64
charts/views/dressing.py Normal file
View File

@ -0,0 +1,64 @@
from django.shortcuts import render
from django.urls import reverse_lazy, reverse
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from charts.models import Chart, Dressing
from charts.forms import DressingForm
from django.db.models import (Exists, OuterRef,
Prefetch, Subquery, Count, Sum, F, Q, Value)
from django.db.models.functions import Length, Upper
# Entries
class ListView(ListView):
model = Dressing
pk_url_kwarg = 'entry_pk'
class CreateView(CreateView):
model = Dressing
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_create_form.html'
form_class = DressingForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.chart = Chart.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})
class DetailView(DetailView):
model = Dressing
pk_url_kwarg = 'entry_pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class UpdateView(UpdateView):
model = Dressing
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_form.html'
form_class = DressingForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class DeleteView(DeleteView):
model = Dressing
pk_url_kwarg = 'entry_pk'
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})

64
charts/views/emesis.py Normal file
View File

@ -0,0 +1,64 @@
from django.shortcuts import render
from django.urls import reverse_lazy, reverse
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from charts.models import Chart, Emesis
from charts.forms import EmesisForm
from django.db.models import (Exists, OuterRef,
Prefetch, Subquery, Count, Sum, F, Q, Value)
from django.db.models.functions import Length, Upper
# Entries
class ListView(ListView):
model = Emesis
pk_url_kwarg = 'entry_pk'
class CreateView(CreateView):
model = Emesis
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_create_form.html'
form_class = EmesisForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.chart = Chart.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})
class DetailView(DetailView):
model = Emesis
pk_url_kwarg = 'entry_pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class UpdateView(UpdateView):
model = Emesis
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_form.html'
form_class = EmesisForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class DeleteView(DeleteView):
model = Emesis
pk_url_kwarg = 'entry_pk'
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})

63
charts/views/entry.py Normal file
View File

@ -0,0 +1,63 @@
from django.shortcuts import render
from django.urls import reverse_lazy, reverse
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from charts.models import Chart, Entry
from charts.forms import EntryForm
from django.db.models import (Exists, OuterRef,
Prefetch, Subquery, Count, Sum, F, Q, Value)
from django.db.models.functions import Length, Upper
# Entries
class ListView(ListView):
model = Entry
pk_url_kwarg = 'entry_pk'
class CreateView(CreateView):
model = Entry
pk_url_kwarg = 'entry_pk'
template_name_suffix = '_create_form'
form_class = EntryForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.chart = Chart.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})
class DetailView(DetailView):
model = Entry
pk_url_kwarg = 'entry_pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class UpdateView(UpdateView):
model = Entry
pk_url_kwarg = 'entry_pk'
form_class = EntryForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class DeleteView(DeleteView):
model = Entry
pk_url_kwarg = 'entry_pk'
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})

View File

@ -0,0 +1,64 @@
from django.shortcuts import render
from django.urls import reverse_lazy, reverse
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from charts.models import Chart, FluidIntake
from charts.forms import FluidIntakeForm
from django.db.models import (Exists, OuterRef,
Prefetch, Subquery, Count, Sum, F, Q, Value)
from django.db.models.functions import Length, Upper
# Entries
class ListView(ListView):
model = FluidIntake
pk_url_kwarg = 'entry_pk'
class CreateView(CreateView):
model = FluidIntake
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_create_form.html'
form_class = FluidIntakeForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.chart = Chart.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})
class DetailView(DetailView):
model = FluidIntake
pk_url_kwarg = 'entry_pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class UpdateView(UpdateView):
model = FluidIntake
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_form.html'
form_class = FluidIntakeForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class DeleteView(DeleteView):
model = FluidIntake
pk_url_kwarg = 'entry_pk'
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})

64
charts/views/meal.py Normal file
View File

@ -0,0 +1,64 @@
from django.shortcuts import render
from django.urls import reverse_lazy, reverse
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from charts.models import Chart, Meal
from charts.forms import MealForm
from django.db.models import (Exists, OuterRef,
Prefetch, Subquery, Count, Sum, F, Q, Value)
from django.db.models.functions import Length, Upper
# Entries
class ListView(ListView):
model = Meal
pk_url_kwarg = 'entry_pk'
class CreateView(CreateView):
model = Meal
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_create_form.html'
form_class = MealForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.chart = Chart.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})
class DetailView(DetailView):
model = Meal
pk_url_kwarg = 'entry_pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class UpdateView(UpdateView):
model = Meal
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_form.html'
form_class = MealForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class DeleteView(DeleteView):
model = Meal
pk_url_kwarg = 'entry_pk'
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})

64
charts/views/oral_care.py Normal file
View File

@ -0,0 +1,64 @@
from django.shortcuts import render
from django.urls import reverse_lazy, reverse
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from charts.models import Chart, OralCare
from charts.forms import OralCareForm
from django.db.models import (Exists, OuterRef,
Prefetch, Subquery, Count, Sum, F, Q, Value)
from django.db.models.functions import Length, Upper
# Entries
class ListView(ListView):
model = OralCare
pk_url_kwarg = 'entry_pk'
class CreateView(CreateView):
model = OralCare
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_create_form.html'
form_class = OralCareForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.chart = Chart.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})
class DetailView(DetailView):
model = OralCare
pk_url_kwarg = 'entry_pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class UpdateView(UpdateView):
model = OralCare
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_form.html'
form_class = OralCareForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class DeleteView(DeleteView):
model = OralCare
pk_url_kwarg = 'entry_pk'
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})

View File

@ -0,0 +1,64 @@
from django.shortcuts import render
from django.urls import reverse_lazy, reverse
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from charts.models import Chart, OxygenLevel
from charts.forms import OxygenLevelForm
from django.db.models import (Exists, OuterRef,
Prefetch, Subquery, Count, Sum, F, Q, Value)
from django.db.models.functions import Length, Upper
# Entries
class ListView(ListView):
model = OxygenLevel
pk_url_kwarg = 'entry_pk'
class CreateView(CreateView):
model = OxygenLevel
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_create_form.html'
form_class = OxygenLevelForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.chart = Chart.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})
class DetailView(DetailView):
model = OxygenLevel
pk_url_kwarg = 'entry_pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class UpdateView(UpdateView):
model = OxygenLevel
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_form.html'
form_class = OxygenLevelForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class DeleteView(DeleteView):
model = OxygenLevel
pk_url_kwarg = 'entry_pk'
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})

View File

@ -0,0 +1,64 @@
from django.shortcuts import render
from django.urls import reverse_lazy, reverse
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from charts.models import Chart, PainLevel
from charts.forms import PainLevelForm
from django.db.models import (Exists, OuterRef,
Prefetch, Subquery, Count, Sum, F, Q, Value)
from django.db.models.functions import Length, Upper
# Entries
class ListView(ListView):
model = PainLevel
pk_url_kwarg = 'entry_pk'
class CreateView(CreateView):
model = PainLevel
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_create_form.html'
form_class = PainLevelForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.chart = Chart.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})
class DetailView(DetailView):
model = PainLevel
pk_url_kwarg = 'entry_pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class UpdateView(UpdateView):
model = PainLevel
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_form.html'
form_class = PainLevelForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class DeleteView(DeleteView):
model = PainLevel
pk_url_kwarg = 'entry_pk'
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})

64
charts/views/restraint.py Normal file
View File

@ -0,0 +1,64 @@
from django.shortcuts import render
from django.urls import reverse_lazy, reverse
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from charts.models import Chart, Restraint
from charts.forms import RestraintForm
from django.db.models import (Exists, OuterRef,
Prefetch, Subquery, Count, Sum, F, Q, Value)
from django.db.models.functions import Length, Upper
# Entries
class ListView(ListView):
model = Restraint
pk_url_kwarg = 'entry_pk'
class CreateView(CreateView):
model = Restraint
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_create_form.html'
form_class = RestraintForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.chart = Chart.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})
class DetailView(DetailView):
model = Restraint
pk_url_kwarg = 'entry_pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class UpdateView(UpdateView):
model = Restraint
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_form.html'
form_class = RestraintForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class DeleteView(DeleteView):
model = Restraint
pk_url_kwarg = 'entry_pk'
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})

64
charts/views/rom.py Normal file
View File

@ -0,0 +1,64 @@
from django.shortcuts import render
from django.urls import reverse_lazy, reverse
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from charts.models import Chart, RangeOfMotion
from charts.forms import RangeOfMotionForm
from django.db.models import (Exists, OuterRef,
Prefetch, Subquery, Count, Sum, F, Q, Value)
from django.db.models.functions import Length, Upper
# Entries
class ListView(ListView):
model = RangeOfMotion
pk_url_kwarg = 'entry_pk'
class CreateView(CreateView):
model = RangeOfMotion
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_create_form.html'
form_class = RangeOfMotionForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.chart = Chart.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})
class DetailView(DetailView):
model = RangeOfMotion
pk_url_kwarg = 'entry_pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class UpdateView(UpdateView):
model = RangeOfMotion
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_form.html'
form_class = RangeOfMotionForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class DeleteView(DeleteView):
model = RangeOfMotion
pk_url_kwarg = 'entry_pk'
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})

64
charts/views/safety.py Normal file
View File

@ -0,0 +1,64 @@
from django.shortcuts import render
from django.urls import reverse_lazy, reverse
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from charts.models import Chart, Safety
from charts.forms import SafetyForm
from django.db.models import (Exists, OuterRef,
Prefetch, Subquery, Count, Sum, F, Q, Value)
from django.db.models.functions import Length, Upper
# Entries
class ListView(ListView):
model = Safety
pk_url_kwarg = 'entry_pk'
class CreateView(CreateView):
model = Safety
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_create_form.html'
form_class = SafetyForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.chart = Chart.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})
class DetailView(DetailView):
model = Safety
pk_url_kwarg = 'entry_pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class UpdateView(UpdateView):
model = Safety
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_form.html'
form_class = SafetyForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class DeleteView(DeleteView):
model = Safety
pk_url_kwarg = 'entry_pk'
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})

64
charts/views/toileting.py Normal file
View File

@ -0,0 +1,64 @@
from django.shortcuts import render
from django.urls import reverse_lazy, reverse
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from charts.models import Chart, Toileting
from charts.forms import ToiletingForm
from django.db.models import (Exists, OuterRef,
Prefetch, Subquery, Count, Sum, F, Q, Value)
from django.db.models.functions import Length, Upper
# Entries
class ListView(ListView):
model = Toileting
pk_url_kwarg = 'entry_pk'
class CreateView(CreateView):
model = Toileting
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_create_form.html'
form_class = ToiletingForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.chart = Chart.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})
class DetailView(DetailView):
model = Toileting
pk_url_kwarg = 'entry_pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class UpdateView(UpdateView):
model = Toileting
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_form.html'
form_class = ToiletingForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class DeleteView(DeleteView):
model = Toileting
pk_url_kwarg = 'entry_pk'
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})

64
charts/views/urine.py Normal file
View File

@ -0,0 +1,64 @@
from django.shortcuts import render
from django.urls import reverse_lazy, reverse
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from charts.models import Chart, Urine
from charts.forms import UrineForm
from django.db.models import (Exists, OuterRef,
Prefetch, Subquery, Count, Sum, F, Q, Value)
from django.db.models.functions import Length, Upper
# Entries
class ListView(ListView):
model = Urine
pk_url_kwarg = 'entry_pk'
class CreateView(CreateView):
model = Urine
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_create_form.html'
form_class = UrineForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.chart = Chart.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})
class DetailView(DetailView):
model = Urine
pk_url_kwarg = 'entry_pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class UpdateView(UpdateView):
model = Urine
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_form.html'
form_class = UrineForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class DeleteView(DeleteView):
model = Urine
pk_url_kwarg = 'entry_pk'
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})

64
charts/views/vitals.py Normal file
View File

@ -0,0 +1,64 @@
from django.shortcuts import render
from django.urls import reverse_lazy, reverse
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from charts.models import Chart, Vitals
from charts.forms import VitalsForm
from django.db.models import (Exists, OuterRef,
Prefetch, Subquery, Count, Sum, F, Q, Value)
from django.db.models.functions import Length, Upper
# Entries
class ListView(ListView):
model = Vitals
pk_url_kwarg = 'entry_pk'
class CreateView(CreateView):
model = Vitals
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_create_form.html'
form_class = VitalsForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.chart = Chart.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})
class DetailView(DetailView):
model = Vitals
pk_url_kwarg = 'entry_pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class UpdateView(UpdateView):
model = Vitals
pk_url_kwarg = 'entry_pk'
template_name = 'charts/entry_form.html'
form_class = VitalsForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['chart'] = Chart.objects.get(pk=self.kwargs['pk'])
return context
class DeleteView(DeleteView):
model = Vitals
pk_url_kwarg = 'entry_pk'
def get_success_url(self):
return reverse('chart-detail', kwargs={'pk': self.kwargs['pk']})

0
cna_charting/__init__.py Normal file
View File

16
cna_charting/asgi.py Normal file
View File

@ -0,0 +1,16 @@
"""
ASGI config for cna_charting project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cna_charting.settings')
application = get_asgi_application()

View File

@ -0,0 +1,17 @@
import pytz
from django.utils import timezone
class TimezoneMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
tzname = request.COOKIES.get('timezone')
if request.user.is_authenticated and hasattr(request.user, 'profile'):
tzname = request.user.profile.timezone
if tzname:
timezone.activate(pytz.timezone(tzname))
else:
timezone.deactivate()
return self.get_response(request)

158
cna_charting/settings.py Normal file
View File

@ -0,0 +1,158 @@
"""
Django settings for cna_charting project.
Generated by 'django-admin startproject' using Django 3.1.7.
For more information on this file, see
https://docs.djangoproject.com/en/3.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""
import ast
import os
import warnings
from pathlib import Path
from django.core.management.utils import get_random_secret_key
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
with open('/etc/secret_key.txt') as f:
SECRET_KEY = f.read().strip()
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
if not SECRET_KEY and DEBUG:
warnings.warn("SECRET_KEY not configured, using a random temporary key.")
SECRET_KEY = get_random_secret_key()
ALLOWED_HOSTS = ['127.0.0.1', 'localhost', '74.207.250.157', 'cna-charting.nathanjchapman.com']
ADMINS = (
('Nathan Chapman', 'debug@nathanjchapman.com'),
)
MANAGERS = ADMINS
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'charts.apps.ChartsConfig',
'landing.apps.LandingConfig',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'cna_charting.middleware.TimezoneMiddleware',
]
ROOT_URLCONF = 'cna_charting.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'cna_charting.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'cna_charting',
'USER': 'django',
'PASSWORD': 'V$+`-#Urty=yg8[2q5zs6B',
'HOST': 'localhost',
'PORT': '',
}
}
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
# Password validation
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/
STATICFILES_DIRS = [
BASE_DIR / 'static'
]
STATIC_URL = '/static/'
STATIC_ROOT = '/var/www/cna-charting.nathanjchapman.com/static/'
# Media file storage
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
# Sites
SITE_ID = 1

View File

@ -0,0 +1,158 @@
"""
Django settings for cna_charting project.
Generated by 'django-admin startproject' using Django 3.1.7.
For more information on this file, see
https://docs.djangoproject.com/en/3.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""
import ast
import os
import warnings
from pathlib import Path
from django.core.management.utils import get_random_secret_key
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
if not SECRET_KEY and DEBUG:
warnings.warn("SECRET_KEY not configured, using a random temporary key.")
SECRET_KEY = get_random_secret_key()
ALLOWED_HOSTS = ['127.0.0.1', 'localhost']
INTERNAL_IPS = [
'localhost',
'127.0.0.1',
]
ADMINS = (
('Nathan Chapman', 'debug@nathanjchapman.com'),
)
MANAGERS = ADMINS
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'charts.apps.ChartsConfig',
'landing.apps.LandingConfig',
'debug_toolbar',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'cna_charting.middleware.TimezoneMiddleware',
'debug_toolbar.middleware.DebugToolbarMiddleware',
]
ROOT_URLCONF = 'cna_charting.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'cna_charting.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
# Password validation
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = '/static/'
STATICFILES_DIRS = [BASE_DIR / 'static']
# Media file storage
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
# Sites
SITE_ID = 1

31
cna_charting/urls.py Normal file
View File

@ -0,0 +1,31 @@
"""cna_charting URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
import debug_toolbar
from django.conf import settings
from django.contrib import admin
from django.urls import include, path
from django.views.generic import TemplateView
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('landing.urls')),
path('charts/', include('charts.urls')),
]
if settings.DEBUG:
urlpatterns += [
path('__debug__/', include(debug_toolbar.urls)),
]

16
cna_charting/wsgi.py Normal file
View File

@ -0,0 +1,16 @@
"""
WSGI config for cna_charting project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cna_charting.settings')
application = get_wsgi_application()

View File

View File

@ -0,0 +1,66 @@
import os
import time
from selenium.webdriver.firefox.webdriver import WebDriver
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import WebDriverException
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from charts import models
class TestCharts(StaticLiveServerTestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.browser = WebDriver()
@classmethod
def tearDownClass(cls):
cls.browser.quit()
super().tearDownClass()
def test_shows_home_page_with_create_chart_button(self):
self.browser.get(self.live_server_url)
self.assertEqual(self.browser.title, "BTech CNA Charting")
create_chart_button = self.browser.find_element_by_css_selector("main .action-button")
self.assertIn(
create_chart_button.get_attribute("href"),
self.live_server_url + '/charts/new/'
)
self.assertIn(
'Welcome to\nBTech CNA Charting',
self.browser.find_element_by_css_selector('main header').text
)
create_chart_button.click()
self.assertEqual(
self.browser.current_url,
self.live_server_url + '/charts/new/'
)
def test_create_chart(self):
self.browser.get(f'{self.live_server_url}/charts/new/')
student_name_input = self.browser.find_element_by_css_selector("input[name=student_name]")
student_id_input = self.browser.find_element_by_css_selector("input[name=student_id]")
patient_input = self.browser.find_element_by_css_selector("input[name=patient]")
assignment_input = self.browser.find_element_by_css_selector("input[name=assignment]")
submit_button = self.browser.find_element_by_css_selector("input[value=Continue]")
student_name_input.send_keys('Nicholas Jenkins')
student_id_input.send_keys('12345')
patient_input.send_keys('Widmerpool')
assignment_input.send_keys('#1')
submit_button.click()
self.assertEqual(
self.browser.current_url,
self.live_server_url + '/charts/1/entry/'
)
self.assertIn(
'Widmerpool',
self.browser.find_element_by_css_selector(".chart__header").text
)

0
landing/__init__.py Normal file
View File

3
landing/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
landing/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class LandingConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'landing'

View File

3
landing/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@ -0,0 +1,13 @@
{% extends 'base.html' %}
{% block content %}
<article class="chart">
<header >
<h1>Welcome to<br>
BTech CNA Charting</h1>
</header>
<section>
<h3><a href="{% url 'chart-create' %}" class="action-button">Start a new chart &rarr;</a></h3>
</section>
</article>
{% endblock %}

3
landing/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

6
landing/urls.py Normal file
View File

@ -0,0 +1,6 @@
from django.urls import path, include
from .views import landing_view
urlpatterns = [
path('', landing_view, name='landing_view'),
]

4
landing/views.py Normal file
View File

@ -0,0 +1,4 @@
from django.shortcuts import render
def landing_view(request):
return render(request, 'landing/home.html')

22
manage.py Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cna_charting.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,34 @@
Font Awesome Free License
-------------------------
Font Awesome Free is free, open source, and GPL friendly. You can use it for
commercial projects, open source projects, or really almost whatever you want.
Full Font Awesome Free license: https://fontawesome.com/license/free.
# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
In the Font Awesome Free download, the CC BY 4.0 license applies to all icons
packaged as SVG and JS file types.
# Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL)
In the Font Awesome Free download, the SIL OFL license applies to all icons
packaged as web and desktop font files.
# Code: MIT License (https://opensource.org/licenses/MIT)
In the Font Awesome Free download, the MIT license applies to all non-font and
non-icon files.
# Attribution
Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font
Awesome Free files already contain embedded comments with sufficient
attribution, so you shouldn't need to do anything additional when using these
files normally.
We've kept attribution comments terse, so we ask that you do not actively work
to remove them from files, especially code. They're a great way for folks to
learn about Font Awesome.
# Brand Icons
All brand icons are trademarks of their respective owners. The use of these
trademarks does not indicate endorsement of the trademark holder by Font
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
to represent the company, product, or service to which they refer.**

View File

@ -0,0 +1,3 @@
console.log(`Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com
License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
`)

4616
static/fontawesome/css/all.css vendored Normal file

File diff suppressed because it is too large Load Diff

5
static/fontawesome/css/all.min.css vendored Normal file

File diff suppressed because one or more lines are too long

15
static/fontawesome/css/brands.css vendored Normal file
View File

@ -0,0 +1,15 @@
/*!
* Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
*/
@font-face {
font-family: 'Font Awesome 5 Brands';
font-style: normal;
font-weight: 400;
font-display: block;
src: url("../webfonts/fa-brands-400.eot");
src: url("../webfonts/fa-brands-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.woff") format("woff"), url("../webfonts/fa-brands-400.ttf") format("truetype"), url("../webfonts/fa-brands-400.svg#fontawesome") format("svg"); }
.fab {
font-family: 'Font Awesome 5 Brands';
font-weight: 400; }

5
static/fontawesome/css/brands.min.css vendored Normal file
View File

@ -0,0 +1,5 @@
/*!
* Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
*/
@font-face{font-family:"Font Awesome 5 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.eot);src:url(../webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.woff) format("woff"),url(../webfonts/fa-brands-400.ttf) format("truetype"),url(../webfonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:"Font Awesome 5 Brands";font-weight:400}

4582
static/fontawesome/css/fontawesome.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

15
static/fontawesome/css/regular.css vendored Normal file
View File

@ -0,0 +1,15 @@
/*!
* Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
*/
@font-face {
font-family: 'Font Awesome 5 Free';
font-style: normal;
font-weight: 400;
font-display: block;
src: url("../webfonts/fa-regular-400.eot");
src: url("../webfonts/fa-regular-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.woff") format("woff"), url("../webfonts/fa-regular-400.ttf") format("truetype"), url("../webfonts/fa-regular-400.svg#fontawesome") format("svg"); }
.far {
font-family: 'Font Awesome 5 Free';
font-weight: 400; }

View File

@ -0,0 +1,5 @@
/*!
* Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
*/
@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.eot);src:url(../webfonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.woff) format("woff"),url(../webfonts/fa-regular-400.ttf) format("truetype"),url(../webfonts/fa-regular-400.svg#fontawesome) format("svg")}.far{font-family:"Font Awesome 5 Free";font-weight:400}

16
static/fontawesome/css/solid.css vendored Normal file
View File

@ -0,0 +1,16 @@
/*!
* Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
*/
@font-face {
font-family: 'Font Awesome 5 Free';
font-style: normal;
font-weight: 900;
font-display: block;
src: url("../webfonts/fa-solid-900.eot");
src: url("../webfonts/fa-solid-900.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.woff") format("woff"), url("../webfonts/fa-solid-900.ttf") format("truetype"), url("../webfonts/fa-solid-900.svg#fontawesome") format("svg"); }
.fa,
.fas {
font-family: 'Font Awesome 5 Free';
font-weight: 900; }

5
static/fontawesome/css/solid.min.css vendored Normal file
View File

@ -0,0 +1,5 @@
/*!
* Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
*/
@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.eot);src:url(../webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.woff) format("woff"),url(../webfonts/fa-solid-900.ttf) format("truetype"),url(../webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.fas{font-family:"Font Awesome 5 Free";font-weight:900}

371
static/fontawesome/css/svg-with-js.css vendored Normal file
View File

@ -0,0 +1,371 @@
/*!
* Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
*/
svg:not(:root).svg-inline--fa {
overflow: visible; }
.svg-inline--fa {
display: inline-block;
font-size: inherit;
height: 1em;
overflow: visible;
vertical-align: -.125em; }
.svg-inline--fa.fa-lg {
vertical-align: -.225em; }
.svg-inline--fa.fa-w-1 {
width: 0.0625em; }
.svg-inline--fa.fa-w-2 {
width: 0.125em; }
.svg-inline--fa.fa-w-3 {
width: 0.1875em; }
.svg-inline--fa.fa-w-4 {
width: 0.25em; }
.svg-inline--fa.fa-w-5 {
width: 0.3125em; }
.svg-inline--fa.fa-w-6 {
width: 0.375em; }
.svg-inline--fa.fa-w-7 {
width: 0.4375em; }
.svg-inline--fa.fa-w-8 {
width: 0.5em; }
.svg-inline--fa.fa-w-9 {
width: 0.5625em; }
.svg-inline--fa.fa-w-10 {
width: 0.625em; }
.svg-inline--fa.fa-w-11 {
width: 0.6875em; }
.svg-inline--fa.fa-w-12 {
width: 0.75em; }
.svg-inline--fa.fa-w-13 {
width: 0.8125em; }
.svg-inline--fa.fa-w-14 {
width: 0.875em; }
.svg-inline--fa.fa-w-15 {
width: 0.9375em; }
.svg-inline--fa.fa-w-16 {
width: 1em; }
.svg-inline--fa.fa-w-17 {
width: 1.0625em; }
.svg-inline--fa.fa-w-18 {
width: 1.125em; }
.svg-inline--fa.fa-w-19 {
width: 1.1875em; }
.svg-inline--fa.fa-w-20 {
width: 1.25em; }
.svg-inline--fa.fa-pull-left {
margin-right: .3em;
width: auto; }
.svg-inline--fa.fa-pull-right {
margin-left: .3em;
width: auto; }
.svg-inline--fa.fa-border {
height: 1.5em; }
.svg-inline--fa.fa-li {
width: 2em; }
.svg-inline--fa.fa-fw {
width: 1.25em; }
.fa-layers svg.svg-inline--fa {
bottom: 0;
left: 0;
margin: auto;
position: absolute;
right: 0;
top: 0; }
.fa-layers {
display: inline-block;
height: 1em;
position: relative;
text-align: center;
vertical-align: -.125em;
width: 1em; }
.fa-layers svg.svg-inline--fa {
-webkit-transform-origin: center center;
transform-origin: center center; }
.fa-layers-text, .fa-layers-counter {
display: inline-block;
position: absolute;
text-align: center; }
.fa-layers-text {
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
-webkit-transform-origin: center center;
transform-origin: center center; }
.fa-layers-counter {
background-color: #ff253a;
border-radius: 1em;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: #fff;
height: 1.5em;
line-height: 1;
max-width: 5em;
min-width: 1.5em;
overflow: hidden;
padding: .25em;
right: 0;
text-overflow: ellipsis;
top: 0;
-webkit-transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: top right;
transform-origin: top right; }
.fa-layers-bottom-right {
bottom: 0;
right: 0;
top: auto;
-webkit-transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: bottom right;
transform-origin: bottom right; }
.fa-layers-bottom-left {
bottom: 0;
left: 0;
right: auto;
top: auto;
-webkit-transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: bottom left;
transform-origin: bottom left; }
.fa-layers-top-right {
right: 0;
top: 0;
-webkit-transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: top right;
transform-origin: top right; }
.fa-layers-top-left {
left: 0;
right: auto;
top: 0;
-webkit-transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: top left;
transform-origin: top left; }
.fa-lg {
font-size: 1.33333em;
line-height: 0.75em;
vertical-align: -.0667em; }
.fa-xs {
font-size: .75em; }
.fa-sm {
font-size: .875em; }
.fa-1x {
font-size: 1em; }
.fa-2x {
font-size: 2em; }
.fa-3x {
font-size: 3em; }
.fa-4x {
font-size: 4em; }
.fa-5x {
font-size: 5em; }
.fa-6x {
font-size: 6em; }
.fa-7x {
font-size: 7em; }
.fa-8x {
font-size: 8em; }
.fa-9x {
font-size: 9em; }
.fa-10x {
font-size: 10em; }
.fa-fw {
text-align: center;
width: 1.25em; }
.fa-ul {
list-style-type: none;
margin-left: 2.5em;
padding-left: 0; }
.fa-ul > li {
position: relative; }
.fa-li {
left: -2em;
position: absolute;
text-align: center;
width: 2em;
line-height: inherit; }
.fa-border {
border: solid 0.08em #eee;
border-radius: .1em;
padding: .2em .25em .15em; }
.fa-pull-left {
float: left; }
.fa-pull-right {
float: right; }
.fa.fa-pull-left,
.fas.fa-pull-left,
.far.fa-pull-left,
.fal.fa-pull-left,
.fab.fa-pull-left {
margin-right: .3em; }
.fa.fa-pull-right,
.fas.fa-pull-right,
.far.fa-pull-right,
.fal.fa-pull-right,
.fab.fa-pull-right {
margin-left: .3em; }
.fa-spin {
-webkit-animation: fa-spin 2s infinite linear;
animation: fa-spin 2s infinite linear; }
.fa-pulse {
-webkit-animation: fa-spin 1s infinite steps(8);
animation: fa-spin 1s infinite steps(8); }
@-webkit-keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg); }
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg); } }
@keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg); }
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg); } }
.fa-rotate-90 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
-webkit-transform: rotate(90deg);
transform: rotate(90deg); }
.fa-rotate-180 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
-webkit-transform: rotate(180deg);
transform: rotate(180deg); }
.fa-rotate-270 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
-webkit-transform: rotate(270deg);
transform: rotate(270deg); }
.fa-flip-horizontal {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
-webkit-transform: scale(-1, 1);
transform: scale(-1, 1); }
.fa-flip-vertical {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
-webkit-transform: scale(1, -1);
transform: scale(1, -1); }
.fa-flip-both, .fa-flip-horizontal.fa-flip-vertical {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
-webkit-transform: scale(-1, -1);
transform: scale(-1, -1); }
:root .fa-rotate-90,
:root .fa-rotate-180,
:root .fa-rotate-270,
:root .fa-flip-horizontal,
:root .fa-flip-vertical,
:root .fa-flip-both {
-webkit-filter: none;
filter: none; }
.fa-stack {
display: inline-block;
height: 2em;
position: relative;
width: 2.5em; }
.fa-stack-1x,
.fa-stack-2x {
bottom: 0;
left: 0;
margin: auto;
position: absolute;
right: 0;
top: 0; }
.svg-inline--fa.fa-stack-1x {
height: 1em;
width: 1.25em; }
.svg-inline--fa.fa-stack-2x {
height: 2em;
width: 2.5em; }
.fa-inverse {
color: #fff; }
.sr-only {
border: 0;
clip: rect(0, 0, 0, 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px; }
.sr-only-focusable:active, .sr-only-focusable:focus {
clip: auto;
height: auto;
margin: 0;
overflow: visible;
position: static;
width: auto; }
.svg-inline--fa .fa-primary {
fill: var(--fa-primary-color, currentColor);
opacity: 1;
opacity: var(--fa-primary-opacity, 1); }
.svg-inline--fa .fa-secondary {
fill: var(--fa-secondary-color, currentColor);
opacity: 0.4;
opacity: var(--fa-secondary-opacity, 0.4); }
.svg-inline--fa.fa-swap-opacity .fa-primary {
opacity: 0.4;
opacity: var(--fa-secondary-opacity, 0.4); }
.svg-inline--fa.fa-swap-opacity .fa-secondary {
opacity: 1;
opacity: var(--fa-primary-opacity, 1); }
.svg-inline--fa mask .fa-primary,
.svg-inline--fa mask .fa-secondary {
fill: black; }
.fad.fa-inverse {
color: #fff; }

File diff suppressed because one or more lines are too long

2172
static/fontawesome/css/v4-shims.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

4466
static/fontawesome/js/all.js Normal file

File diff suppressed because one or more lines are too long

5
static/fontawesome/js/all.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More