Commit 8a33f2a7 authored by Dan-Schaefer's avatar Dan-Schaefer

Uploaded code Max Hirsch Bachelorabeit TEDIAS Use-Case 2 (UC2) Dashboard

parent 956dd335
FLASK_ENV=development
FLASK_APP=api
\ No newline at end of file
/unprocessed_files
/processed_files
/deleted_files/
FROM tiangolo/uwsgi-nginx-flask:python3.8
ENV UWSGI_INI /app/uwsgi.ini
WORKDIR /app
COPY . .
COPY ./requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
WORKDIR /app/api
ENV LISTEN_PORT 8069
EXPOSE 8069
\ No newline at end of file
FLASK_ENV=development
FLASK_APP=api
\ No newline at end of file
{"1.1": {"text": "Schwindel", "flag": ""}, "1.2": {"text": "Kopfschmerzen", "flag": ""}, "1.3": {"text": "Schwäche/Lähmung", "flag": ""}, "1.4": {"text": "Missempfindung (Kribbeln)/Taubheit der Haut", "flag": ""}, "1.5": {"text": "Sprach-/Sprechstörung", "flag": ""}, "1.6": {"text": "Sehstörung", "flag": ""}, "1.7": {"text": "Krampfanfall", "flag": ""}, "1.8": {"text": "Andere Beschwerden", "flag": ""}, "2.1": {"text": "Seit 4 Stunden oder kürzer", "flag": "red"}, "2.2": {"text": "Zwischen 4 und 24 Stunden", "flag": ""}, "2.3": {"text": "Innerhalb der letzten 6 Tage oder länger mit kürzlicher Verschlimmerung", "flag": ""}, "2.4": {"text": "Länger als 6 Tage ohne kürzliche Verschlimmerung", "flag": "green"}, "3.1": {"text": "ja", "flag": ""}, "3.2": {"text": "nein", "flag": ""}, "4.1": {"text": "ja", "flag": ""}, "4.2": {"text": "nein", "flag": ""}, "5.1": {"text": "ja", "flag": "green"}, "5.2": {"text": "nein", "flag": ""}, "6.1": {"text": "ja", "flag": "red"}, "6.2": {"text": "nein", "flag": "green"}, "7.1": {"text": "Doppelbilder", "flag": ""}, "7.2": {"text": "Probleme beim Sitzen, Stehen oder Gehen", "flag": ""}, "7.3": {"text": "Schluckstörung", "flag": ""}, "7.4": {"text": "Sprachstörung (undeutliches „verwaschenes“ Sprechen)", "flag": ""}, "7.5": {"text": "einseitige Schwäche/Lähmung oder Missempfindung", "flag": ""}, "7.6": {"text": "einseitige Koordinationsstörung (z. B. vermehrte Ungeschicklichkeit einer Hand)", "flag": ""}, "7.7": {"text": "Brustenge/-schmerzen", "flag": ""}, "7.8": {"text": "Luftnot", "flag": ""}, "8.1": {"text": "Nebeneinanderstehend", "flag": ""}, "8.2": {"text": "Übereinanderstehend", "flag": ""}, "8.3": {"text": "Anders", "flag": ""}, "9.1": {"text": "Beim Sitzen", "flag": ""}, "9.2": {"text": "Beim Gehen", "flag": ""}, "9.3": {"text": "Beim Stehen", "flag": ""}, "9.4": {"text": "Anders", "flag": ""}, "10.1": {"text": "Seit 4 Stunden oder kürzer", "flag": ""}, "10.2": {"text": "Zwischen 4 und 24 Stunden", "flag": ""}, "10.3": {"text": "Innerhalb der letzten 6 Tage oder länger mit kürzlicher Verschlimmerung", "flag": ""}, "10.4": {"text": "Länger ohne kürzliche Verschlimmerung", "flag": ""}, "11.1": {"text": "ja", "flag": "red"}, "11.2": {"text": "nein", "flag": ""}, "12.1": {"text": "Hinterkopf", "flag": ""}, "12.2": {"text": "Seite/Schläfe", "flag": ""}, "12.3": {"text": "Stirn/Augen/Gesicht", "flag": ""}, "12.4": {"text": "Gesamter Kopf", "flag": ""}, "13.1": {"text": "0", "flag": ""}, "13.2": {"text": "1", "flag": ""}, "13.3": {"text": "2", "flag": ""}, "13.4": {"text": "3", "flag": ""}, "13.5": {"text": "4", "flag": ""}, "13.6": {"text": "5", "flag": ""}, "13.7": {"text": "6", "flag": ""}, "13.8": {"text": "7", "flag": "red"}, "13.9": {"text": "8", "flag": "red"}, "13.10": {"text": "9", "flag": "red"}, "13.11": {"text": "10", "flag": "red"}, "14.1": {"text": "ja", "flag": ""}, "14.2": {"text": "nein", "flag": "green"}, "15.1": {"text": "Ja, gemessen und über 38°", "flag": "red"}, "15.2": {"text": "Ja, gemessen und unter 38°", "flag": ""}, "15.3": {"text": "Nein, gemessen und kein Fieber", "flag": ""}, "15.4": {"text": "Nein, aber auch nicht gemessen", "flag": ""}, "15.5": {"text": "Unsicher, nicht gemessen aber fühle mich fiebrig", "flag": ""}, "16.1": {"text": "ja", "flag": ""}, "16.2": {"text": "nein", "flag": ""}, "17.1": {"text": "ja", "flag": "red"}, "17.2": {"text": "nein", "flag": ""}, "18.1": {"text": "ja", "flag": "red"}, "18.2": {"text": "nein", "flag": ""}, "19.1": {"text": "Übelkeit/Erbrechen", "flag": ""}, "19.2": {"text": "Lichtscheu", "flag": ""}, "19.3": {"text": "Schmerzen an einer Hals- oder Nackenseite", "flag": "red"}, "19.4": {"text": "Sprachstörung (z. B. undeutliches „verwaschenes“ Sprechen)", "flag": "red"}, "19.5": {"text": "einseitige Schwäche/Lähmung oder Missempfindung", "flag": "red"}, "19.6": {"text": "begleitende Schmerzen im Bereich eines Auges", "flag": ""}, "19.7": {"text": "Tränendes Auge oder verstopfte Nase", "flag": ""}, "19.8": {"text": "Sehstörung (z. B. Doppelbilder)", "flag": ""}, "20.1": {"text": "Seit 4 Stunden oder kürzer", "flag": "red"}, "20.2": {"text": "Zwischen 4 und 24 Stunden", "flag": ""}, "20.3": {"text": "Innerhalb der letzten 6 Tage", "flag": ""}, "20.4": {"text": "oder länger mit kürzlicher Verschlimmerung", "flag": ""}, "20.5": {"text": "Zwischen 1 und 2 Wochen ohne Verschlimmerung", "flag": ""}, "20.6": {"text": "Länger als 14 Tage ohne Verschlimmerung", "flag": "green"}, "21.1": {"text": "Schwäche/Lähmung", "flag": ""}, "21.2": {"text": "Taubheit", "flag": ""}, "21.3": {"text": "Kribbeln", "flag": ""}, "22.1": {"text": "Kopf/Gesicht (einseitig)", "flag": "red"}, "22.2": {"text": "Kopf/Gesicht (beidseitig)", "flag": ""}, "22.3": {"text": "eine Körperhälfte (rechts)", "flag": "red"}, "22.4": {"text": "eine Körperhälfte (links)", "flag": "red"}, "22.5": {"text": "eine Gesichtshälfte & eine Köperhälfte", "flag": "red"}, "22.6": {"text": "beide Beine", "flag": "red"}, "22.7": {"text": "gesamter Körper ohne Seitenbetonung", "flag": ""}, "23.1": {"text": "Acetylsalicylsäure (ASS)", "flag": ""}, "23.2": {"text": "Marcumar", "flag": ""}, "23.3": {"text": "Apixaban (Eliquis)", "flag": ""}, "23.4": {"text": "Dabigatran (Pradaxa)", "flag": ""}, "23.5": {"text": "Edoxaban (Lixiana)", "flag": ""}, "23.6": {"text": "Rivaroxaban (Xarelto)", "flag": ""}, "24.1": {"text": "Seit 4 Stunden oder kürzer", "flag": "red"}, "24.2": {"text": "Zwischen 4 und 24 Stunden", "flag": ""}, "24.3": {"text": "Innerhalb der letzten 6 Tage oder länger mit kürzlicher Verschlimmerung", "flag": ""}, "24.4": {"text": "Länger ohne kürzliche Verschlimmerung", "flag": ""}, "25.1": {"text": "Ich kann/konnte nur undeutlich sprechen, mein Gesprächspartner versteht/verstand jedoch den Inhalt des Gesagten.", "flag": ""}, "25.2": {"text": "Ich kann/konnte unverständlich/nicht verständlich sprechen („Wortsalat“), mein Gesprächspartner hat/te Schwierigkeiten, den Inhalt des Gesagten zu verstehen.", "flag": ""}, "25.3": {"text": "Das Sprechen fällt/fiel mir schwer, ich muss nach Worten suchen oder habe Probleme, die Wörter herauszubringen.", "flag": ""}, "26.1": {"text": "Einseitige Lähmung/Schwäche", "flag": "red"}, "26.2": {"text": "Sehstörung", "flag": ""}, "26.3": {"text": "Starke Kopfschmerzen", "flag": ""}, "27.1": {"text": "Acetylsalicylsäure (ASS)", "flag": ""}, "27.2": {"text": "Marcumar", "flag": ""}, "27.3": {"text": "Apixaban (Eliquis)", "flag": ""}, "27.4": {"text": "Dabigatran (Pradaxa)", "flag": ""}, "27.5": {"text": "Edoxaban (Lixiana)", "flag": ""}, "27.6": {"text": "Rivaroxaban (Xarelto)", "flag": ""}, "28.1": {"text": "Seit 4 Stunden oder kürzer", "flag": "red"}, "28.2": {"text": "Zwischen 4 und 24 Stunden", "flag": ""}, "28.3": {"text": "Innerhalb der letzten 6 Tage oder länger mit kürzlicher Verschlimmerung", "flag": ""}, "28.4": {"text": "Länger ohne kürzliche Verschlimmerung", "flag": ""}, "29.1": {"text": "Ich kann/konnte plötzlich auf einem Auge nichts mehr oder viel schlechter sehen.", "flag": "red"}, "29.2": {"text": "Ich habe Flackern/Lichtzacken wahrgenommen, die gewandert sind.", "flag": ""}, "29.3": {"text": "Ich sehe doppelt bzw. habe doppelt gesehen (d. h. nebeneinander oder übereinander stehende Bilder).", "flag": "red"}, "30.1": {"text": "Schmerzen am oder ums Auge", "flag": ""}, "30.2": {"text": "Schwäche/Lähmung", "flag": "red"}, "30.3": {"text": "Missempfindung", "flag": ""}, "30.4": {"text": "Sprach-/Sprechstörung", "flag": ""}, "30.5": {"text": "Häufiges Verschlucken", "flag": ""}, "30.6": {"text": "Vermehrte Erschöpfbarkeit/körperliche Schwäche", "flag": ""}, "30.7": {"text": "Kopfschmerzen, v. a. im Bereich der Schläfe", "flag": ""}, "31.1": {"text": "Acetylsalicylsäure (ASS)", "flag": ""}, "31.2": {"text": "Marcumar", "flag": ""}, "31.3": {"text": "Apixaban (Eliquis)", "flag": ""}, "31.4": {"text": "Dabigatran (Pradaxa)", "flag": ""}, "31.5": {"text": "Edoxaban (Lixiana)", "flag": ""}, "31.6": {"text": "Rivaroxaban (Xarelto)", "flag": ""}, "32.1": {"text": "ja", "flag": ""}, "32.2": {"text": "nein", "flag": "green"}, "33.1": {"text": "Rezept für Antiepileptikum", "flag": "green"}, "33.2": {"text": "Anderes", "flag": ""}, "34.1": {"text": "#Freitext", "flag": ""}, "35.1": {"text": "Innerhalb der letzten 12 Stunden", "flag": ""}, "35.2": {"text": "Vor 12 bis 24 Stunden", "flag": ""}, "35.3": {"text": "Vor mehr als 24 Stunden", "flag": ""}, "36.1": {"text": "ja", "flag": ""}, "36.2": {"text": "nein", "flag": ""}, "37.1": {"text": "2", "flag": ""}, "37.2": {"text": "3", "flag": ""}, "37.3": {"text": "Mehr als 3", "flag": ""}, "38.1": {"text": "ja", "flag": ""}, "38.2": {"text": "nein", "flag": ""}, "39.1": {"text": "ja", "flag": ""}, "39.2": {"text": "nein", "flag": ""}, "40.1": {"text": "ja", "flag": ""}, "40.2": {"text": "nein", "flag": ""}, "41.1": {"text": "Schmerzen", "flag": ""}, "41.2": {"text": "Konzentrationsstörung oder Wahrnehmungsstörung", "flag": ""}, "41.3": {"text": "unwillkürliche Bewegungen", "flag": ""}, "41.4": {"text": "andere Bewegungsstörung", "flag": ""}, "41.5": {"text": "neu aufgetretene Vergesslichkeit", "flag": ""}, "41.6": {"text": "Andere (Freitexteingabe)", "flag": ""}, "42.1": {"text": "#Freitext", "flag": ""}, "43.1": {"text": "Seit 4 Stunden oder kürzer", "flag": "red"}, "43.2": {"text": "Zwischen 4 und 24 Stunden", "flag": ""}, "43.3": {"text": "Innerhalb der letzten 6 Tage oder länger mit kürzlicher Verschlimmerung", "flag": ""}, "43.4": {"text": "Zwischen 1 und 2 Wochen ohne Verschlimmerung", "flag": ""}, "43.5": {"text": "Länger als 14 Tage ohne Verschlimmerung", "flag": "green"}, "44.1": {"text": "ja", "flag": ""}, "44.2": {"text": "nein", "flag": ""}, "45.1": {"text": "#Freitext", "flag": ""}, "46.1": {"text": "Ja, innerhalb der letzten 10 Tage", "flag": ""}, "46.2": {"text": "Ja, vor längerer Zeit", "flag": ""}, "46.3": {"text": "Nein", "flag": ""}, "47.1": {"text": "ja", "flag": ""}, "47.2": {"text": "nein", "flag": ""}, "48.1": {"text": "Multiple Sklerose", "flag": ""}, "48.2": {"text": "Epilepsie", "flag": ""}, "48.3": {"text": "Parkinson-Erkrankung", "flag": ""}, "48.4": {"text": "Schlaganfall in der Vorgeschichte", "flag": ""}, "48.5": {"text": "Andere (Freitexteingabe)", "flag": ""}, "49.1": {"text": "ja", "flag": ""}, "49.2": {"text": "nein", "flag": ""} }
\ No newline at end of file
import json
# style guide: you should not import explicit functions.
# Import the whole package so that in code it is clearer from which package the function comes.
# In that case:
# import flask
# and then below app = flask.Flask(__name__)
# In that case it is clear but for other functions that could be misleading
import threading
import requests
from flask import Flask, jsonify, request
from genson import SchemaBuilder
import web.webt as web
from flask_cors import CORS
import json
import pathlib
import os
import time
# Import custom python code
import logic
import schemas
from functools import wraps
from werkzeug.security import generate_password_hash, check_password_hash
import jwt
from datetime import datetime
app = Flask(__name__)
app.secret_key = "secret" # os.urandom(12)
CORS(app)
# base_route=''
base_route = '/tedias/UC2Dashboard/api/v1'
service_description = 'This is the UC2 dashboard.'
service_logs = ['initial log\r\n']
service_api_key = 'super secret secret'
service_name = 'UC2Dashboard'
service_diagnostics = {
"uptime": "Service is up for 1 second."
}
middleware_base_address = 'http://localhost:8765/tedias/api/middleware/v1'
service_uid = ''
def authenticated(username: str, password_entered: str) -> bool:
with open("../users") as file:
correct_password = json.load(file)[username]
return check_password_hash(correct_password, password_entered)
@app.route("/login", methods=["POST"])
def login():
data = request.get_json()
with open("../users") as file:
usernames = list(json.load(file).keys())
if not data or "username" not in data or "password" not in data or \
data["username"] not in usernames or \
not authenticated(data["username"], data["password"]):
return {"message": "Invalid credentials", "authenticated": False}, 401
token = jwt.encode({
'sub': data["username"],
'iat': datetime.utcnow(),
#'exp': datetime.utcnow() + timedelta(minutes=180)
}, app.secret_key, algorithm="HS256")
return jsonify({'token': token})# .decode("UTF-8")})
def token_required(f):
@wraps(f)
def _verify(*args, **kwargs):
auth_headers = request.headers.get("Authorization", "").split()
invalid_message = {
"message": "Invalid token. Authentication required",
"authenticated": False
}
if len(auth_headers) != 2:
return invalid_message, 401
try:
token = auth_headers[1]
data = jwt.decode(token, app.secret_key, algorithms=["HS256"])
with open("../users") as file:
userlist =list(json.load(file).keys())
if data["sub"] not in userlist:
# logging message would be nice, but logging is not set up
return invalid_message, 401
return f(*args, **kwargs)
except jwt.InvalidTokenError as error:
# logging message would be nice, but logging is not set up
return invalid_message, 401
return _verify
@app.route('/')
def index():
return 'Web App with Python Flask!'
# Necessary endpoints for middleware
def append_log(msg):
service_logs.append(msg + '\r\n')
@app.route(base_route + '/description', endpoint='get_description', methods=['GET'])
def get_description():
global service_description
return jsonify(service_description)
@app.route(base_route + '/diagnostics', endpoint='get_diagnostics', methods=['GET'])
def get_diagnostics():
global service_diagnostics
return jsonify(service_diagnostics)
@app.route(base_route + '/logs', endpoint='get_logs', methods=['GET'])
def get_logs():
global service_logs
return jsonify(service_logs)
# Custom endpoints
@app.route(base_route + '/incoming-data', endpoint='incoming_data', methods=['POST'])
def incoming_data():
# Create Class to compare with questionnaire map
questionnaire_logic = logic.QuestionnaireFlagCheck(questionnaire_map
="../questionnaire_templates/Processing_Template_Neuro.json")
# get questionnaire response from server
# if inputs is None:
questionnaire_response_dict = request.get_json()
questionnaire_id = questionnaire_response_dict['id']
# Write received questionnaire to file
try:
os.makedirs("../unprocessed_files")
except FileExistsError:
pass
with open('../unprocessed_files/{0}.json'.format(questionnaire_id), 'w') as outfile:
outfile.write(json.dumps(questionnaire_response_dict))
# Process received questionnaire
try:
questionnaire_response_processed_dict = questionnaire_logic.process(path='../unprocessed_files/{0}.json'
.format(questionnaire_id))
except KeyError:
raise KeyError("Received questionnaire does not match expected form. "
"Please compare questionnaire response with the loaded template. ")
# Print result to file if
try:
os.makedirs("../processed_files")
except FileExistsError:
pass
if 'questionnaireresponseid' in questionnaire_response_processed_dict:
questionnaire_response_processed_str = json.dumps(questionnaire_response_processed_dict)
with open('../processed_files/{0}.json'.format(questionnaire_id), 'w') as outfile:
outfile.write(questionnaire_response_processed_str)
return questionnaire_response_processed_str
# SHOULD BE GET AND NOT POST
@app.route(base_route + '/return-data', endpoint='return_data', methods=['POST'])
def return_data():
with open('../processed_files/{0}.json'.format(request.get_json()['questionnaire_id']), 'rb') as infile:
data_dict = json.load(infile)
return data_dict
@app.route(base_route + '/dashboard', endpoint='dashboard', methods=['GET'])
def dashboard():
patient_list = []
path = '../processed_files/'
path_list = pathlib.Path(path).glob('**/*.json')
for path in path_list:
patient_list.append(logic.load_questionnaire(path=path))
return web.build_html(patient_list)
# SHOULD BE A DELETE NOT A POST
@app.route(base_route + '/remove-questionnaire', endpoint='remove_questionnaire', methods=['POST'])
def remove_questionnaire():
questionnaire_id = request.get_json()['questionnaire_id']
questionnaire_raw_path = '{0}/{1}.json'.format('../processed_files/', questionnaire_id)
questionnaire_processed_path = '{0}/{1}.json'.format('../unprocessed_files/', questionnaire_id)
if os.path.exists(questionnaire_raw_path) and os.path.exists(questionnaire_processed_path):
os.remove(questionnaire_raw_path)
os.remove(questionnaire_processed_path)
return '', 204
elif os.path.exists(questionnaire_raw_path):
return 'Questionnaire has not been processed yet', 500
if os.path.exists(questionnaire_processed_path):
os.remove(questionnaire_processed_path)
return '', 204
else:
return 'File not found', 404
@app.route(base_route + '/remove-all', endpoint='remove_all', methods=['DELETE'])
def remove_all():
for folder in ['../processed_files/', '../unprocessed_files/']:
path_list = pathlib.Path(folder).glob('**/*.json')
for path in path_list:
os.remove(path)
return 'all .json files deleted', 204
@app.route("/ampel", endpoint='get_patient_list', methods=["GET"])
@token_required
def get_patient_list():
patient_list = []
path = '../processed_files/'
path_list = pathlib.Path(path).glob('**/*.json')
for path in path_list:
patient = logic.load_questionnaire(path=path)
patient["color"] = patient["Patient_Priority"]
del patient["Patient_Priority"]
patient["id"] = patient["questionnaireresponseid"]
del patient["questionnaireresponseid"]
patient["waiting_time"] = int((time.time() - patient["entrance"]) / 60)
del patient["entrance"]
patient["primary_concern"] = patient["prios"][0]["answer"][0]["valueString"]
patient["answersInText"] = []
for questionAndAnswers in patient["prios"][1:]:
answers = map(lambda x: x["valueString"], questionAndAnswers["answer"])
patient["answersInText"].append(questionAndAnswers["text"] + " " + "; ".join(answers))
del patient["prios"]
patient_list.append(patient)
return jsonify(patient_list)
@app.route("/ampel", endpoint='delete_patient', methods=["POST"])
@token_required
def delete_patient():
os.system("touch marker")
# If necessary create the folders "deleted_files/unprocessed_files" and "deleted_files/processed_files"
try:
os.makedirs("..deleted_files/unprocessed_files")
except FileExistsError:
pass
try:
os.makedirs("..deleted_files/processed_files")
except FileExistsError:
pass
data = request.get_json()
id_ = data["id_"]
done_how = data["done_how"]
patient_list = []
path = '../processed_files/'
path_list = pathlib.Path(path).glob('**/*.json')
for path in path_list:
patient = logic.load_questionnaire(path=path)
if patient["questionnaireresponseid"] != id_:
patient["color"] = patient["Patient_Priority"]
del patient["Patient_Priority"]
patient["id"] = patient["questionnaireresponseid"]
del patient["questionnaireresponseid"]
patient["waiting_time"] = int((time.time() - patient["entrance"]) / 60)
del patient["entrance"]
patient["primary_concern"] = patient["prios"][0]["answer"][0]["valueString"]
patient["answersInText"] = []
for questionAndAnswers in patient["prios"][1:]:
answers = map(lambda x: x["valueString"], questionAndAnswers["answer"])
patient["answersInText"].append(questionAndAnswers["text"] + " " + "; ".join(answers))
del patient["prios"]
patient_list.append(patient)
else:
with open(path) as file:
patientdata = json.load(file)
patientdata["handled_in_which_way"] = done_how
with open(path, "w") as file:
json.dump(patientdata, file)
os.rename(path, "../deleted_files/processed_files/" + id_ + ".json")
os.rename("../unprocessed_files/" + id_ + ".json", "../deleted_files/unprocessed_files/" + id_ + ".json")
return jsonify(patient_list)
# Make the WSGI interface available at the top level so wfastcgi can get it.
wsgi_app = app.wsgi_app
def generate_schema_string(obj):
schema_generator = SchemaBuilder()
schema_generator.add_object(obj)
return schema_generator.to_json(indent=2)
def register_to_middleware():
global service_uid
global service_api_key
global service_name
global service_diagnostics
headers = {
'Content-type': 'application/json',
'X-ApiKey': 't3d14s-s',
}
# Schema strings for TEDIAS Middleware descriptor
error_schema_str = generate_schema_string('error')
string_schema_str = generate_schema_string('result')
diagnostics_schema_str = generate_schema_string(service_diagnostics)
logs_schema_str = generate_schema_string([''])
incoming_data_input_schema_str = generate_schema_string(schemas.incoming_data_input_schema())
incoming_data_output_schema_str = generate_schema_string(schemas.incoming_data_output_schema())
return_data_output_schema_str = generate_schema_string(schemas.return_data_output_schema())
descriptor = {
'apiKey': service_api_key,
'serviceName': service_name,
'baseUrl': 'http://' + str(HOST) + ':' + str(PORT),
'methodDescriptors': [
{
"methodName": "description",
"relativeRoute": base_route + "/description",
"requestMethod": "GET",
"argumentValueTypes": None,
"returnValueTypes": {
"result": string_schema_str,
"error": error_schema_str
}
},
{
"methodName": "diagnostics",
"relativeRoute": base_route + "/diagnostics",
"requestMethod": "GET",
"argumentValueTypes": None,
"returnValueTypes": {
"result": diagnostics_schema_str,
"error": error_schema_str
}
},
{
"methodName": "logs",
"relativeRoute": base_route + "/logs",
"requestMethod": "GET",
"argumentValueTypes": None,
"returnValueTypes": {
"result": logs_schema_str,
"error": error_schema_str
}
},
{
"methodName": "incoming-data",
"relativeRoute": base_route + "/incoming-data",
"requestMethod": "POST",
"argumentValueTypes": {
"data": incoming_data_input_schema_str
},
"returnValueTypes": {
"result": incoming_data_output_schema_str,
"error": error_schema_str
}
},
{
"methodName": "return-data",
"relativeRoute": base_route + "/return-data",
"requestMethod": "POST",
"argumentValueTypes": {
"questionnaire_id": "string"
},
"returnValueTypes": {
"result": return_data_output_schema_str,
"error": error_schema_str
}
},
{
"methodName": "dashboard",
"relativeRoute": base_route + "/dashboard",
"requestMethod": "GET",
"argumentValueTypes": None,
"returnValueTypes": {
"result": None,
"error": error_schema_str
}
},
{
"methodName": "remove-questionnaire",
"relativeRoute": base_route + "/remove-questionnaire",
"requestMethod": "POST",
"argumentValueTypes": {
"questionnaire_id": "string"
},
"returnValueTypes": {
"result": None,
"error": error_schema_str
}
},
{
"methodName": "remove-all",
"relativeRoute": base_route + "/remove-all",
"requestMethod": "DELETE",
"argumentValueTypes": None,
"returnValueTypes": {
"result": None,
"error": error_schema_str
}
},
{
"methodName": "ampel",
"relativeRoute": base_route + "/ampel",
"requestMethod": "GET",
"argumentValueTypes": None,
"returnValueTypes": {
"result": None,
"error": error_schema_str
}
},
{
"methodName": "ampel",
"relativeRoute": base_route + "/ampel",
"requestMethod": "POST",
"argumentValueTypes": None,
"returnValueTypes": {
"result": None,
"error": error_schema_str
}
},
]
}
body = json.dumps(descriptor)
response = requests.post(middleware_base_address + '/services/register', data=body, headers=headers, verify=False)
if response.status_code > 299:
append_log('Something went wrong during registration')
return
service_uid = str(response.json())
def unregister_from_middleware():
global service_uid
headers = {
'Content-type': 'application/x-www-form-urlencoded',
'X-ApiKey': middlewareApiKey,
}
dateUrlEncoded = {"identifier": serviceIdentifier}
response = requests.post(middleware_base_address + '/services/unregister', data=dateUrlEncoded, headers=headers,
verify=False)
if response.status_code > 299:
append_log('Something went wrong during unregistration')
return
service_uid = ''
def main_loop():
while True:
try:
register_to_middleware()
while True:
print('> ', end='')
key = input()
if key == 'q':
break
unregister_from_middleware()
except requests.exceptions.ConnectionError:
print('Middleware either not running or not found.')
time.sleep(10)
def run_app(host, port):
app.run(host=host, port=port, use_reloader=False, debug=True)
# todo Im Orchestrator testen ->
# in def uc2_1_minimal():
# todo Patienten_Liste: Als Map machen,
# sodass die Patienten direkt nach ID abgefragt werden können
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(description='Set HOST and PORT externally.')
parser.add_argument('--HOST', metavar='HOST', required=False, help='Host url',
default=os.environ.get('SERVER_HOST', 'localhost'))
parser.add_argument('--PORT', metavar='PORT', required=False, help='Port number',
default=int(os.environ.get('SERVER_PORT_', '8069')))
args = parser.parse_args()
HOST = args.HOST
PORT = args.PORT
print("Host: {0}".format(HOST))
print("Port: {0}".format(PORT))
print("Trivial dashboard at http://{0}:{1}{2}".format(HOST, PORT, base_route + '/dashboard'))
web_client = threading.Thread(target=run_app, args=(HOST, PORT))
web_client.start()
main_loop = threading.Thread(target=main_loop)
main_loop.start()
main_loop.join()
exit(0)
import os, json
from werkzeug.security import generate_password_hash
from getpass import getpass
def add_user():
username = input("Username: ")
with open("../users") as file:
userdata = json.load(file)
usernames = list(userdata.keys())
if username in usernames:
print("username already exists")
return
password = getpass()
password = generate_password_hash(password, 'sha256')
userdata[username] = password
with open("../users", "w") as file:
json.dump(userdata, file)
add_user()
\ No newline at end of file
import json
import copy
import random
# load data
with open("patientdata.json", "r") as file:
patientData = json.load(file)
with open("answers.json", "r") as file:
answers = json.load(file)
with open("questions.json", "r") as file:
questions = json.load(file)
# is later used to check if question of id q_id should be asked according to the answers so far in anser_dictionary
def helper_func_for_check_question(dependency, answer_dictionary):
# print("q")
if dependency.split(".")[0] not in answer_dictionary:
# print("w")
return False
if dependency.split(".")[1] in answer_dictionary[dependency.split(".")[0]]:
return True
return False
def check_question(q_id, answer_dictionary):
if "or" in questions[q_id]["dependency"]:
for dependency in [d for d in questions[q_id]["dependency"] if d != "or"]:
if helper_func_for_check_question(dependency, answer_dictionary):
return True
else:
for dependency in questions[q_id]["dependency"]:
# print(dependency)
if not helper_func_for_check_question(dependency, answer_dictionary):
return False
return True
return False
# to stop the infinite loop coming up
stop_variable = False
while not stop_variable:
patientName = input("patientname (write 'stop' to stop): ")
if patientName == "stop":
stop_variable = True
else:
answer_dictionary = {}
for q_id, question in questions.items():
# print(answer_dictionary)
if check_question(q_id, answer_dictionary):
print(f"{q_id}: "+question["text"])
for a_id, answer in answers.items():
if a_id.split(".")[0] == q_id:
if answer["text"] == "#Freitext":
print(f" Freitext")
else:
a_id_pretty = a_id.split(".")[1]
print(f" {a_id_pretty}: "+answer["text"]+answer["flag"])
if question["single_choices"] == 0:
print(" Multiple answers possible!")
if question["text"] == "#Freitext":
_input = input("Answer: ")
answer_dictionary[q_id] = [_input]
else:
_input = input("Answer (only number): ")
answer_dictionary[q_id] = _input.split("; ")
if len([patientData[idx]["id"] for idx in range(len(patientData))]) == 0:
_id = 1
else:
_id = max([patientData[idx]["id"] for idx in range(len(patientData))])+1
newPatientData = {"id": _id, "first_name": patientName.split(" ")[0], "last_name": patientName.split(" ")[1], "answers": copy.deepcopy(answer_dictionary), "waiting_time": random.randint(1,45)}
patientData.append(newPatientData)
# save data
with open("patientdata.json", "w") as file:
json.dump(patientData, file)
import json
import os
import subprocess
import sys
import time
class QuestionnaireProcessor:
def __init__(self, questionnaire_path: dict = None):
if questionnaire_path is None:
with open("../questionnaire_templates/Processing_Template_Neuro.json") as file:
self.questionnaire = json.load(file)
else:
with open(questionnaire_path) as file:
self.questionnaire = json.load(file)
def loadUnprocessedPatientdata(self, path: str = None) -> dict:
if path is None:
path = "../unprocessed_files/"+os.listdir("../unprocessed_files")[0]
with open(path) as file:
unprocessedPatientdata = json.load(file)
return unprocessedPatientdata
def processPatientdata(self, unprocessedPatientdata: dict) -> dict:
patientdata = dict()
self._createProcessedHeadder(patientdata, unprocessedPatientdata)
self._createPrios(patientdata, unprocessedPatientdata)
self._createPatientpriority(patientdata, unprocessedPatientdata)
self._addEntranceTime(patientdata, unprocessedPatientdata)
return patientdata
def _createProcessedHeadder(self, patientdata: dict, unprocessedPatientdata: dict):
patientdata["first_name"] = " ".join(unprocessedPatientdata["contained"][0]["name"][0]["given"])
patientdata["last_name"] = unprocessedPatientdata["contained"][0]["name"][0]["family"]
patientdata["questionnaireresponseid"] = self.questionnaire["id"]
def _createPrios(self, patientdata: dict, unprocessedPatientdata: dict):
patientdata["prios"] = []
for question in unprocessedPatientdata["item"]:
prio = "yellow"
answer = []
for a in question["answer"]:
if a["flag"] == "red":
prio = "red"
if a["flag"] == "green" and prio != "red":
prio = "green"
answer.append({"valueString": a["valueString"]})
patientdata["prios"].append({"linkId": question["linkId"], "id": self.questionnaire["id"], "prio": prio, "text": question["text"], "answer": answer})
def _createPatientpriority(self, patientdata: dict, unprocessedPatientdata: dict):
patientdata["Patient_Priority"] = "yellow"
for question in patientdata["prios"]:
if question["prio"] == "red":
patientdata["Patient_Priority"] = "red"
if question["prio"] == "green" and patientdata["Patient_Priority"] != "red":
patientdata["Patient_Priority"] = "green"
def _addEntranceTime(self, patientdata: dict, unprocessedPatientdata: dict):
patientdata["entrance"] = time.time()
def saveProcessedData(self, data: dict, path: str = None):
if path is None:
path = "../processed_data/temporary.json"
if not os.path.exists(path):
subprocess.run(["touch", path])
with open(path, "w") as file:
json.dump(data,file)
else:
raise ValueError # file already exists... override?
import json
import sys
import time
class QuestionnaireFlagCheck:
def __init__(self, questionnaire_map=None):
self.questionnaire_response = None
if questionnaire_map is None:
questionnaire_map = "testQAndANeuro.json"
self.questionnaire_map = json.loads(open(questionnaire_map, "rb").read())
def process(self, path=None):
if path is None:
path = "../logic/testQNeuroRedFlag.json"
# todo check if questionnaire already processed
questionnaire = self.load_questionnaire(q_path=path)
prios = self.create_flags(questionnaire)
# return for landing stage
self.create_json_return_landing_stage(prios)
patient_priority = 'yellow'
for possible_flag in ['green', 'red']:
if possible_flag in [elem['prio'] for elem in prios]:
patient_priority = possible_flag
# todo check for correct name and contained.patient,
# do as function to keep code clean
patient_name = questionnaire['contained'][0]['name'][0]
# todo put answers of interest with questions in form (question: \n\t answer)
# check key in patientdata.json
return {
'questionnaireresponseid': questionnaire['id'],
'first_name': ' '.join(given_name for given_name in patient_name['given']),
'last_name': patient_name['family'],
'prios': prios,
'Patient_Priority': patient_priority,
'entrance': time.time()}
@staticmethod
def load_questionnaire(q_path=None):
if q_path is None:
q_path = "../logic/testQuestionareResponse.json"
file = open(q_path, "r")
return json.loads(file.read())
def create_flags(self, questionnaire: dict):
self.questionnaire_response = questionnaire
flag_response = []
for question_response in self.questionnaire_response['item']:
self.compare_question(question_response['linkId'])
prio = self.flag_answer(question_response=question_response)
flag_response.append({
"linkId": question_response['linkId'],
"prio": prio,
"id": questionnaire['id'],
"text": question_response['text'],
"answer": question_response['answer'],
})
return flag_response
def compare_question(self, link_id):
a = self.get_question(link_id, self.questionnaire_response)['text']
q = self.get_question(link_id, self.questionnaire_map)['text']
if q != a:
raise Exception("Questions with linkId={0} of response and map do not match!"
"\n\tq_res: {1}"
"\n\tq_map: {2}".format(link_id, a, q))
def flag_answer(self, question_response=None):
# todo make sure that the given answer exists in the template
question = self.get_question(question_response['linkId'], self.questionnaire_map)
patient_answers = [elem['valueString'] for elem in question_response['answer']]
for possible_answer in question['answer']:
if possible_answer['valueString'] in patient_answers:
if 'if' in possible_answer:
for flag_condition in possible_answer['if']:
cond_patient_answers = self.get_question(flag_condition['linkId'], questionnaire=self.questionnaire_response)
if flag_condition['answer'] in [elem['valueString'] for elem in cond_patient_answers['answer']]:
return possible_answer['flag']
pass
elif 'flag' in possible_answer:
return possible_answer['flag']
return 'yellow'
def get_question(self, question_response_link_id, questionnaire):
for q in questionnaire['item']:
if q['linkId'] == question_response_link_id:
return q
raise ValueError("Key not found in questionnaire")
@staticmethod
def create_json_return_landing_stage(flags):
pass
def load_questionnaire(path=None):
if path is None:
path = "../logic/testQuestionareResponse.json"
file = open(path, "r")
return json.loads(file.read())
if __name__ == '__main__':
print(len(sys.argv))
for i, arg in enumerate(sys.argv):
print(f"Argument {i:>6}: {arg}")
# class_test = QuestionnaireFlagCheck()
# result = class_test.process()
import pytest
import json
import os
import time
from logic import QuestionnaireProcessor
def test_init_empty() -> None:
'''Test whether after initializing without arguments the questionnaire variable is set correctly.'''
empty_init_questionnairePro = QuestionnaireProcessor()
with open("../questionnaire_templates/Processing_Template_Neuro.json") as file:
correct_empty_questionnare = json.load(file)
assert correct_empty_questionnare == empty_init_questionnairePro.questionnaire
def test_init_with_path() -> None:
'''Test whether after initializing with a path the questionnaire variable is set correctly.'''
questionnairePro = QuestionnaireProcessor("../questionnaire_templates/test_questionnaire")
with open("../questionnaire_templates/test_questionnaire") as file:
correct_test_questionnare = json.load(file)
assert correct_test_questionnare == questionnairePro.questionnaire
def test_load_unprocessed_empty_arg() -> None:
'''Test whether the first file in the ../unprocessed directory is loaded by the
loadUnprocessedPatientdata method if no arguments are given.'''
questionnairePro = QuestionnaireProcessor("../questionnaire_templates/test_questionnaire")
with open("../unprocessed_files/" + os.listdir("../unprocessed_files/")[0]) as file:
unprocessed = json.load(file)
assert unprocessed == questionnairePro.loadUnprocessedPatientdata()
def test_load_unprocessed_path1() -> None:
'''Test whether for a given path loadUnprocessedPatientdata returns the dictionary in said path.'''
questionnairePro = QuestionnaireProcessor("../questionnaire_templates/test_questionnaire")
with open("../unprocessed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87c0.json") as file:
unprocessed = json.load(file)
assert unprocessed == questionnairePro.loadUnprocessedPatientdata("../unprocessed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87c0.json")
def test_load_unprocessed_path2() -> None:
'''Test whether for a given path loadUnprocessedPatientdata returns the dictionary in said path.'''
questionnairePro = QuestionnaireProcessor("../questionnaire_templates/test_questionnaire")
with open("../unprocessed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87ae.json") as file:
unprocessed = json.load(file)
assert unprocessed == questionnairePro.loadUnprocessedPatientdata("../unprocessed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87ae.json")
def test_processed_headder_includes_correct_keys() -> None:
'''Test that all neccessary keys are included and have propper types'''
questionnairePro = QuestionnaireProcessor("../questionnaire_templates/test_questionnaire")
unprocessed = questionnairePro.loadUnprocessedPatientdata("../unprocessed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87b0.json")
processed = questionnairePro.processPatientdata(unprocessed)
keys = list(processed.keys())
for key in {"first_name", "last_name", "questionnaireresponseid"}:
key in keys
assert isinstance(processed[key],str)
def test_processed_headder_has_correct_values() -> None:
'''Test for the correct values'''
questionnairePro = QuestionnaireProcessor("../questionnaire_templates/test_questionnaire")
unprocessed = questionnairePro.loadUnprocessedPatientdata("../unprocessed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87b0.json")
processed = questionnairePro.processPatientdata(unprocessed)
assert processed["questionnaireresponseid"] == questionnairePro.questionnaire["id"]
assert processed["first_name"] == ""
assert processed["last_name"] == unprocessed["contained"][0]["name"][0]["family"]
def test_processed_includes_nothing_additional() -> None:
'''Test no keys are improperly included in the processed data and the dictionary is not empty'''
questionnairePro = QuestionnaireProcessor("../questionnaire_templates/test_questionnaire")
unprocessed = questionnairePro.loadUnprocessedPatientdata("../unprocessed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87b0.json")
processed = questionnairePro.processPatientdata(unprocessed)
assert processed
for key in processed:
assert key in {"questionnaireresponseid", "first_name", "last_name", "prios", "Patient_Priority", "entrance"}
def test_prios_key_exists() -> None:
'''Test that the prios key is in the processed data'''
questionnairePro = QuestionnaireProcessor("../questionnaire_templates/test_questionnaire")
unprocessed = questionnairePro.loadUnprocessedPatientdata("../unprocessed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87b0.json")
processed = questionnairePro.processPatientdata(unprocessed)
assert "prios" in processed
def test_prios_includes_no_additional_keys() -> None:
'''Test that prios includes no keys beyond the ones it should have and that it is not empty.'''
questionnairePro = QuestionnaireProcessor("../questionnaire_templates/test_questionnaire")
unprocessed = questionnairePro.loadUnprocessedPatientdata("../unprocessed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87b0.json")
processed = questionnairePro.processPatientdata(unprocessed)
prios = processed["prios"]
assert prios # should not be empty
for question in prios:
for key in question:
assert key in {"linkId", "id", "prio", "text", "answer"}
def test_prios_have_correct_structure() -> None:
'''Test that the prios have the intended structure of holding a string under linkId, id, text, prio
and a list under answer that contains dictionaries with the key valueString'''
questionnairePro = QuestionnaireProcessor("../questionnaire_templates/test_questionnaire")
unprocessed = questionnairePro.loadUnprocessedPatientdata("../unprocessed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87b0.json")
processed = questionnairePro.processPatientdata(unprocessed)
prios = processed["prios"]
for item in prios:
for key in {"linkId", "id", "prio", "text"}:
assert key in item
assert isinstance(item[key],str)
assert "answer" in item
assert isinstance(item["answer"],list)
assert item["answer"] # not empty
for answer in item["answer"]:
assert isinstance(answer,dict)
assert list(answer) == ["valueString"]
assert isinstance(answer["valueString"],str)
def test_prios_have_correct_values() -> None:
''' Test the values are copied over correctly and given the correct prios even if multiple answers were given. '''
questionnairePro = QuestionnaireProcessor("../questionnaire_templates/test_questionnaire")
unprocessed = questionnairePro.loadUnprocessedPatientdata("../unprocessed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87b0.json")
processed = questionnairePro.processPatientdata(unprocessed)
prios = processed["prios"]
assert prios[0]["linkId"] == "question neutral"
assert prios[0]["id"] == "e6f93635-cbd6-468b-9fb6-1fabde7b879a"
assert prios[0]["text"] == "question neutral"
assert prios[0]["prio"] == "yellow"
assert prios[0]["answer"][0]["valueString"] == "Answer1"
assert prios[4]["answer"][1]["valueString"] == "Answer3"
def test_priority_key_exists() -> None:
'''Test that the Patient Priority key exists and has a string Value. '''
questionnairePro = QuestionnaireProcessor("../questionnaire_templates/test_questionnaire")
unprocessed = questionnairePro.loadUnprocessedPatientdata("../unprocessed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87b0.json")
processed = questionnairePro.processPatientdata(unprocessed)
assert "Patient_Priority" in processed
assert isinstance(processed["Patient_Priority"],str)
def test_patient_priority_correct() -> None:
'''Test for unprocessed files that the patient priority was calculated corectly.'''
correct_prios = ["yellow", "red", "green", "red", "red", "red", "red", "red", "red", "green", "green", "red", "red", "red", "red", "red", "red", "green", "red", "green"]
for idx, fileending in enumerate(list(map(hex,range(int("ae",16),int("c2",16))))):
questionnairePro = QuestionnaireProcessor("../questionnaire_templates/test_questionnaire")
unprocessed = questionnairePro.loadUnprocessedPatientdata("../unprocessed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87"+fileending[-2:]+".json")
processed = questionnairePro.processPatientdata(unprocessed)
assert processed["Patient_Priority"] == correct_prios[idx]
def test_entrance_exists() -> None:
'''Test that the entrance time key exists and has a float type value. '''
questionnairePro = QuestionnaireProcessor("../questionnaire_templates/test_questionnaire")
unprocessed = questionnairePro.loadUnprocessedPatientdata("../unprocessed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87b0.json")
processed = questionnairePro.processPatientdata(unprocessed)
assert "entrance" in processed
assert isinstance(processed["entrance"],float)
def test_entrance_time_correct_value() -> None:
'''Test that the entrance time of a patient that was just processed is nearly the current time.'''
questionnairePro = QuestionnaireProcessor("../questionnaire_templates/test_questionnaire")
unprocessed = questionnairePro.loadUnprocessedPatientdata("../unprocessed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87b0.json")
processed = questionnairePro.processPatientdata(unprocessed)
assert int(time.time()) == int(processed["entrance"])
def test_save_processed_data() -> None:
'''Test that if processed data is saved the file exists and has the correct data in it.'''
questionnairePro = QuestionnaireProcessor("../questionnaire_templates/test_questionnaire")
unprocessed = questionnairePro.loadUnprocessedPatientdata("../unprocessed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87b0.json")
processed = questionnairePro.processPatientdata(unprocessed)
questionnairePro.saveProcessedData(processed, "../processed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87b0.json")
assert os.path.exists("../processed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87b0.json")
with open("../processed_files/e6f93635-cbd6-468b-9fb6-1fabde7b87b0.json")as file:
assert json.load(file) == processed
question 34: 33.1, nein ???
23 and similar: nein-Antwort -> multiple answers: is no answer possible?
[{"id": 1, "first_name": "Jialu", "last_name": "Li", "answers": {"1": ["1"], "2": ["1"], "3": ["2"], "4": ["1"], "6": ["2"]}, "waiting_time": 23}, {"id": 2, "first_name": "Emine", "last_name": "Durmaz", "answers": {"1": ["2"], "10": ["2"], "11": ["1"], "12": ["1"], "13": ["8"], "14": ["1"], "15": ["2"], "16": ["2"], "17": ["1"], "18": ["2"]}, "waiting_time": 45}, {"id": 3, "first_name": "Anna", "last_name": "Fischer", "answers": {"1": ["3"], "20": ["6"], "21": ["1", "2"], "22": ["2"], "23": ["2"]}, "waiting_time": 6}, {"id": 4, "first_name": "Peter", "last_name": "Schulz", "answers": {"1": ["4"], "20": ["2"], "21": ["3"], "22": ["7"], "23": ["5"]}, "waiting_time": 25}, {"id": 5, "first_name": "Desire", "last_name": "Dupont", "answers": {"1": ["5"], "24": ["3"], "25": ["1"], "26": ["3"], "27": ["2"]}, "waiting_time": 38}, {"id": 6, "first_name": "Kim", "last_name": "Park", "answers": {"1": ["6"], "28": ["1"], "29": ["1"], "30": ["1", "3", "4", "7"], "31": ["6"]}, "waiting_time": 40}]
\ No newline at end of file
[{"id": 1, "first_name": "Jialu", "last_name": "Li", "answers": {"1": ["1"], "2": ["1"], "3": ["2"], "4": ["1"], "6": ["2"]}, "waiting_time": 23}, {"id": 2, "first_name": "Emine", "last_name": "Durmaz", "answers": {"1": ["2"], "10": ["2"], "11": ["1"], "12": ["1"], "13": ["8"], "14": ["1"], "15": ["2"], "16": ["2"], "17": ["1"], "18": ["2"]}, "waiting_time": 45}, {"id": 3, "first_name": "Anna", "last_name": "Fischer", "answers": {"1": ["3"], "20": ["6"], "21": ["1", "2"], "22": ["2"], "23": ["2"]}, "waiting_time": 6}, {"id": 4, "first_name": "Peter", "last_name": "Schulz", "answers": {"1": ["4"], "20": ["2"], "21": ["3"], "22": ["7"], "23": ["5"]}, "waiting_time": 25}, {"id": 5, "first_name": "Desire", "last_name": "Dupont", "answers": {"1": ["5"], "24": ["3"], "25": ["1"], "26": ["3"], "27": ["2"]}, "waiting_time": 38}, {"id": 6, "first_name": "Kim", "last_name": "Park", "answers": {"1": ["6"], "28": ["1"], "29": ["1"], "30": ["1", "3", "4", "7"], "31": ["6"]}, "waiting_time": 40}, {"id": 7, "first_name": "Dimitri", "last_name": "Smirnoff", "answers": {"1": ["7"], "32": ["2"], "33": ["1"], "34": ["generelles Unwohlsein"], "38": ["2"], "40": ["2"]}, "waiting_time": 29}]
\ No newline at end of file
{"1": {"text": "Wegen welcher der folgenden Beschwerden haben Sie heute die Notaufnahme aufgesucht oder wurden von einem Arzt in die Notaufnahme geschickt (Bei Vorhandensein mehrerer Beschwerden kreuzen Sie bitte diejenige an, die im Vordergrund steht und zur Vorstellung hier Anlass gegeben hat.)", "number_answers": 8, "single_choices": 1, "dependency": []}, "2": {"text": "Seit wann besteht der Schwindel?", "number_answers": 4, "single_choices": 1, "dependency": ["1.1"]}, "3": {"text": "Leiden Sie zusätzlich unter Übelkeit/Erbrechen?", "number_answers": 2, "single_choices": 1, "dependency": ["1.1"]}, "4": {"text": "Ist der Schwindel dauerhaft vorhanden?", "number_answers": 2, "single_choices": 1, "dependency": ["1.1"]}, "5": {"text": "Tritt der Schwindel nur bei bestimmten Bewegungen auf (z. B. Umdrehen im Bett, Bücken, Kopfneigung nach hinten)?", "number_answers": 2, "single_choices": 1, "dependency": ["1.1", "4.2"]}, "6": {"text": "Haben Sie zusätzliche Beschwerden seit dem Auftreten des Schwindels?", "number_answers": 2, "single_choices": 1, "dependency": ["1.1"]}, "7": {"text": "Welche der folgenden Beschwerden haben Sie zusätzlich?", "number_answers": 8, "single_choices": 0, "dependency": ["1.1", "6.1"]}, "8": {"text": "Wie äußern sich die Doppelbilder konkret?", "number_answers": 3, "single_choices": 0, "dependency": ["1.1", "7.1"]}, "9": {"text": "Wie äußern sich Ihre Probleme konkret?", "number_answers": 4, "single_choices": 1, "dependency": ["1.1", "7.2"]}, "10": {"text": "Seit wann besteht der Kopfschmerz?", "number_answers": 4, "single_choices": 1, "dependency": ["1.2"]}, "11": {"text": "Sind die Schmerzen plötzlich oder innerhalb sehr kurzer Zeit (d. h. innerhalb einer Minute) aufgetreten?", "number_answers": 2, "single_choices": 1, "dependency": ["1.2"]}, "12": {"text": "In welchem Bereich des Kopfes?", "number_answers": 4, "single_choices": 1, "dependency": ["1.2"]}, "13": {"text": "Wie stark sind die Schmerzen?\n(0=keine Schmerzen, 10=maximal starke Schmerzen)?", "number_answers": 11, "single_choices": 1, "dependency": ["1.2"]}, "14": {"text": "Sind die Schmerzen hinsichtlich Ort ihres Auftretens, ihrer Stärke oder anderweitig anders als Kopfschmerzen, die Sie kennen?", "number_answers": 2, "single_choices": 1, "dependency": ["1.2"]}, "15": {"text": "Haben Sie Fieber?", "number_answers": 5, "single_choices": 1, "dependency": ["1.2"]}, "16": {"text": "Hatten Sie kürzlich eine Kopfverletzung, einen Sturz auf den Kopf oder heftiger Anprall?", "number_answers": 2, "single_choices": 1, "dependency": ["1.2"]}, "17": {"text": "Ging den Kopfschmerzen eine körperliche Anstrengung (inkl. Husten/Niesen/Pressen/schweres Heben) voraus?", "number_answers": 2, "single_choices": 1, "dependency": ["1.2"]}, "18": {"text": "Haben Sie weitere Beschwerden zusätzlich zu den Kopfschmerzen?", "number_answers": 2, "single_choices": 1, "dependency": ["1.2"]}, "19": {"text": "Welche der folgenden Beschwerden haben Sie zusätzlich?", "number_answers": 8, "single_choices": 0, "dependency": ["1.2", "18.1"]}, "20": {"text": "Seit wann besteht die Schwäche/Lähmung/ Missempfindung?", "number_answers": 6, "single_choices": 1, "dependency": ["1.3", "or", "1.4"]}, "21": {"text": "Welche Beschwerden haben Sie?", "number_answers": 3, "single_choices": 0, "dependency": ["1.3", "or", "1.4"]}, "22": {"text": "Wo sind Ihre Beschwerden?", "number_answers": 7, "single_choices": 1, "dependency": ["1.3", "or", "1.4"]}, "23": {"text": "Nehmen Sie derzeit eines der folgenden Medikamente ein?", "number_answers": 6, "single_choices": 0, "dependency": ["1.3", "or", "1.4"]}, "24": {"text": "Seit wann besteht die Sprach-/ Sprechstörung?", "number_answers": 4, "single_choices": 1, "dependency": ["1.5"]}, "25": {"text": "Welche Aussage beschreibt die Beschwerden am besten?", "number_answers": 3, "single_choices": 1, "dependency": ["1.5"]}, "26": {"text": "Haben oder hatten Sie zusätzlich eine oder mehrere der folgenden Beschwerden:", "number_answers": 3, "single_choices": 0, "dependency": ["1.5"]}, "27": {"text": "Nehmen Sie derzeit eines der folgenden Medikamente ein?", "number_answers": 6, "single_choices": 0, "dependency": ["1.5"]}, "28": {"text": "Seit wann besteht die Sehstörung?", "number_answers": 4, "single_choices": 1, "dependency": ["1.6"]}, "29": {"text": "Welche Aussage beschreibt die Beschwerden am besten?", "number_answers": 3, "single_choices": 1, "dependency": ["1.6"]}, "30": {"text": "Sind die Sehstörungen von anderen Symptomen begleitet:", "number_answers": 7, "single_choices": 0, "dependency": ["1.6"]}, "31": {"text": "Nehmen Sie derzeit eines der folgenden Medikamente ein?", "number_answers": 6, "single_choices": 0, "dependency": ["1.6"]}, "32": {"text": "Kommen Sie wegen eines stattgehabten Krampfanfalls in die Klinik?", "number_answers": 2, "single_choices": 1, "dependency": ["1.7"]}, "33": {"text": "Geben Sie bitte den Grund Ihres Kommens an.", "number_answers": 2, "single_choices": 1, "dependency": ["1.7", "32.2"]}, "34": {"text": "Warum kommen Sie in die Klinik?", "number_answers": 1, "single_choices": 1, "dependency": ["1.7", "33.1"]}, "35": {"text": "Wann war der Anfall?", "number_answers": 3, "single_choices": 1, "dependency": ["1.7", "32.1"]}, "36": {"text": "Hatten Sie mehr als einen Krampfanfall?", "number_answers": 2, "single_choices": 1, "dependency": ["1.7", "32.1"]}, "37": {"text": "Wie viele Krampfanfälle hatten Sie?", "number_answers": 3, "single_choices": 1, "dependency": ["1.7", "36.1"]}, "38": {"text": "Ist bei Ihnen eine Epilepsie bekannt?", "number_answers": 2, "single_choices": 1, "dependency": ["1.7"]}, "39": {"text": "Sind Sie deswegen in regelmäßiger Behandlung bei einem niedergelassenen Neurologen?", "number_answers": 2, "single_choices": 1, "dependency": ["1.7", "38.1"]}, "40": {"text": "Nehmen Sie regelmäßig ein Antiepileptikum (Medikament gegen epileptische Anfälle) ein?", "number_answers": 2, "single_choices": 1, "dependency": ["1.7"]}, "41": {"text": "Handelt es sich bei Ihren Beschwerden um", "number_answers": 6, "single_choices": 0, "dependency": ["1.8"]}, "42": {"text": "Welche anderen Beschwerden haben Sie?", "number_answers": 1, "single_choices": 1, "dependency": ["1.8", "41.6"]}, "43": {"text": "Seit wann bestehen diese Beschwerden?", "number_answers": 5, "single_choices": 1, "dependency": ["1.8"]}, "44": {"text": "Nehmen Sie wegen dieser Beschwerden Medikamente ein?", "number_answers": 2, "single_choices": 1, "dependency": ["1.8"]}, "45": {"text": "Welche Medikamente nehmen Sie aufgrund dieser Beschwerden?", "number_answers": 1, "single_choices": 1, "dependency": ["1.8", "44.1"]}, "46": {"text": "Waren Sie wegen dieser Beschwerden schon in ärztlicher Behandlung?", "number_answers": 3, "single_choices": 1, "dependency": ["1.8"]}, "47": {"text": "Ist bei Ihnen eine neurologische Erkrankung bekannt?", "number_answers": 2, "single_choices": 1, "dependency": [""]}, "48": {"text": "Welche neurologische Erkrankung ist bei Ihnen bekannt?", "number_answers": 5, "single_choices": 0, "dependency": ["47.1"]}, "49": {"text": "Sind Sie deswegen in regelmäßiger Behandlung bei einem niedergelassenen Neurologen?", "number_answers": 2, "single_choices": 1, "dependency": ["48.1"]} }
\ No newline at end of file
# Schemas for TEDIAS Middleware descriptor
# incomingData Schema (input). This is for providing the schema for the Middleware descriptor.
def incoming_data_input_schema():
return {
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"authored": {
"type": "string"
},
"contained": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"address": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"text": {
"type": "string"
}
},
"required": [
"text"
]
}
]
},
"birthDate": {
"type": "string"
},
"gender": {
"type": "string"
},
"id": {
"type": "string"
},
"identifier": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"type": {
"type": "object",
"properties": {
"text": {
"type": "string"
}
},
"required": [
"text"
]
},
"value": {
"type": "string"
}
},
"required": [
"type",
"value"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "object",
"properties": {
"text": {
"type": "string"
}
},
"required": [
"text"
]
},
"value": {
"type": "string"
}
},
"required": [
"type",
"value"
]
}
]
},
"name": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"family": {
"type": "string"
},
"given": {
"type": "array",
"items": [
{
"type": "string"
}
]
}
},
"required": [
"family",
"given"
]
}
]
},
"resourceType": {
"type": "string"
}
},
"required": [
"address",
"birthDate",
"gender",
"id",
"identifier",
"name",
"resourceType"
]
}
]
},
"id": {
"type": "string"
},
"identifier": {
"type": "object",
"properties": {
"type": {
"type": "object",
"properties": {
"text": {
"type": "string"
}
},
"required": [
"text"
]
},
"value": {
"type": "string"
}
},
"required": [
"type",
"value"
]
},
"item": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"answer": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"valueString": {
"type": "string"
}
},
"required": [
"valueString"
]
}
]
},
"linkId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"answer",
"linkId",
"text"
]
},
{
"type": "object",
"properties": {
"answer": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"valueString": {
"type": "string"
}
},
"required": [
"valueString"
]
}
]
},
"linkId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"answer",
"linkId",
"text"
]
}
]
},
"resourceType": {
"type": "string"
},
"source": {
"type": "object",
"properties": {
"reference": {
"type": "string"
}
},
"required": [
"reference"
]
},
"status": {
"type": "string"
}
},
"required": [
"authored",
"contained",
"id",
"identifier",
"item",
"resourceType",
"source",
"status"
]
}
# incomingData Schema (output)
def incoming_data_output_schema():
return {
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"questionnaireresponseid": {
"type": "string"
},
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
},
"prios": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"linkId": {
"type": "string"
},
"prio": {
"type": "string"
},
"id": {
"type": "string"
},
"text": {
"type": "string"
},
"answer": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"valueString": {
"type": "string"
}
},
"required": [
"valueString"
]
}
]
}
},
"required": [
"linkId",
"prio",
"id",
"text",
"answer"
]
},
{
"type": "object",
"properties": {
"linkId": {
"type": "string"
},
"prio": {
"type": "string"
},
"id": {
"type": "string"
},
"text": {
"type": "string"
},
"answer": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"valueString": {
"type": "string"
}
},
"required": [
"valueString"
]
}
]
}
},
"required": [
"linkId",
"prio",
"id",
"text",
"answer"
]
}
]
},
"Patient_Priority": {
"type": "string"
},
"entrance": {
"type": "number"
}
},
"required": [
"questionnaireresponseid",
"first_name",
"last_name",
"prios",
"Patient_Priority",
"entrance"
]
}
# returnData Schema (output)
def return_data_output_schema():
return {
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"Patient_Priority": {
"type": "string"
},
"entrance": {
"type": "number"
},
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
},
"prios": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"answer": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"valueString": {
"type": "string"
}
},
"required": [
"valueString"
]
}
]
},
"id": {
"type": "string"
},
"linkId": {
"type": "string"
},
"prio": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"answer",
"id",
"linkId",
"prio",
"text"
]
},
{
"type": "object",
"properties": {
"answer": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"valueString": {
"type": "string"
}
},
"required": [
"valueString"
]
}
]
},
"id": {
"type": "string"
},
"linkId": {
"type": "string"
},
"prio": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"answer",
"id",
"linkId",
"prio",
"text"
]
}
]
},
"questionnaireresponseid": {
"type": "string"
}
},
"required": [
"Patient_Priority",
"entrance",
"first_name",
"last_name",
"prios",
"questionnaireresponseid"
]
}
<html lang="GER">
<head>
<meta http-equiv="refresh" content="10" /> <title>
Patients and their flags
</title>
</head>
<body style="background-color:grey">
<b>Patient List</b><table>
<tr>
<th>Patient</th>
<th>Flag</th>
<th>Wait Time [s]</th>
</tr>
<tr>
<td>Hatsune, Miku</td>
<td> <p style="color:green;"> green
</p> </td> <td>44128</td>
</tr>
</table>
</body>
</html>
# Import server module
import time
import http.server
# Import SocketServer module
import socketserver
def start_server(port=8000):
# Create object for handling HTTP requests
handler = http.server.SimpleHTTPRequestHandler
# Run the server forever to handle the HTTP requests
with socketserver.TCPServer(("", port), handler) as httpd:
print("Web Server is running at http://localhost:%s" % port)
print("Patient list can be accessed at http://localhost:%s/web/PatientFlags.html" % port)
httpd.serve_forever()
def update_function(patient_list):
while True:
update_html_file(patient_list=patient_list)
time.sleep(0.5)
def html_patient(patient):
html_str = " <tr>\n" \
" <td>{0}</td>\n" \
" <td>" \
" <p style=\"color:{1};\">" \
" {1}\n" \
" </p>" \
" </td>" \
" <td>{2}</td>\n" \
" </tr>\n".format(patient["last_name"] + ' ' + patient["first_name"],
patient["Patient_Priority"],
int(time.time()-patient["entrance"]))
return html_str
def html_patient_list(patient_list):
html_str = "<b>Patient List</b>"
html_str += "<table>\n" \
" <tr>\n" \
" <th>Patient</th>\n" \
" <th>Flag</th>\n" \
" <th>Wait Time [s]</th>\n" \
" </tr>\n"
for elem in patient_list:
html_str += html_patient(elem)
html_str += "</table>\n"
return html_str
def html_header():
html_str = " <head>\n" \
" <meta http-equiv=\"refresh\" content=\"10\" />" \
" <title>\n" \
" Patients and their flags\n" \
" </title>\n" \
" </head>\n"
return html_str
def build_html(patient_list):
html_str = "<html lang=\"GER\">\n"
html_str += html_header()
html_str += "<body style=\"background-color:grey\">\n"
html_str += html_patient_list(patient_list)
html_str += "</body>\n"
html_str += "</html>\n"
return html_str
def update_html_file(patient_list):
table_file = open('web/PatientFlags.html', 'w')
table_file.write(build_html(patient_list))
table_file.close()
{
"resourceType":"QuestionnaireResponse",
"id":"e6f93635-cbd6-468b-9fb6-1fabde7b879a",
"identifier":{
"type":{
"text":"Questionnaire Id"
},
"value":"087445ac-56e2-438b-9aa3-f144d7114307"
},
"status":"completed",
"authored":"2022-01-20T12:58:21.776000+00:00",
"source":{
"reference":"#4ae96da4-e763-43b6-bced-cac45d6949db"
},
"item":[
{
"linkId":"Datum_1",
"text":"Heutiges Datum",
"answer":[
{
"valueString":"2022-03-09",
"flag":"red"
}
]
},
{
"linkId":"Freitext_1",
"text":"Wie geht es Ihnen sonst?",
"answer":[
{
"valueString":"Zweiter Fragebogen",
"flag":"green",
"if":[
{
"linkId":"Datum_1",
"answer":"2022-03-09"
}
]
}
]
}
]
}
\ No newline at end of file
{
"resourceType":"QuestionnaireResponse",
"id":"e6f93635-cbd6-468b-9fb6-1fabde7b879a",
"identifier":{
"type":{
"text":"Questionnaire Id"
},
"value":"087445ac-56e2-438b-9aa3-f144d7114307"
},
"status":"completed",
"authored":"2022-04-20T12:58:21.776000+00:00",
"source":{
"reference":"#4ae96da4-e763-43b6-bced-cac45d6949db"
},
"item":
[
{"linkId": "question neutral", "text": "question neutral", "multipleAnswers": "0",
"answer": [{"valueString": "Answer1", "flag": "yellow"}, {"valueString": "Answer2", "flag": "yellow"}, {"valueString": "Answer3", "flag": "yellow"}]},
{"linkId": "question 1RedFlag", "text": "question 1RedFlag", "multipleAnswers": "0",
"answer": [{"valueString": "Answer1", "flag": "red"}, {"valueString": "Answer2", "flag": "yellow"}, {"valueString": "Answer3", "flag": "yellow"}]},
{"linkId": "question 1GreenFlag", "text": "question 1GreenFlag", "multipleAnswers": "0",
"answer": [{"valueString": "Answer1", "flag": "green"}, {"valueString": "Answer2", "flag": "yellow"}, {"valueString": "Answer3", "flag": "yellow"}]},
{"linkId": "question 1Green2Red3yellow", "text": "question 1Green2Red3yellow", "multipleAnswers": "0",
"answer": [{"valueString": "Answer1", "flag": "green"}, {"valueString": "Answer2", "flag": "red"}, {"valueString": "Answer3", "flag": "yellow"}]},
{"linkId": "question neutral multipleAnswers", "text": "question neutral multipleAnswers", "multipleAnswers": "1",
"answer": [{"valueString": "Answer1", "flag": "yellow"}, {"valueString": "Answer2", "flag": "yellow"}, {"valueString": "Answer3", "flag": "yellow"}]},
{"linkId": "question 1RedFlag multipleAnswers", "text": "question 1RedFlag multipleAnswers", "multipleAnswers": "1",
"answer": [{"valueString": "Answer1", "flag": "red"}, {"valueString": "Answer2", "flag": "yellow"}, {"valueString": "Answer3", "flag": "yellow"}]},
{"linkId": "question 12RedFlag multipleAnswers", "text": "question 12RedFlag multipleAnswers", "multipleAnswers": "1",
"answer": [{"valueString": "Answer1", "flag": "red"}, {"valueString": "Answer2", "flag": "red"}, {"valueString": "Answer3", "flag": "yellow"}]},
{"linkId": "question 1GreenFlag multipleAnswers", "text": "question 1GreenFlag multipleAnswers", "multipleAnswers": "1",
"answer": [{"valueString": "Answer1", "flag": "green"}, {"valueString": "Answer2", "flag": "yellow"}, {"valueString": "Answer3", "flag": "yellow"}]},
{"linkId": "question 12GreenFlag multipleAnswers", "text": "question 12GreenFlag multipleAnswers", "multipleAnswers": "1",
"answer": [{"valueString": "Answer1", "flag": "green"}, {"valueString": "Answer2", "flag": "green"}, {"valueString": "Answer3", "flag": "yellow"}]},
{"linkId": "question 1Green2Red3Yellow multipleAnswers", "text": "question 1Green2Red3Yellow multipleAnswers", "multipleAnswers": "1",
"answer": [{"valueString": "Answer1", "flag": "green"}, {"valueString": "Answer2", "flag": "red"}, {"valueString": "Answer3", "flag": "yellow"}]}
]
}
\ No newline at end of file
Bcertifi==2021.10.8 Bcertifi==2021.10.8
{"questionnaireresponseid": "e6f93635-cbd6-468b-9fb6-1fabde7b879a", "first_name": "Miku", "last_name": "Hatsune", "prios": [{"linkId": "1", "prio": "yellow", "id": "e6f93635-cbd6-468b-9fb6-1fabde7b879a", "text": "Wegen welcher der folgenden Beschwerden haben Sie heute die Notaufnahme aufgesucht oder wurden von einem Arzt in die Notaufnahme geschickt (Bei Vorhandensein mehrerer Beschwerden kreuzen Sie bitte diejenige an, die im Vordergrund steht und zur Vorstellung hier Anlass gegeben hat.): ", "answer": [{"valueString": "Schwindel"}]}, {"linkId": "2", "prio": "yellow", "id": "e6f93635-cbd6-468b-9fb6-1fabde7b879a", "text": "Seit wann besteht der Schwindel?", "answer": [{"valueString": "L\u00e4nger als 6 Tage ohne k\u00fcrzliche Verschlimmerung"}]}, {"linkId": "3", "prio": "yellow", "id": "e6f93635-cbd6-468b-9fb6-1fabde7b879a", "text": "Leiden Sie zus\u00e4tzlich unter \u00dcbelkeit/Erbrechen?", "answer": [{"valueString": "Nein"}]}, {"linkId": "4", "prio": "yellow", "id": "e6f93635-cbd6-468b-9fb6-1fabde7b879a", "text": "Ist der Schwindel dauerhaft vorhanden?", "answer": [{"valueString": "Ja"}]}, {"linkId": "5", "prio": "green", "id": "e6f93635-cbd6-468b-9fb6-1fabde7b879a", "text": "Tritt der Schwindel nur bei bestimmten Bewegungen auf (z. B. Umdrehen im Bett, B\u00fccken, Kopfneigung nach hinten)?", "answer": [{"valueString": "Ja"}]}, {"linkId": "6", "prio": "green", "id": "e6f93635-cbd6-468b-9fb6-1fabde7b879a", "text": "Haben Sie zus\u00e4tzliche Beschwerden seit dem Auftreten des Schwindels? ", "answer": [{"valueString": "Nein"}]}], "Patient_Priority": "green", "entrance": 1648650461.425189}
\ No newline at end of file
{"resourceType": "QuestionnaireResponse", "id": "e6f93635-cbd6-468b-9fb6-1fabde7b879a", "contained": [{"resourceType": "Patient", "id": "4ae96da4-e763-43b6-bced-cac45d6949db", "identifier": [{"type": {"text": "Patient Number"}, "value": "0"}, {"type": {"text": "Case Number"}, "value": "0"}], "name": [{"family": "Hatsune", "given": ["Miku"]}], "gender": "female", "birthDate": "1900-01-01", "address": [{"text": "Musterstr"}]}], "identifier": {"type": {"text": "Questionnaire Id"}, "value": "087445ac-56e2-438b-9aa3-f144d7114307"}, "status": "completed", "authored": "2022-01-20T12:58:21.776000+00:00", "source": {"reference": "#4ae96da4-e763-43b6-bced-cac45d6949db"}, "item": [{"linkId": "1", "text": "Wegen welcher der folgenden Beschwerden haben Sie heute die Notaufnahme aufgesucht oder wurden von einem Arzt in die Notaufnahme geschickt (Bei Vorhandensein mehrerer Beschwerden kreuzen Sie bitte diejenige an, die im Vordergrund steht und zur Vorstellung hier Anlass gegeben hat.): ", "answer": [{"valueString": "Schwindel"}]}, {"linkId": "2", "text": "Seit wann besteht der Schwindel?", "answer": [{"valueString": "L\u00e4nger als 6 Tage ohne k\u00fcrzliche Verschlimmerung"}]}, {"linkId": "3", "text": "Leiden Sie zus\u00e4tzlich unter \u00dcbelkeit/Erbrechen?", "answer": [{"valueString": "Nein"}]}, {"linkId": "4", "text": "Ist der Schwindel dauerhaft vorhanden?", "answer": [{"valueString": "Ja"}]}, {"linkId": "5", "text": "Tritt der Schwindel nur bei bestimmten Bewegungen auf (z. B. Umdrehen im Bett, B\u00fccken, Kopfneigung nach hinten)?", "answer": [{"valueString": "Ja"}]}, {"linkId": "6", "text": "Haben Sie zus\u00e4tzliche Beschwerden seit dem Auftreten des Schwindels? ", "answer": [{"valueString": "Nein"}]}]}
\ No newline at end of file
cd api
flask run &
cd ../ui
npm run serve
\ No newline at end of file
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# hello-world
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# hello-world
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "hello-world",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.6.5",
"vue": "^3.0.0",
"vue-router": "^4.0.12"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/compiler-sfc": "^3.0.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^7.0.0"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
<template>
<!--img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/-->
<router-view />
</template>
<script>
// import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
// HelloWorld
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
import { createApp } from 'vue'
import router from './router'
import App from './App.vue'
createApp(App).use(router).mount('#app')
import { createRouter, createWebHistory } from 'vue-router'
import Ampel from '../views/Ampel.vue'
import WaitingTime from '../views/WaitingTime.vue'
const routes = [
{
path: '/ampel',
name: 'Ampel',
component: Ampel
},
{
path: '/waitingtime',
name: 'WaitingTime',
component: WaitingTime
},
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
<template>
<div>
<p>Test</p>
</div>
</template>
<script>
//import statements
export default {
name: "Ampel",
//components from import statements
data() {
return {
patients: []
}
},
methods: {
//search(){
//postSearch({ //only a get function?
//json like
//})
//.then(response => response.json())
//.then(data => {console.log(data); this.patients = data})
//}
}
}
</script>
<!--<style></style>-->
<template>
<div>
<p>Waiting</p>
</div>
</template>
<script>
//import statements
export default {
name: "WaitingTime",
//components from import statements
data() {
return {
// todo
}
},
methods: {
// todo
}
}
</script>
<!--<style></style>-->
\ No newline at end of file
npm uninstall tailwindcss postcss autoprefixer
npm install -D tailwindcss@npm:@tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "hello-world",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.6.5",
"fusioncharts": "^3.18.0",
"vue": "^3.0.0",
"vue-fusioncharts": "^3.2.0",
"vue-router": "^4.0.12",
"vuex": "^4.0.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/compiler-sfc": "^3.0.0",
"autoprefixer": "^9.8.8",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^7.0.0",
"postcss": "^7.0.39",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer')
]
}
\ No newline at end of file
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>UC2</title>
</head>
<body>
<noscript>
<strong>We're sorry but UC2 doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
<template>
<!--img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/-->
<router-view />
</template>
<script>
// import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
// HelloWorld
}
}
</script>
<!--<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>-->
\ No newline at end of file
const API_URL = "http://localhost:5000"
//const API_URL = "http://localhost:8710"
export function get_patient_list(jwt) {
return fetch(`${API_URL}/ampel`, {
method: 'GET',
headers: {
'Content-Type': 'application/json;charset=utf-8',
Authorization: `Bearer: ${jwt}`
}
})
}
export function delete_patient(id_, jwt) {
return fetch(`${API_URL}/ampel`, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
Authorization: `Bearer: ${jwt}`
},
body: JSON.stringify(id_)
})
}
export function login(auth) {
return fetch(`${API_URL}/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify(auth)
})
}
\ No newline at end of file
export function isValidJwt (jwt) {
console.log(jwt.split('.').length)
if (!jwt || jwt.split('.').length < 1) { // If no Token exists or it is formatted wrong this function will return false and you will be rerouted to login
return false
}
return true //returns true only to say that the jwt token exists in the correct form, not that the logindata was correct.
}
\ No newline at end of file
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
@tailwind base;
@tailwind components;
@tailwind utilities;
\ No newline at end of file
import { createApp } from 'vue'
import router from './router'
import App from './App.vue'
import store from './store'
import './main.css'
createApp(App).use(store).use(router).mount('#app')
import { createRouter, createWebHistory } from 'vue-router'
import Ampel from '../views/Ampel.vue'
import Login from '../views/Login.vue'
import store from '@/store'
const routes = [
{
path: '/ampel',
name: 'Ampel',
component: Ampel
},
{
path: '/login',
name: 'Login',
component: Login
},
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
// Login Guard
router.beforeEach((to, from, next) => {
if (store.getters['isAuthenticated']) {
if (to.name === 'Login') next({name: 'Ampel'})
else next()
} else {
if (to.name !== 'Login')
next({name: 'Login'})
else next()
}
})
export default router
import { createStore } from 'vuex'
import { login } from '@/api'
import { isValidJwt } from '@/authentification'
const store = createStore({
state() {
return {
jwt: '',
userData: {},
}
},
mutations: {
setUserData(state, payload) {
console.log('setUserData payload = ', payload)
state.userData = payload.userData
},
setJwtToken(state, payload) {
console.log('setJwtToken payload = ', payload)
localStorage.jwt = payload.jwt
state.jwt = payload.jwt
}
},
actions: {
login(context, userData) {
console.log("login userData = ", userData)
context.commit('setUserData', { userData })
return login(userData)
.then(response => response.json())
.then(data => { context.commit('setJwtToken', { jwt: data.token }) } )
.catch(error => {
console.log('Error Authenticating: ', error)
})
},
tryRecoverJwt(context) {
if (localStorage.getItem('jwt') !== '') {
console.log("recovering from localStorage")
context.commit("setJwtToken", { jwt: localStorage.jwt })
}
},
logout(context) {
context.commit("setUserData", { userData: {} })
context.commit("setJwtToken", { jwt: '' })
}
},
modules: {
},
getters: {
isAuthenticated(state) {
console.log(state, isValidJwt(state.jwt))
return isValidJwt(state.jwt)
}
}
})
export default store
\ No newline at end of file
<!--todo:
"done" button,
autorefresh -> noch mal googeln (so alle 5 Sekunden),
sorting -> Zweitrangig
longterm: history
-->
<template>
<div>
<div>
<p>todo: options(change to waiting time, change colorblindness, etc)</p>
</div>
<!-- todo: pretty, additional info, widths-->
<div class="content-center"><!--does not seem to work maybe just give some space in each direction-->
<table class="w-full border-collapse border border-slate-900 table-fixed" id="patientTable">
<thead>
<tr>
<th class="border border-slate-500" v-for="col in columns" :key="col" v-bind:class="this.tablewidths[col]">{{niceColumnNames[col]}}</th>
</tr>
</thead>
<tbody>
<template v-for="patient in patients" :key="patient.id">
<tr class="border border-slate-500">
<template v-for="col in columns" :key="col">
<td @click="this.changeAddionalInfoBools(patient.id)" v-bind:class="this.tablewidths[col]">{{patient[col]}} </td>
</template>
<td @click="this.changeAddionalInfoBools(patient.id)" v-bind:class="colornames[patient.color]"></td>
<td @click="this.patientDone(patient.id, 'done')" class="">Bearbeitet</td>
<td @click="this.patientDone(patient.id, 'sent_home')" class="">Nach Hause geschickt</td>
<td @click="this.patientDone(patient.id, 'sent_on')" class="">Weitergeleitet</td>
</tr>
<tr>
<td colspan="6" v-if="this.additionalInfoBools[patient.id]">
<div v-for="answerInText in patient.answersInText" :key="answerInText">
{{answerInText}}
</div>
</td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
</template>
<script>
import { get_patient_list } from '@/api'
import { delete_patient } from '@/api'
export default {
name: "Ampel",
//components from import statements
methods: {
fillAdditionalInfoBools() {
console.log({"fill": "called"})
for (const patient in this.patients){
if (!(patient.id in this.additionalInfoBools)){
this.additionalInfoBools[patient.id] = false;
}
}
},
changeAddionalInfoBools(patientid){
console.log({"change": "called", "id": patientid})
if (this.additionalInfoBools[patientid]){
this.additionalInfoBools[patientid] = false;
}
else{
this.additionalInfoBools[patientid] = true;
}
},
Intervallmethod(){
setInterval(() => {
get_patient_list(this.$store.state.jwt)
.then(response => response.json())
.then(data => { this.patients = data; console.log(data) })
.then(this.fillAdditionalInfoBools())
}, 5000)
},
patientDone(id_, done_how){
delete_patient({"id_": id_, "done_how": done_how}, this.$store.state.jwt)
.then(response => response.json())
.then(data => { this.patients = data; console.log(data) })
.then(this.fillAdditionalInfoBools())
},
},
beforeMount() {
get_patient_list(this.$store.state.jwt)
.then(response => response.json())
.then(data => { this.patients = data; console.log(data) })
.then(this.fillAdditionalInfoBools())
this.Intervallmethod()
},
data() {
return {
patients: [],
additionalInfoBools: {},
columns: ["id", "first_name", "last_name", "primary_concern", "waiting_time"],
niceColumnNames: {"id": "ID", "first_name": "Vorname", "last_name": "Nachname", "waiting_time": "Wartezeit", "primary_concern": "Besuchgrund", "color": ""},
tablewidths: {"id": "border border-slate-500 flex justify-center", "first_name": "border border-slate-500", "last_name": "border border-slate-500", "waiting_time": "border border-slate-500", "primary_concern": "border border-slate-500 w-5/12", "color": "border border-slate-500"},
colornames: {"red": "bg-red-600", "green": "bg-green-600", "yellow": "bg-yellow-600"}
}
},
}
</script>
<template>
<div class="flex flex-col items-center">
<div class="text-left w-64 mb-8 space-y-2 text-center text-2xl">Bitte authentifizieren Sie sich</div>
<div class="flex flex-col space-y-2">
<input
type="text" id="username" name="username" v-model="username"
placeholder="username">
<input
type="password" id="password" name="password" v-model="password"
placeholder="password">
</div>
<div>
<button @click="postLogin" class="bg-blue-400 text-white">
Login
</button>
</div>
</div>
</template>
<script>
export default {
name: "Login",
methods: {
postLogin(){
this.$store.dispatch('login', { username: this.username, password: this.password })
.then(() => this.$router.push('/ampel'))
},
},
data() {
return {
username: "",
password: "",
}
},
}
</script>
\ No newline at end of file
<template>
<div>
<p>Waiting</p>
</div>
</template>
<script>
//import Vue from 'vue';
//import VueFusionCharts from 'vue-fusioncharts';
//import FusionCharts from 'fusioncharts';
//import Column2D from 'fusioncharts/fusioncharts.charts';
//import FusionTheme from 'fusioncharts/themes/fusioncharts.theme.fusion';
//
//Vue.use(VueFusionCharts, FusionCharts, Column2D, FusionTheme);
export default {
name: "WaitingTime",
//components from import statements
data() {
return {
// todo
}
},
methods: {
// todo
}
}
</script>
<!--<style></style>-->
\ No newline at end of file
module.exports = {
content: ["./src/App.vue", "./src/**/*.{html, js, vue}"],
theme: {
extend: {},
},
plugins: [],
}
\ No newline at end of file
{"resourceType": "QuestionnaireResponse", "id": "e6f93635-cbd6-468b-9fb6-1fabde7b879a", "contained": [{"resourceType": "Patient", "id": "4ae96da4-e763-43b6-bced-cac45d6949db", "identifier": [{"type": {"text": "Patient Number"}, "value": "0"}, {"type": {"text": "Case Number"}, "value": "0"}], "name": [{"family": "Hatsune", "given": ["Miku"]}], "gender": "female", "birthDate": "1900-01-01", "address": [{"text": "Musterstr"}]}], "identifier": {"type": {"text": "Questionnaire Id"}, "value": "087445ac-56e2-438b-9aa3-f144d7114307"}, "status": "completed", "authored": "2022-01-20T12:58:21.776000+00:00", "source": {"reference": "#4ae96da4-e763-43b6-bced-cac45d6949db"}, "item": [{"linkId": "1", "text": "Wegen welcher der folgenden Beschwerden haben Sie heute die Notaufnahme aufgesucht oder wurden von einem Arzt in die Notaufnahme geschickt (Bei Vorhandensein mehrerer Beschwerden kreuzen Sie bitte diejenige an, die im Vordergrund steht und zur Vorstellung hier Anlass gegeben hat.): ", "answer": [{"valueString": "Schwindel"}]}, {"linkId": "2", "text": "Seit wann besteht der Schwindel?", "answer": [{"valueString": "L\u00e4nger als 6 Tage ohne k\u00fcrzliche Verschlimmerung"}]}, {"linkId": "3", "text": "Leiden Sie zus\u00e4tzlich unter \u00dcbelkeit/Erbrechen?", "answer": [{"valueString": "Nein"}]}, {"linkId": "4", "text": "Ist der Schwindel dauerhaft vorhanden?", "answer": [{"valueString": "Ja"}]}, {"linkId": "5", "text": "Tritt der Schwindel nur bei bestimmten Bewegungen auf (z. B. Umdrehen im Bett, B\u00fccken, Kopfneigung nach hinten)?", "answer": [{"valueString": "Ja"}]}, {"linkId": "6", "text": "Haben Sie zus\u00e4tzliche Beschwerden seit dem Auftreten des Schwindels? ", "answer": [{"valueString": "Nein"}]}]}
\ No newline at end of file
{"user1": "sha256$qoYHrJmQr0tZMhJ9$61dd48c6c3f9c1fb06ac1a1416bd8dcb37d87ca3e558ded37da5448b568e1eef"}
\ No newline at end of file
.TEDIAS @ 8d91e986
Subproject commit 8d91e98692c223e464302ed25da22573438ae328
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "544fe53f",
"metadata": {},
"outputs": [],
"source": [
"import glob\n",
"import numpy as np\n",
"from scipy.signal import resample_poly\n",
"import wfdb\n",
"from wfdb import processing\n",
"import math\n",
"import _pickle as pickle\n",
"import matplotlib.pyplot as plt\n",
"import pandas as pd\n",
"import ast\n",
"import os"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "2ff3353a",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>patient_id</th>\n",
" <th>filename_hr</th>\n",
" <th>_AVB</th>\n",
" <th>AMI</th>\n",
" <th>AO/AE</th>\n",
" <th>IMI</th>\n",
" <th>ISCA</th>\n",
" <th>ISC_</th>\n",
" <th>IVCD</th>\n",
" <th>LAFB/LPFB</th>\n",
" <th>LBBB</th>\n",
" <th>NORM</th>\n",
" <th>NST_</th>\n",
" <th>RBBB</th>\n",
" <th>STTC</th>\n",
" <th>VH</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>15709.0</td>\n",
" <td>records500/00000/00001_hr</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>13243.0</td>\n",
" <td>records500/00000/00002_hr</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>20372.0</td>\n",
" <td>records500/00000/00003_hr</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>17014.0</td>\n",
" <td>records500/00000/00004_hr</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>17448.0</td>\n",
" <td>records500/00000/00005_hr</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>21185</th>\n",
" <td>17180.0</td>\n",
" <td>records500/21000/21833_hr</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>21186</th>\n",
" <td>20703.0</td>\n",
" <td>records500/21000/21834_hr</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>21187</th>\n",
" <td>19311.0</td>\n",
" <td>records500/21000/21835_hr</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>21188</th>\n",
" <td>8873.0</td>\n",
" <td>records500/21000/21836_hr</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>21189</th>\n",
" <td>11744.0</td>\n",
" <td>records500/21000/21837_hr</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>21190 rows × 16 columns</p>\n",
"</div>"
],
"text/plain": [
" patient_id filename_hr _AVB AMI AO/AE IMI ISCA \\\n",
"0 15709.0 records500/00000/00001_hr 0 0 0 0 0 \n",
"1 13243.0 records500/00000/00002_hr 0 0 0 0 0 \n",
"2 20372.0 records500/00000/00003_hr 0 0 0 0 0 \n",
"3 17014.0 records500/00000/00004_hr 0 0 0 0 0 \n",
"4 17448.0 records500/00000/00005_hr 0 0 0 0 0 \n",
"... ... ... ... ... ... ... ... \n",
"21185 17180.0 records500/21000/21833_hr 0 0 0 0 0 \n",
"21186 20703.0 records500/21000/21834_hr 0 0 0 0 0 \n",
"21187 19311.0 records500/21000/21835_hr 0 0 0 0 1 \n",
"21188 8873.0 records500/21000/21836_hr 0 0 0 0 0 \n",
"21189 11744.0 records500/21000/21837_hr 0 0 0 0 0 \n",
"\n",
" ISC_ IVCD LAFB/LPFB LBBB NORM NST_ RBBB STTC VH \n",
"0 0 0 0 0 1 0 0 0 0 \n",
"1 0 0 0 0 1 0 0 0 0 \n",
"2 0 0 0 0 1 0 0 0 0 \n",
"3 0 0 0 0 1 0 0 0 0 \n",
"4 0 0 0 0 1 0 0 0 0 \n",
"... ... ... ... ... ... ... ... ... .. \n",
"21185 0 0 0 0 0 0 0 1 0 \n",
"21186 0 0 0 0 1 0 0 0 0 \n",
"21187 0 0 0 0 0 0 0 0 0 \n",
"21188 0 0 0 0 1 0 0 0 0 \n",
"21189 0 0 0 0 1 0 0 0 0 \n",
"\n",
"[21190 rows x 16 columns]"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"record_path = '../../../../Datenbanken/PTB-XL/'\n",
"one_hot_encoded_diag = pd.read_csv(record_path+'one-hot-encoded_subclasses.csv')\n",
"one_hot_encoded_diag"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "3d5f4d9c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Use the lead: ['all']\n",
"Save datafiles to ./all_\n"
]
}
],
"source": [
"leads = ['I', 'II', 'III', 'AVR', 'AVL', 'AVF', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6', 'all']\n",
"leads = np.array(leads)\n",
"\n",
"#If yout want to train only on a lead selection append the indices of the corresponding leads into the list use_leads\n",
"#If yout want to use all 12 leads, append 12\n",
"use_leads = [12]\n",
"lead_names = leads[use_leads]\n",
"print('Use the lead:', lead_names)\n",
"\n",
"datapath = './'\n",
"for lead_name in lead_names:\n",
" datapath = datapath + lead_name +'_'\n",
"\n",
"if not os.path.exists(datapath):\n",
" os.makedirs(datapath)\n",
" \n",
"print('Save datafiles to', datapath)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "d9b21dcc",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Number of patients in the test set: 1847\n",
"Number of patients in the validation set: 1847\n",
"Number of patients in the training set: 14774\n",
"test set traces shape: (2125, 4096, 12)\n",
"test set diagnostic shape: (2125, 14)\n",
"validation set traces shape: (2116, 4096, 12)\n",
"validation set diagnostic shape: (2116, 14)\n",
"5.7 % done...\n",
"11.4 % done...\n",
"17.1 % done...\n",
"22.8 % done...\n",
"28.4 % done...\n",
"34.1 % done...\n",
"39.8 % done...\n",
"45.5 % done...\n",
"51.2 % done...\n",
"56.9 % done...\n",
"62.6 % done...\n",
"68.3 % done...\n",
"73.9 % done...\n",
"79.6 % done...\n",
"85.3 % done...\n",
"91.0 % done...\n",
"96.7 % done...\n",
"100.0% DONE.\n",
"training set traces shape: (16949, 4096, 12)\n",
"training set diagnostic shape: (16949, 14)\n"
]
}
],
"source": [
"import random\n",
"random.seed(42)\n",
"\n",
"#Get a list of all patients\n",
"patients = np.array(one_hot_encoded_diag['patient_id'])\n",
"pat, num = np.unique(patients, return_counts = True)\n",
"random.shuffle(pat)\n",
"\n",
"#Split the patient into 10 folds\n",
"patient_split = np.array_split(pat, 10)\n",
"\n",
"#Use 10% of all patients as test set and 10% as validation set\n",
"test_patients = patient_split[0]\n",
"val_patients = patient_split[1]\n",
"\n",
"print('Number of patients in the test set:', len(test_patients))\n",
"print('Number of patients in the validation set:', len(val_patients))\n",
"print('Number of patients in the training set:', len(pat)-len(test_patients)-len(val_patients))\n",
"\n",
"\n",
"#Create the output data of the test, val and train patients from the pandas DataFrame\n",
"test_diag = []\n",
"val_diag = []\n",
"train_diag = []\n",
"\n",
"for patient in pat:\n",
" if patient in test_patients:\n",
" ecg_id = np.where(patient==one_hot_encoded_diag['patient_id'])[0]\n",
" for ecg in ecg_id:\n",
" diagnose = list(one_hot_encoded_diag.loc[ecg])\n",
" test_diag.append(diagnose)\n",
" elif patient in val_patients:\n",
" ecg_id = np.where(patient==one_hot_encoded_diag['patient_id'])[0]\n",
" for ecg in ecg_id:\n",
" diagnose = list(one_hot_encoded_diag.loc[ecg])\n",
" val_diag.append(diagnose)\n",
" else:\n",
" ecg_id = np.where(patient==one_hot_encoded_diag['patient_id'])[0]\n",
" for ecg in ecg_id:\n",
" diagnose = list(one_hot_encoded_diag.loc[ecg])\n",
" train_diag.append(diagnose)\n",
" \n",
"#output format: integer-array of shape (number of test patients, 14)\n",
"test_diag_arr = np.array(np.array(test_diag)[:,2:], dtype=int)\n",
"#find the ECG signals corresponding to the patient and the diagnosis via the entry filename_hr\n",
"test_diag_file = np.array(np.array(test_diag)[:,1], dtype=str)\n",
"test_traces = list()\n",
"\n",
"for file in test_diag_file:\n",
" filepath = record_path+file\n",
" signal, fields = wfdb.rdsamp(filepath) #read the records with wfdb software package \n",
" ecg_sig = np.zeros((4096,12))\n",
" for k in use_leads:\n",
" if k == 12:\n",
" ecg_sig = signal[:4096,:] #convert signal into numpy array\n",
" else:\n",
" ecg_sig[:,k] = signal[:4096,k]\n",
" test_traces.append(ecg_sig)\n",
"\n",
"#convert list of records to numpy array\n",
"test_traces = np.array(test_traces, dtype=np.float32)\n",
"print('test set traces shape:', test_traces.shape)\n",
"print('test set diagnostic shape:', test_diag_arr.shape)\n",
"\n",
"#Save the traces and the diagnosis as pickle files\n",
"with open(datapath+'/test_set_traces.pkl', 'wb') as output:\n",
" pickle.dump(test_traces, output)\n",
"with open(datapath+'/test_set_diag.pkl', 'wb') as output:\n",
" pickle.dump(test_diag_arr, output)\n",
"\n",
"#Copy-and-Paste of the code above for validation and training set...\n",
"\n",
"val_diag_arr = np.array(np.array(val_diag)[:,2:], dtype=int)\n",
"val_diag_file = np.array(np.array(val_diag)[:,1], dtype=str)\n",
"val_traces = list()\n",
"\n",
"for file in val_diag_file:\n",
" filepath = record_path+file\n",
" signal, fields = wfdb.rdsamp(filepath)\n",
" ecg_sig = np.zeros((4096,12))\n",
" for k in use_leads:\n",
" if k == 12:\n",
" ecg_sig = signal[:4096,:]\n",
" else:\n",
" ecg_sig[:,k] = signal[:4096,k]\n",
" val_traces.append(ecg_sig)\n",
"\n",
"val_traces = np.array(val_traces, dtype=np.float32)\n",
"print('validation set traces shape:', val_traces.shape)\n",
"print('validation set diagnostic shape:', val_diag_arr.shape)\n",
"\n",
"with open(datapath+'/val_set_traces.pkl', 'wb') as output:\n",
" pickle.dump(val_traces, output)\n",
"with open(datapath+'/val_set_diag.pkl', 'wb') as output:\n",
" pickle.dump(val_diag_arr, output)\n",
"\n",
" \n",
"train_diag_arr = np.array(np.array(train_diag)[:,2:], dtype=int)\n",
"train_diag_file = np.array(np.array(train_diag)[:,1], dtype=str)\n",
"train_traces = list()\n",
"\n",
"counter = 0\n",
"per = 0\n",
"for file in train_diag_file:\n",
" filepath = record_path+file\n",
" signal, fields = wfdb.rdsamp(filepath)\n",
" ecg_sig = np.zeros((4096,12))\n",
" for j in use_leads:\n",
" if j == 12:\n",
" ecg_sig = signal[:4096,:]\n",
" else:\n",
" ecg_sig[:,j] = signal[:4096,j]\n",
" train_traces.append(ecg_sig)\n",
" counter = counter+1\n",
" per = per+1\n",
" if per == len(train_diag_file):\n",
" print('100.0% DONE.')\n",
" elif counter==964:\n",
" print(np.round(per/len(train_diag_file)*100, 1), '% done...')\n",
" counter = 0\n",
"\n",
"train_traces = np.array(train_traces, dtype=np.float32)\n",
"print('training set traces shape:', train_traces.shape)\n",
"print('training set diagnostic shape:', train_diag_arr.shape)\n",
"\n",
"with open(datapath+'/train_set_traces.pkl', 'wb') as output:\n",
" pickle.dump(train_traces, output)\n",
"with open(datapath+'/train_set_diag.pkl', 'wb') as output:\n",
" pickle.dump(train_diag_arr, output)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "04341f12",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "automatic-ecg-diagnosis",
"language": "python",
"name": "automatic-ecg-diagnosis"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.5"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Username: mhirsch
Token: w1kB_NGNf9qZxaHCqdRS
Mit folgendem Command kannst du das Repository clonen:
git clone https://mhirsch:w1kB_NGNf9qZxaHCqdRS@gitlab.cc-asp.fraunhofer.de/tedias/architecture/tedias.architecture.git
Probier mal, ob das bei dir klappt.
import json
import os
from copy import deepcopy
response = {
"resourceType": "QuestionnaireResponse",
"id": "e6f93635-cbd6-468b-9fb6-1fabde7b879a",
"contained":
[
{
"resourceType": "Patient",
"id": "4ae96da4-e763-43b6-bced-cac45d6949db",
"identifier":
[
{
"type":
{
"text": "Patient Number"
},
"value": "0"
},
{
"type":
{
"text": "Case Number"
},
"value": "0"
}
],
"name":
[
{
"family": "Tepes",
"given":
[
"Krul"
]
}
],
"gender": "female",
"birthDate": "1900-01-01",
"address":
[
{
"text": "Musterstr"
}
]
}
],
"identifier":
{
"type":
{
"text": "Questionnaire Id"
},
"value": "087445ac-56e2-438b-9aa3-f144d7114307"
},
"status": "completed",
"authored": "2022-01-20T12:58:21.776000+00:00",
"source":
{
"reference": "#4ae96da4-e763-43b6-bced-cac45d6949db"
},
"item":
[
{
"linkId": "1",
"text": "Wegen welcher der folgenden Beschwerden haben Sie heute die Notaufnahme aufgesucht oder wurden von einem Arzt in die Notaufnahme geschickt (Bei Vorhandensein mehrerer Beschwerden kreuzen Sie bitte diejenige an, die im Vordergrund steht und zur Vorstellung hier Anlass gegeben hat.): ",
"answer":
[
{
"valueString": "Schwindel"
}
]
},
]
}
def f(x):
return x.split("-")[-1].split(".")[0]
with open("questionnaire") as file:
questionnaire = json.load(file)
files_already_created = list(map(f, os.listdir("unprocessed_files")))
files_already_created.sort(key = lambda i: int(i, 16))
last_patient_id = "e6f93635-cbd6-468b-9fb6-" + files_already_created[-1]
patient_name = " " # placeholder
while patient_name != "":
response["item"] = []
patient_name = input("Patient Name: ")
if " " in patient_name:
first_name = patient_name.split(" ")[1]
last_name = patient_name.split(" ")[0]
else:
first_name = patient_name
last_name = ""
response["id"] = "-".join(last_patient_id.split("-")[:-1]) + "-" + hex(int(last_patient_id.split("-")[-1],16)+1)[2:] # counts the last hexadecimal one up
last_patient_id = deepcopy(response["id"])
print(f"ID: {last_patient_id}")
response["contained"][0]["name"] = [
{
"family": first_name,
"given":
[
last_name
]
}
]
for question in questionnaire["item"]:
question_processed = {"linkId": question["linkId"], "text": question["text"], "answer": []}
print(question["text"])
print("Answers: ")
for idx, answer in enumerate(question["answer"]):
print(str(idx+1)+": "+answer["valueString"])
if int(question["multipleAnswers"]):
input_answer = " " # placeholder
print("choose by number untill done, then press enter")
while input_answer != "":
input_answer = input("> ")
if input_answer != "":
try:
input_answer = int(input_answer)
except Exception:
input_answer = -1
if input_answer-1 in range(len(question["answer"])):
question_processed["answer"].append(question["answer"][input_answer-1])
else:
print("Invalid")
else:
input_answer = -1
while input_answer-1 not in range(len(question["answer"])):
print("choose an answer by number")
input_answer = input("> ")
try:
input_answer = int(input_answer)
except Exception:
input_answer = -1
question_processed["answer"].append(question["answer"][input_answer-1])
response["item"].append(question_processed)
os.system("touch unprocessed_files/"+last_patient_id+".json")
with open("unprocessed_files/"+last_patient_id+".json", "w") as file:
json.dump(response, file)
{
"resourceType": "QuestionnaireResponse",
"id": "e6f93635-cbd6-468b-9fb6-1fabde7b879a",
"contained":
[
{
"resourceType": "Patient",
"id": "4ae96da4-e763-43b6-bced-cac45d6949db",
"identifier":
[
{
"type":
{
"text": "Patient Number"
},
"value": "0"
},
{
"type":
{
"text": "Case Number"
},
"value": "0"
}
],
"name":
[
{
"family": "Tepes",
"given":
[
"Krul"
]
}
],
"gender": "female",
"birthDate": "1900-01-01",
"address":
[
{
"text": "Musterstr"
}
]
}
],
"identifier":
{
"type":
{
"text": "Questionnaire Id"
},
"value": "087445ac-56e2-438b-9aa3-f144d7114307"
},
"status": "completed",
"authored": "2022-01-20T12:58:21.776000+00:00",
"source":
{
"reference": "#4ae96da4-e763-43b6-bced-cac45d6949db"
},
"item":
[
{
"linkId": "1",
"text": "Wegen welcher der folgenden Beschwerden haben Sie heute die Notaufnahme aufgesucht oder wurden von einem Arzt in die Notaufnahme geschickt (Bei Vorhandensein mehrerer Beschwerden kreuzen Sie bitte diejenige an, die im Vordergrund steht und zur Vorstellung hier Anlass gegeben hat.): ",
"answer":
[
{
"valueString": "Schwindel"
}
]
},
{
"linkId": "2",
"text": "Seit wann besteht der Schwindel?",
"answer":
[
{
"valueString": "Seit 4 Stunden oder kürzer"
}
]
},
{
"linkId": "3",
"text": "Leiden Sie zusätzlich unter Übelkeit/Erbrechen?",
"answer":
[
{
"valueString": "Nein"
}
]
},
{
"linkId": "4",
"text": "Ist der Schwindel dauerhaft vorhanden?",
"answer":
[
{
"valueString": "Ja"
}
]
},
{
"linkId": "5",
"text": "Tritt der Schwindel nur bei bestimmten Bewegungen auf (z. B. Umdrehen im Bett, Bücken, Kopfneigung nach hinten)?",
"answer":
[
{
"valueString": "Ja"
}
]
},
{
"linkId": "6",
"text": "Haben Sie zusätzliche Beschwerden seit dem Auftreten des Schwindels? ",
"answer":
[
{
"valueString": "Ja"
}
]
}
]
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment