Initial commit

This commit is contained in:
2020-11-13 15:31:14 +01:00
parent 2af942da5a
commit 10e2a34143
103 changed files with 3609 additions and 0 deletions

View File

@@ -0,0 +1 @@
default_app_config = 'apps.synthea.apps.SyntheaConfig'

View File

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

View File

@@ -0,0 +1,10 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
class SyntheaConfig(AppConfig):
name = 'apps.synthea'
label = 'synthea'
verbose_name = _('Synthea')
verbose_name_plural = _('Synthea')

View File

@@ -0,0 +1,27 @@
from django.forms import ModelForm
from django import forms
from apps.synthea.models import Synthea
from .lib.utils import available_states, available_modules
class SyntheaForm(ModelForm):
class Meta:
model = Synthea
fields = ['state', 'population', 'gender', 'age', 'module']
# This is loaded only once during startup. So changing the state data will not be picked up after a restart
state_options = [('','Any')]
for item in available_states():
state_options.append((item,item))
module_options = [('','Any')]
for item in available_modules():
module_options.append((item['module'],item['name']))
widgets = {
'state': forms.Select(choices=state_options),
'gender': forms.Select(choices=[('','Any'),('m','Male'),('f','Female')]),
'module': forms.Select(choices=module_options)
}

View File

@@ -0,0 +1,83 @@
from pathlib import Path
import pandas as pd
import shlex
import subprocess
from zipfile import ZipFile
import json
def available_states():
#TODO: Make a setting for this path
location = Path('/opt/development/synthea_webservice/synthea/src/main/resources/geography/')
df = pd.read_csv(location / 'timezones.csv', index_col=False)
# The state information is expected in the first column
states = df[df.columns[0]].to_list()
states.sort()
return states
def available_modules():
#TODO: Make a setting for this path
location = Path('/opt/development/synthea_webservice/synthea/src/main/resources/modules/')
# Assumption here: A folder is a single module. And all .json in the main modules folder is a module.
modules = []
for module in location.iterdir():
if module.is_file() and module.suffix == '.json':
data = json.loads(module.read_text())
modules.append({'module' : module.name.replace('.json',''), 'name' : data['name']})
modules = sorted(modules, key=lambda k: k['name'].lower())
return modules
def run_synthea(state = None, population = None, gender = None, age = None, module = None):
# TODO: Make synthea setting(s)
location = '/opt/development/synthea_webservice/synthea/'
synthea_cmd = ['/opt/development/synthea_webservice/synthea/run_synthea']
zip_file = 'Synthea_'
zip_export = location
if population:
synthea_cmd.append('-p')
synthea_cmd.append(str(population))
zip_file += f'population_{population}_'
if gender:
synthea_cmd.append('-g')
synthea_cmd.append(gender.upper())
zip_file += f'gender_{gender}_'
if age:
synthea_cmd.append('-a')
synthea_cmd.append(age)
zip_file += f'age_{age}_'
if module:
synthea_cmd.append('-m')
synthea_cmd.append(module)
zip_file += f'module_{module}_'
if state:
synthea_cmd.append(state)
zip_file += f'state_{state}'
process_ok = False
log = ''
with subprocess.Popen(synthea_cmd,cwd=location, stdout=subprocess.PIPE,stderr=subprocess.PIPE) as process:
for line in process.stdout:
line = line.decode('utf8')
log += line
if not process_ok:
process_ok = line.find('BUILD SUCCESSFUL') >= 0
if process_ok:
with ZipFile(f'{zip_export}/{zip_file}.zip', 'w') as export:
for file in Path(location + 'output/fhir_stu3').iterdir():
export.write(file,file.name)
return Path(f'{zip_export}/{zip_file}.zip')
else:
raise Exception(log)

View File

@@ -0,0 +1,32 @@
# Generated by Django 3.1.3 on 2020-11-13 09:36
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Synthea',
fields=[
('created_at', models.DateTimeField(auto_now_add=True, help_text='The date and time this model has been created', verbose_name='Date created')),
('updated_at', models.DateTimeField(auto_now=True, help_text='The date and time this model has been updated', verbose_name='Date updated')),
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='A unique id', primary_key=True, serialize=False, unique=True, verbose_name='ID')),
('state', models.CharField(help_text='The state for which synthea generate data.', max_length=200, verbose_name='Stage')),
('population', models.PositiveSmallIntegerField(default=50, help_text='The size of the population', verbose_name='Population')),
('gender', models.CharField(help_text='Select the gender type', max_length=1, verbose_name='Gender')),
('age', models.CharField(help_text='Select the age range', max_length=10, verbose_name='Age range')),
('module', models.CharField(help_text='Select the module', max_length=50, verbose_name='Mopdule')),
],
options={
'verbose_name': 'token',
'verbose_name_plural': 'tokens',
},
),
]

View File

@@ -0,0 +1,24 @@
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import gettext_lazy as _
from django_cryptography.fields import encrypt
from lib.utils.general import get_random_string
from lib.models.base import MetaDataModel
import uuid
# Create your models here.
class Synthea(MetaDataModel):
class Meta:
verbose_name = _('token')
verbose_name_plural = _('tokens')
id = models.UUIDField(_('ID'), primary_key=True, unique=True, default=uuid.uuid4, editable=False, help_text=_('A unique id'))
state = models.CharField(_('State'), max_length=200, help_text=_('The state for which synthea generate data.'))
population = models.PositiveSmallIntegerField(_('Population'), blank=True, default=50, help_text=_('The size of the population'))
gender = models.CharField(_('Gender'), blank=True,max_length=1, help_text=_('Select the gender type'))
age = models.CharField(_('Age range'), blank=True,max_length=10, help_text=_('Select the age range'))
module = models.CharField(_('Module'),blank=True, max_length=50, help_text=_('Select the module'))

View File

@@ -0,0 +1,21 @@
{% extends 'base.html' %} <!-- Add this for inheritance -->
{% load i18n %}
{% block title %}{% trans "Generate a new Synthea data set" %}{% endblock %}
{% block pagetitle %}{% trans "Generate a new Synthea data set" %}{% endblock %}
{% block menu %}
{% include 'synthea/menu.html' %}
{% endblock %}
{% block content %}
<h1>Synthea generartor</h1>
<p>Enter the form fields and press submit. <small>U vraagt, wij draaien ;)</small></p>
<form method="POST" class="post-form">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<button type="submit" class="save btn btn-default">Save</button>
</form>
{% endblock %}

View File

@@ -0,0 +1,14 @@
{% extends 'base.html' %} <!-- Add this for inheritance -->
{% load i18n %}
{% block title %}{% trans "dHealth Synthea" %}{% endblock %}
{% block pagetitle %}{% trans "dHealth Synthea" %}{% endblock %}
{% block menu %}
{% include 'synthea/menu.html' %}
{% endblock %}
{% block content %}
<h1>dHealt Nederland</h1>
<p>Onder leiding van UMCG</p>
{% endblock %}

View File

@@ -0,0 +1,17 @@
{% load i18n %}
<li class="rug-nav--secondary__item">
<a class="rug-nav--secondary__link js--togglable-switch" data-toggle-class="rug-nav--secondary__link--selected" data-toggle-group="submenu" data-toggle-id="menu-2427370b-9435-44d9-bca7-b93ec9d03cc0-33.31" data-toggle-mode="togglable">{% trans "Menu" %}</a>
<ul class="rug-nav--secondary__sub rug-nav--secondary__sub--hidden js--togglable-item" data-toggle-class="rug-block" data-toggle-group="submenu" data-toggle-id="menu-2427370b-9435-44d9-bca7-b93ec9d03cc0-33.31">
<li class="rug-nav--secondary__sub__item" data-menu-id="b512aa55-f0cb-4588-9054-302caa5fa951-33.34">
<a class="rug-nav--secondary__sub__link" href="{% url 'index' %}"><span class="rug-nav--secondary__sub__link-text">{% trans "Informatie" %}</span></a>
</li>
<li class="rug-nav--secondary__sub__item" data-menu-id="b512aa55-f0cb-4588-9054-302caa5fa951-33.35">
<a class="rug-nav--secondary__sub__link" href="{% url 'generator_form' %}"><span class="rug-nav--secondary__sub__link-text">{% trans "Generator" %}</span></a>
</li>
<li class="rug-nav--secondary__sub__item" data-menu-id="b512aa55-f0cb-4588-9054-302caa5fa951-33.36">
<a class="rug-nav--secondary__sub__link" href="{% url 'api_info' %}"><span class="rug-nav--secondary__sub__link-text">{% trans "API" %}</span></a>
</li>
</ul>
</li>

View File

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

View File

@@ -0,0 +1,9 @@
from django.urls import path, include
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('api/', views.index, name='api_info'),
path('generate/', views.show_synthea_form, name='generator_form'),
]

View File

@@ -0,0 +1,49 @@
from django.shortcuts import render
from django.http import HttpResponse
from apps.synthea.forms import SyntheaForm
from .lib.utils import run_synthea
import mimetypes
# Create your views here.
def index(request):
template_name = 'synthea/index.html'
return render(request,template_name,{})
def show_synthea_form(request):
template_name = 'synthea/generator_form.html'
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = SyntheaForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
try:
zipfile = run_synthea(
form.cleaned_data['state'],
form.cleaned_data['population'],
form.cleaned_data['gender'],
form.cleaned_data['age'],
form.cleaned_data['module']
)
mime_type, _ = mimetypes.guess_type(zipfile)
response = HttpResponse(zipfile.open('rb'), content_type=mime_type)
response['Content-Disposition'] = f'attachment; filename={zipfile.name}'
return response
except Exception as ex:
print(ex)
# if a GET (or any other method) we'll create a blank form
else:
form = SyntheaForm()
return render(request,template_name,{
'form':form
})