Skip to content
This repository has been archived by the owner on Mar 8, 2021. It is now read-only.

Dockerize Osler #180

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7fed870
Build nested dir structure, begin building docker stuff.
justinrporter Sep 22, 2018
dab0f97
working docker
justinrporter Sep 23, 2018
8ca245c
cleanup nginx.conf, dockerfile, makefile, mount static files and medi…
justinrporter Sep 23, 2018
6ead9f4
Use docker secrets.
justinrporter Sep 23, 2018
00f55bf
Use docker secrets, add more logging.
justinrporter Sep 23, 2018
c39fc2d
remove non-root user use in webapp
justinrporter Sep 23, 2018
9cfdff9
Add media and static volumes as docker volumes
justinrporter Sep 24, 2018
083a635
add postgres volume
justinrporter Sep 24, 2018
a781cb1
Revert "Revert "Move to python 3"" (i.e. go back to py3.)
justinrporter Mar 24, 2019
2be84a1
Merge branch 'master' into python3
justinrporter Oct 23, 2019
ffa364e
Remove merge confict indicators.
justinrporter Oct 23, 2019
dd8cad9
fix python 3 stuff
justinrporter Oct 23, 2019
f5dabc9
fix string decoding issues.
justinrporter Oct 23, 2019
fda939f
manually install chrome stable.
justinrporter Oct 29, 2019
4ca35a1
remove google-chrome --version
justinrporter Oct 29, 2019
45257ff
sudo chrome installation
justinrporter Oct 29, 2019
cdd7888
remove chrome changes.
justinrporter Oct 29, 2019
3b98db4
Merge branch 'master' into python3
justinrporter Nov 5, 2019
1f764ec
bump tested python version
justinrporter Nov 5, 2019
fb62eea
switch jessie for buster
justinrporter Nov 5, 2019
7f20756
try stretch on 3.6
justinrporter Nov 5, 2019
d707cc8
try to set w3c compatibility checking to false.
justinrporter Nov 5, 2019
6d0dd1d
Merge branch 'python3' into docker; a few attempts at making docker w…
justinrporter Nov 13, 2019
ac2f094
Fix directory structure
justinrporter Nov 17, 2019
dad47e9
Add root name back into urls.py, move init scripts to python3
justinrporter Nov 17, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: 2
jobs:
build:
docker:
- image: circleci/python:2.7.15-jessie-browsers
- image: circleci/python:3.6.8-stretch-browsers
steps:
- checkout
- run: mkdir test-reports
Expand Down
15 changes: 15 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM python:3.7-buster

RUN apt-get update && apt-get upgrade -y && \
apt-get install -y git

COPY . /home/gunicorn_user/osler
WORKDIR /home/gunicorn_user/osler

RUN pip install --upgrade pip --trusted-host pypi.python.org
RUN pip install --trusted-host pypi.python.org -r requirements-prod.txt

EXPOSE 8000

CMD ["gunicorn", "--chdir", "osler", "--bind", "0.0.0.0:8000", \
"osler.gunicorn_wsgi:application", "--log-file", "-", "--log-level", "debug"]
33 changes: 33 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# makefile

start-dev:
docker-compose up

start-prod:
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up

stop-compose:
@eval docker stop $$(docker ps -a -q)
docker-compose down

ssh-nginx:
docker exec -it nginx_server bash

ssh-django-web:
docker exec -it osler_app bash

ssh-db:
docker exec -it osler_db bash

build:
docker-compose build

rebuild:
docker-compose build --no-cache
docker-compose up

test:
docker-compose run osler_app python osler/manage.py test

cleanup:
docker system prune
51 changes: 51 additions & 0 deletions config/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
user nginx;
worker_processes 1;

pid /var/run/nginx.pid;
error_log /var/log/nginx/error.log warn;

events {
worker_connections 1024; ## Default: 1024, increase if you have lots of clients
}

http {
include /etc/nginx/mime.types;
# fallback in case we can't determine a type
default_type application/octet-stream;

sendfile on;
#tcp_nopush on;

upstream osler_server {
server osler_app:8000;
}

server {

listen 80;
# charset utf-8;
server_name localhost;

location = /favicon.ico { access_log off; log_not_found off; }

location / {
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;

proxy_pass http://osler_server;
proxy_redirect off;
}

location /static/ {
alias /opt/services/osler/static/;
}

location /media/ {
internal;
alias /opt/services/osler/media/;
}
}
}
File renamed without changes.
81 changes: 81 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
version: "3.2"

services:
web:
container_name: osler_app
build:
context: .
dockerfile: Dockerfile
image: osler
stop_signal: SIGINT
volumes:
- static:/home/gunicorn_user/osler/osler/static
- media:/home/gunicorn_user/osler/osler/media
- ./logs:/home/gunicorn_user/logs
networks:
- nginx_network
- database_network
depends_on:
- db
environment:
DJANGO_DEBUG: 0
DJANGO_SETTINGS_MODULE: osler.docker_settings
DJANGO_SENDFILE_BACKEND: sendfile.backends.nginx
DJANGO_SENDFILE_ROOT: /opt/services/osler/media
DJANGO_SENDFILE_URL: /media
DJANGO_SECRET_KEY_FILE: /run/secrets/django_secret_key
DJANGO_DEBUG_LOG_FILE: /home/gunicorn_user/logs/osler.log
DATABASE_PASSWORD_FILE: /run/secrets/database_password
DATABASE_BACKEND: django.db.backends.postgresql_psycopg2
DATABASE_NAME: osler
DATABASE_USER: django
DATABASE_PORT: 5432
DATABASE_HOST: db
secrets:
- database_password
- django_secret_key
db:
container_name: osler_db
image: postgres
restart: always
environment:
POSTGRES_DB: osler
POSTGRES_PASSWORD_FILE: /run/secrets/database_password
POSTGRES_USER: django
networks:
- database_network
secrets:
- database_password
nginx:
image: nginx:latest
container_name: nginx_server
restart: always
ports:
# - "0.0.0.0:80:80"
- 8000:80
volumes:
- db_data:/var/lib/postgresql/data
- ./config/nginx.conf:/etc/nginx/nginx.conf
- static:/opt/services/osler/static/
- media:/opt/services/osler/media/
depends_on:
- web
networks:
- nginx_network

volumes:
static:
media:
db_data:

secrets:
database_password:
file: config/secrets/database_password.txt
django_secret_key:
file: config/secrets/django_secret_key.txt

networks:
nginx_network:
driver: bridge
database_network: # <-- add the bridge
driver: bridge
Binary file removed media/test.jpg
Binary file not shown.
File renamed without changes.
File renamed without changes.
File renamed without changes.
82 changes: 46 additions & 36 deletions api/test.py → osler/api/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ class APITest(APITestCase):
def setUp(self):
workupModels.ClinicType.objects.create(name="Basic Care Clinic")
workupModels.ClinicDate.objects.create(
clinic_type=workupModels.ClinicType.objects.all()[0],
clinic_type=workupModels.ClinicType.objects.first(),
clinic_date=now().date() + datetime.timedelta(days=1))
workupModels.ClinicDate.objects.create(
clinic_type=workupModels.ClinicType.objects.all()[0],
clinic_type=workupModels.ClinicType.objects.first(),
clinic_date=now().date() - datetime.timedelta(days=1))
workupModels.ClinicDate.objects.create(
clinic_type=workupModels.ClinicType.objects.all()[0],
clinic_type=workupModels.ClinicType.objects.first(),
clinic_date=now().date() - datetime.timedelta(days=5))

log_in_provider(self.client, build_provider(["Attending"]))

pt1 = models.Patient.objects.get(pk=1)
Expand All @@ -41,52 +42,52 @@ def setUp(self):
state='BA',
zip_code='63108',
pcp_preferred_zip='63018',
date_of_birth=datetime.date(1990, 01, 01),
date_of_birth=datetime.date(1990, 1, 1),
patient_comfortable_with_english=False,
preferred_contact_method=models.ContactMethod.objects.all()[0],
preferred_contact_method=models.ContactMethod.objects.first(),
)

pt3 = models.Patient.objects.create(
first_name="Asdf",
last_name="Lkjh",
middle_name="Bayer",
phone='+49 178 236 5288',
gender=models.Gender.objects.all()[0],
gender=models.Gender.objects.first(),
address='Schulstrasse 9',
city='Munich',
state='BA',
zip_code='63108',
pcp_preferred_zip='63018',
date_of_birth=datetime.date(1990, 01, 01),
date_of_birth=datetime.date(1990, 1, 1),
patient_comfortable_with_english=False,
preferred_contact_method=models.ContactMethod.objects.all()[0],
preferred_contact_method=models.ContactMethod.objects.first(),
)

pt4 = models.Patient.objects.create(
first_name="No",
last_name="Action",
middle_name="Item",
phone='+12 345 678 9000',
gender=models.Gender.objects.all()[0],
gender=models.Gender.objects.first(),
address='Schulstrasse 9',
city='Munich',
state='BA',
zip_code='63108',
pcp_preferred_zip='63018',
date_of_birth=datetime.date(1990, 01, 01),
date_of_birth=datetime.date(1990, 1, 1),
patient_comfortable_with_english=False,
preferred_contact_method=models.ContactMethod.objects.all()[0],
preferred_contact_method=models.ContactMethod.objects.first(),
)

# Give pt2 a workup one day later.
workupModels.Workup.objects.create(
clinic_day=workupModels.ClinicDate.objects.all()[0], # one day later
clinic_day=workupModels.ClinicDate.objects.first(), # one day later
chief_complaint="SOB",
diagnosis="MI",
HPI="", PMH_PSH="", meds="", allergies="", fam_hx="", soc_hx="",
ros="", pe="", A_and_P="",
author=models.Provider.objects.all()[0],
author_type=models.ProviderType.objects.all()[0],
author=models.Provider.objects.first(),
author_type=models.ProviderType.objects.first(),
patient=pt2)

# Give pt3 a workup one day ago.
Expand All @@ -96,8 +97,8 @@ def setUp(self):
diagnosis="MI",
HPI="", PMH_PSH="", meds="", allergies="", fam_hx="", soc_hx="",
ros="", pe="", A_and_P="",
author=models.Provider.objects.all()[0],
author_type=models.ProviderType.objects.all()[0],
author=models.Provider.objects.first(),
author_type=models.ProviderType.objects.first(),
patient=pt3)


Expand All @@ -108,38 +109,38 @@ def setUp(self):
diagnosis="MI",
HPI="", PMH_PSH="", meds="", allergies="", fam_hx="", soc_hx="",
ros="", pe="", A_and_P="",
author=models.Provider.objects.all()[0],
author_type=models.ProviderType.objects.all()[0],
author=models.Provider.objects.first(),
author_type=models.ProviderType.objects.first(),
patient=pt1,
signer=models.Provider.objects.all().filter(
clinical_roles=models.ProviderType.objects.all().filter(
short_name="Attending")[0])[0])

# make pt1 have and AI due tomorrow
pt1_ai = models.ActionItem.objects.create(
author=models.Provider.objects.all()[0],
author_type=models.ProviderType.objects.all()[0],
instruction=models.ActionInstruction.objects.all()[0],
author=models.Provider.objects.first(),
author_type=models.ProviderType.objects.first(),
instruction=models.ActionInstruction.objects.first(),
due_date=now().date()+datetime.timedelta(days=1),
comments="",
priority=True,
patient=pt1)

# make pt2 have an AI due yesterday
pt2_ai = models.ActionItem.objects.create(
author=models.Provider.objects.all()[0],
author_type=models.ProviderType.objects.all()[0],
instruction=models.ActionInstruction.objects.all()[0],
author=models.Provider.objects.first(),
author_type=models.ProviderType.objects.first(),
instruction=models.ActionInstruction.objects.first(),
due_date=now().date()-datetime.timedelta(days=1),
comments="",
priority=True,
patient=pt2)

# make pt3 have an AI that during the test will be marked done
pt3_ai = models.ActionItem.objects.create(
author=models.Provider.objects.all()[0],
author_type=models.ProviderType.objects.all()[0],
instruction=models.ActionInstruction.objects.all()[0],
author=models.Provider.objects.first(),
author_type=models.ProviderType.objects.first(),
instruction=models.ActionInstruction.objects.first(),
due_date=now().date()-datetime.timedelta(days=15),
comments="",
patient=pt3)
Expand All @@ -149,16 +150,19 @@ def test_api_list_patients_by_last_name(self):
url = reverse("pt_list_api") # Way to have this here and not repeat this line? Reverse is called in every test now

# Test last_name ordering
data = {'sort':'last_name'}
data = {'sort': 'last_name'}
response = self.client.get(reverse("pt_list_api"), data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertLessEqual(response.data[0]['last_name'],response.data[1]['last_name'])
self.assertLessEqual(response.data[1]['last_name'],response.data[2]['last_name'])
self.assertLessEqual(response.data[2]['last_name'],response.data[3]['last_name'])
self.assertLessEqual(response.data[0]['last_name'],
response.data[1]['last_name'])
self.assertLessEqual(response.data[1]['last_name'],
response.data[2]['last_name'])
self.assertLessEqual(response.data[2]['last_name'],
response.data[3]['last_name'])

def test_api_list_patients_by_latest_activity(self):
# Test workup/intake ordering.
data = {'sort':'latest_workup'}
data = {'sort': 'latest_workup'}
response = self.client.get(reverse("pt_list_api"), data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertNotEqual(response.data[0]['latest_workup'], None) # pt2, workup date now()+1day
Expand All @@ -167,9 +171,15 @@ def test_api_list_patients_by_latest_activity(self):
self.assertNotEqual(response.data[3]['latest_workup'], None) # pt1, workup date now()-5days

# Check that dates are correcly sorted
self.assertGreaterEqual(response.data[0]['latest_workup']['clinic_day']['clinic_date'],response.data[1]['history']['last']['history_date'])
self.assertGreaterEqual(response.data[1]['history']['last']['history_date'],response.data[2]['latest_workup']['clinic_day']['clinic_date'])
self.assertGreaterEqual(response.data[2]['latest_workup']['clinic_day']['clinic_date'],response.data[3]['latest_workup']['clinic_day']['clinic_date'])
self.assertGreaterEqual(
response.data[0]['latest_workup']['clinic_day']['clinic_date'],
response.data[1]['history']['last']['history_date'])
self.assertGreaterEqual(
response.data[1]['history']['last']['history_date'],
response.data[2]['latest_workup']['clinic_day']['clinic_date'])
self.assertGreaterEqual(
response.data[2]['latest_workup']['clinic_day']['clinic_date'],
response.data[3]['latest_workup']['clinic_day']['clinic_date'])

def test_api_list_patients_with_unsigned_workup(self):
# Test for unsigned_workup
Expand Down Expand Up @@ -225,7 +235,7 @@ def test_api_list_patients_with_inactive_action_item(self):
self.assertEqual(len(response.data), 1)
self.assertEqual(response.data[0]['id'], pt1.id)

pt3_ai.mark_done(models.Provider.objects.all()[0])
pt3_ai.mark_done(models.Provider.objects.first())
pt3_ai.save()

# Test now only has pt2
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion appointment/models.py → osler/appointment/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class Meta:

history = HistoricalRecords()

def __unicode__(self):
def __str__(self):
return "Appointment ({type}) for {name} on {date}".format(
type=self.verbose_appointment_type(),
name=self.patient.name(),
Expand Down
Loading