def pipeline_download_page(): researcher = Researcher.objects.get(username=session[SESSION_NAME]) warn_researcher_if_hasnt_yet_generated_access_key(researcher) iteratable_studies = get_researcher_allowed_studies(as_json=False) # dict of {study ids : list of user ids} users_by_study = {str(study['id']): [user.id for user in Participant.objects.filter(study__id=study['id'])] for study in iteratable_studies} # it is a bit obnoxious to get this data, we need to deduplcate it and then turn it back into a list tags_by_study = { study['id']: list(set([tag for tag in PipelineUploadTags.objects.filter( pipeline_upload__study__id=study['id'] ).values_list("tag", flat=True)])) for study in iteratable_studies } return render_template( "data_pipeline_web_form.html", allowed_studies=get_researcher_allowed_studies(), downloadable_studies=get_researcher_allowed_studies(), users_by_study=users_by_study, tags_by_study=json.dumps(tags_by_study), is_admin=researcher_is_an_admin() )
def view_study(study_id=None): study = Study.objects.get(pk=study_id) tracking_survey_ids = study.get_survey_ids_and_object_ids_for_study( 'tracking_survey') audio_survey_ids = study.get_survey_ids_and_object_ids_for_study( 'audio_survey') image_survey_ids = study.get_survey_ids_and_object_ids_for_study( 'image_survey') participants = study.participants.all() study_fields = list(study.fields.all().values_list('field_name', flat=True)) for p in participants: p.values_dict = { tag.field.field_name: tag.value for tag in p.field_values.all() } return render_template( 'view_study.html', study=study, patients=participants, audio_survey_ids=audio_survey_ids, image_survey_ids=image_survey_ids, tracking_survey_ids=tracking_survey_ids, allowed_studies=get_researcher_allowed_studies(), is_admin=researcher_is_an_admin(), study_fields=study_fields, page_location='study_landing', study_id=study_id, )
def create_study(): # ONLY THE SITE ADMIN CAN CREATE NEW STUDIES. if not get_session_researcher().site_admin: return abort(403) if request.method == 'GET': studies = [ study.as_native_python() for study in Study.get_all_studies_by_name() ] return render_template( 'create_study.html', studies=json.dumps(studies), allowed_studies=get_researcher_allowed_studies(), is_admin=researcher_is_an_admin()) name = request.form.get('name', '') encryption_key = request.form.get('encryption_key', '') is_test = request.form.get( 'is_test') == 'true' # 'true' -> True, 'false' -> False assert len(name) <= 2**16, "safety check on new study name failed" try: study = Study.create_with_object_id(name=name, encryption_key=encryption_key, is_test=is_test) copy_existing_study_if_asked_to(study) flash('Successfully created study {}.'.format(name), 'success') return redirect('/device_settings/{:d}'.format(study.pk)) except ValidationError as ve: for field, message in ve.message_dict.iteritems(): flash('{}: {}'.format(field, message[0]), 'danger') return redirect('/create_study')
def patient_fields(study_id, patient_id=None): try: patient = Participant.objects.get(pk=patient_id) except Participant.DoesNotExist: return abort(404) patient.values_dict = { tag.field.field_name: tag.value for tag in patient.field_values.all() } study = patient.study if request.method == 'GET': return render_template( 'view_patient_custom_field_values.html', fields=study.fields.all(), study=study, patient=patient, allowed_studies=get_researcher_allowed_studies(), is_admin=researcher_is_an_admin(), ) fields = list(study.fields.values_list('field_name', flat=True)) for key, value in request.values.iteritems(): if key in fields: pfv, created = ParticipantFieldValue.objects.get_or_create( participant=patient, field=StudyField.objects.get(study=study, field_name=key)) pfv.value = value pfv.save() return redirect('/view_study/{:d}'.format(study.id))
def device_settings(study_id=None): study = Study.objects.get(pk=study_id) researcher = get_session_researcher() readonly = True if not researcher.check_study_admin(study_id) and not researcher.site_admin else False # if read only.... if request.method == 'GET': return render_template( "device_settings.html", study=study.as_native_python(), settings=study.get_study_device_settings().as_native_python(), readonly=readonly, allowed_studies=get_researcher_allowed_studies(), is_admin=researcher_is_an_admin() ) if readonly: abort(403) settings = study.get_study_device_settings() params = {k:v for k,v in request.values.items() if not k.startswith("consent_section")} consent_sections = {k: v for k, v in request.values.items() if k.startswith("consent_section")} params = checkbox_to_boolean(CHECKBOX_TOGGLES, params) params = string_to_int(TIMER_VALUES, params) # the ios consent sections are a json field but the frontend returns something weird, # see the documentation in unflatten_consent_sections for details params["consent_sections"] = json.dumps(unflatten_consent_sections(consent_sections)) settings.update(**params) return redirect('/edit_study/{:d}'.format(study.id))
def edit_study(study_id=None): # get the data points for display for all researchers in this study query = Researcher.filter_alphabetical(study_relations__study_id=study_id).values_list( "id", "username", "study_relations__relationship", "site_admin" ) # transform raw query data as needed listed_researchers = [] for pk, username, relationship, site_admin in query: listed_researchers.append(( pk, username, "Site Admin" if site_admin else relationship.replace("_", " ").title(), site_admin )) return render_template( 'edit_study.html', study=Study.objects.get(pk=study_id), administerable_researchers=get_administerable_researchers(), allowed_studies=get_researcher_allowed_studies(), listed_researchers=listed_researchers, is_admin=researcher_is_an_admin(), redirect_url='/edit_study/{:s}'.format(study_id), session_researcher=get_session_researcher(), )
def download_page(): return render_template( "download_landing_page.html", is_admin=researcher_is_an_admin(), allowed_studies=get_researcher_allowed_studies(), domain_name=DOMAIN_NAME, )
def manage_studies(): return render_template('manage_studies.html', studies=json.dumps([ study.as_native_python() for study in get_administerable_studies() ]), allowed_studies=get_researcher_allowed_studies(), is_admin=researcher_is_an_admin())
def view_study_data_pipeline(study_id=None): study = Study.objects.get(pk=study_id) return render_template( 'data-pipeline.html', study=study, allowed_studies=get_researcher_allowed_studies(), is_admin=researcher_is_an_admin(), )
def edit_study(study_id=None): return render_template( 'edit_study.html', study=Study.objects.get(pk=study_id), all_researchers=get_administerable_researchers(), allowed_studies=get_researcher_allowed_studies(), is_admin=researcher_is_an_admin(), redirect_url='/edit_study/{:s}'.format(study_id), )
def edit_researcher(researcher_pk): session_researcher = get_session_researcher() edit_researcher = Researcher.objects.get(pk=researcher_pk) # site admins can edit study admins, but not other site admins. # (users do not edit their own passwords on this page.) editable_password = ( not edit_researcher.username == get_session_researcher().username and not edit_researcher.site_admin ) # if the session researcher is not a site admin then we need to restrict password editing # to only researchers that are not study_admins anywhere. if not session_researcher.site_admin: editable_password = editable_password and not edit_researcher.is_study_admin() # edit_study_info is a list of tuples of (study relationship, whether that study is editable by # the current session admin, and the study itself.) visible_studies = session_researcher.get_visible_studies_by_name() if edit_researcher.site_admin: # if the session admin is a site admin then we can skip the complex logic edit_study_info = [("Site Admin", True, study) for study in visible_studies] else: # When the session admin is just a study admin then we need to determine if the study that # the session admin can see is also one they are an admin on so we can display buttons. administerable_studies = set(get_administerable_studies_by_name().values_list("pk", flat=True)) # We need the overlap of the edit_researcher studies with the studies visible to the session # admin, and we need those relationships for display purposes on the page. edit_study_relationship_map = { study_id: relationship.replace("_", " ").title() for study_id, relationship in edit_researcher.study_relations.filter(study__in=visible_studies).values_list("study_id", "relationship") } # get the relevant studies, populate with relationship, editability, and the study. edit_study_info = [] for study in visible_studies.filter(pk__in=edit_study_relationship_map.keys()): edit_study_info.append(( edit_study_relationship_map[study.id], study.id in administerable_studies, study, )) return render_template( 'edit_researcher.html', edit_researcher=edit_researcher, edit_study_info=edit_study_info, all_studies=get_administerable_studies_by_name(), # this is all the studies administerable by the user allowed_studies=get_researcher_allowed_studies(), editable_password=editable_password, is_admin=researcher_is_an_admin(), redirect_url='/edit_researcher/{:s}'.format(researcher_pk), session_researcher=session_researcher, is_self=edit_researcher.id==session_researcher.id, )
def manage_researchers(): researcher_list = [] # get the study names that each user has access to for researcher in get_administerable_researchers(): allowed_studies = list(Study.get_all_studies_by_name().filter( study_relations__researcher=researcher).values_list('name', flat=True)) researcher_list.append( (researcher.as_native_python(), allowed_studies)) return render_template('manage_researchers.html', admins=json.dumps(researcher_list), allowed_studies=get_researcher_allowed_studies(), is_admin=researcher_is_an_admin())
def edit_researcher(researcher_pk): edit_researcher = Researcher.objects.get(pk=researcher_pk) is_session_researcher = edit_researcher.username == get_session_researcher( ).username, return render_template( 'edit_researcher.html', admin=edit_researcher, current_studies=Study.get_all_studies_by_name().filter( study_relations__researcher=edit_researcher), all_studies=get_administerable_studies(), allowed_studies=get_researcher_allowed_studies(), is_session_researcher=is_session_researcher, is_admin=researcher_is_an_admin(), redirect_url='/edit_researcher/{:s}'.format(researcher_pk), )
def render_edit_survey(survey_id=None): try: survey = Survey.objects.get(pk=survey_id) except Survey.DoesNotExist: return abort(404) s = survey.as_native_python() study = survey.study return render_template( 'edit_survey.html', survey=survey.as_native_python(), study=study, allowed_studies=get_researcher_allowed_studies(), is_admin=researcher_is_an_admin(), domain_name=DOMAIN_NAME, # used in a Javascript alert, see survey-editor.js )
def dashboard_page(study_id): study = get_study_or_404(study_id) """ information for the general dashboard view for a study""" participants = list( Participant.objects.filter(study=study_id).values_list("patient_id", flat=True)) return render_template( 'dashboard/dashboard.html', study=study, participants=participants, study_id=study_id, data_stream_dict=complete_data_stream_dict, allowed_studies=get_researcher_allowed_studies(), is_admin=researcher_is_an_admin(), page_location='dashboard_landing', )
def data_api_web_form_page(): researcher = Researcher.objects.get(username=session[SESSION_NAME]) warn_researcher_if_hasnt_yet_generated_access_key(researcher) allowed_studies = get_researcher_allowed_studies_as_query_set() # dict of {study ids : list of user ids} users_by_study = { study.pk: [user.patient_id for user in study.participants.all()] for study in allowed_studies } return render_template( "data_api_web_form.html", allowed_studies=get_researcher_allowed_studies(), users_by_study=json.dumps(users_by_study), ALL_DATA_STREAMS=ALL_DATA_STREAMS, is_admin=researcher_is_an_admin() )
def study_fields(study_id=None): study = Study.objects.get(pk=study_id) if request.method == 'GET': return render_template( 'study_custom_fields.html', study=study, fields=study.fields.all(), allowed_studies=get_researcher_allowed_studies(), is_admin=researcher_is_an_admin(), ) new_field = request.values.get('new_field', None) if new_field: StudyField.objects.get_or_create(study=study, field_name=new_field) return redirect('/study_fields/{:d}'.format(study.id))
def manage_researchers(): # get the study names that each user has access to, but only those that the current admin also # has access to. session_ids = get_session_researcher_study_ids() researcher_list = [] for researcher in get_administerable_researchers(): allowed_studies = Study.get_all_studies_by_name().filter( study_relations__researcher=researcher, study_relations__study__in=session_ids, ).values_list('name', flat=True) researcher_list.append((researcher.as_native_python(), list(allowed_studies))) return render_template( 'manage_researchers.html', admins=json.dumps(researcher_list), allowed_studies=get_researcher_allowed_studies(), is_admin=researcher_is_an_admin() )
def create_new_researcher(): if request.method == 'GET': return render_template( 'create_new_researcher.html', allowed_studies=get_researcher_allowed_studies(), is_admin=researcher_is_an_admin() ) # Drop any whitespace or special characters from the username username = ''.join(e for e in request.form.get('admin_id', '') if e.isalnum()) password = request.form.get('password', '') if Researcher.objects.filter(username=username).exists(): flash("There is already a researcher with username " + username, 'danger') return redirect('/create_new_researcher') else: researcher = Researcher.create_with_password(username, password) return redirect('/edit_researcher/{:d}'.format(researcher.pk))
def study_fields(study_id=None): study = Study.objects.get(pk=study_id) researcher = get_session_researcher() readonly = True if not researcher.check_study_admin(study_id) and not researcher.site_admin else False if request.method == 'GET': return render_template( 'study_custom_fields.html', study=study, fields=study.fields.all(), readonly=readonly, allowed_studies=get_researcher_allowed_studies(), is_admin=researcher_is_an_admin(), ) if readonly: abort(403) new_field = request.values.get('new_field', None) if new_field: StudyField.objects.get_or_create(study=study, field_name=new_field) return redirect('/study_fields/{:d}'.format(study.id))
def get_data_for_dashboard_datastream_display(study_id, data_stream): """ parses information for the data stream dashboard view GET and POST requests""" # left the post and get requests in the same function because the body of the get request relies on the variables # set in the post request if a post request is sent --thus if a post request is sent we don't want all of the get # request running study = get_study_or_404(study_id) # -------------------------- for a POST request -------------------------------------- if request.method == "POST": color_low_range, color_high_range, all_flags_list = set_default_settings_post_request( study, data_stream) if color_low_range == 0 and color_high_range == 0: show_color = "false" else: show_color = "true" # ------------------ below is for a GET request - POST requests will ALSO run this code! ------------------------ else: color_low_range, color_high_range, show_color = extract_range_args_from_request( ) all_flags_list = extract_flag_args_from_request() default_filters = "" if DashboardColorSetting.objects.filter(data_type=data_stream, study=study).exists(): settings = DashboardColorSetting.objects.get(data_type=data_stream, study=study) default_filters = DashboardColorSetting.get_dashboard_color_settings( settings) # -------------------------------- dealing with color settings ------------------------------------------------- # test if there are default settings saved, # and if there are, test if the default filters should be used or if the user has overridden them if default_filters != "": inflection_info = default_filters["inflections"] if all_flags_list == [] and color_high_range is None and color_low_range is None: # since none of the filters are set, parse default filters to pass in the default settings # set the values for gradient filter # backend: color_range_min, color_range_max --> frontend: color_low_range, color_high_range # the above is consistent throughout the back and front ends if settings.gradient_exists(): gradient_info = default_filters["gradient"] color_low_range = gradient_info["color_range_min"] color_high_range = gradient_info["color_range_max"] show_color = "true" else: color_high_range = 0 color_low_range = 0 show_color = "false" # set the values for the flag/inflection filter*s* # the html is expecting a list of lists for the flags [[operator, value], ... ] all_flags_list = [] for flag_info in inflection_info: single_flag = [ flag_info["operator"].encode("utf-8"), flag_info["inflection_point"] ] all_flags_list.append(single_flag) # change the url params from jinja t/f to python understood T/F if show_color == "true": show_color = True elif show_color == "false": show_color = False # ----------------------------------- general data fetching -------------------------------------------- start, end = extract_date_args_from_request() participant_objects = Participant.objects.filter( study=study_id).order_by("patient_id") unique_dates = [] next_url = "" past_url = "" byte_streams = {} data_exists = None # --------------------- decide whether data is in Processed DB or Bytes DB ----------------------------- if data_stream in ALL_DATA_STREAMS: first_day, last_day = dashboard_chunkregistry_date_query( study_id, data_stream) if first_day is not None: stream_data = OrderedDict( (participant.patient_id, dashboard_chunkregistry_query(participant.id, data_stream=data_stream)) for participant in participant_objects) unique_dates, _, _ = get_unique_dates(start, end, first_day, last_day) next_url, past_url = create_next_past_urls(first_day, last_day, start=start, end=end) # get the byte streams per date for each patient for a specific data stream for those dates byte_streams = OrderedDict((participant.patient_id, [ get_bytes_participant_match( stream_data[participant.patient_id], date) for date in unique_dates ]) for participant in participant_objects) # check if there is data to display data_exists = len([ data for patient in byte_streams for data in byte_streams[patient] if data is not None ]) > 0 else: start, end = extract_date_args_from_request() first_day, last_day, stream_data = parse_processed_data( study_id, participant_objects, data_stream) if first_day is not None: unique_dates, _, _ = get_unique_dates(start, end, first_day, last_day) next_url, past_url = create_next_past_urls(first_day, last_day, start=start, end=end) # get the byte streams per date for each patient for a specific data stream for those dates byte_streams = OrderedDict((participant.patient_id, [ get_bytes_processed_data_match( stream_data[participant.patient_id], date) for date in unique_dates ]) for participant in participant_objects) # check if there is data to display data_exists = len([ data for patient in byte_streams for data in byte_streams[patient] if data is not None ]) > 0 # ---------------------------------- base case if there is no data ------------------------------------------ if first_day is None or (not data_exists and past_url == ""): unique_dates = [] next_url = "" past_url = "" byte_streams = {} return render_template( 'dashboard/data_stream_dashboard.html', study=study, data_stream=complete_data_stream_dict.get(data_stream), times=unique_dates, byte_streams=byte_streams, base_next_url=next_url, base_past_url=past_url, study_id=study_id, data_stream_dict=complete_data_stream_dict, color_low_range=color_low_range, color_high_range=color_high_range, first_day=first_day, last_day=last_day, show_color=show_color, all_flags_list=all_flags_list, allowed_studies=get_researcher_allowed_studies(), is_admin=researcher_is_an_admin(), page_location='dashboard_data', )
def get_data_for_dashboard_patient_display(study_id, patient_id): """ parses data to be displayed for the singular participant dashboard view """ study = get_study_or_404(study_id) participant = get_participant(patient_id, study_id) start, end = extract_date_args_from_request() chunks = dashboard_chunkregistry_query(participant.id) patient_ids = list( Participant.objects.filter(study=study_id).exclude( patient_id=patient_id).values_list("patient_id", flat=True)) # ----------------- dates for bytes data streams ----------------------- if chunks: first_day, last_day = dashboard_chunkregistry_date_query(study_id) _, first_date_data_entry, last_date_data_entry = \ get_unique_dates(start, end, first_day, last_day, chunks) # --------------- dates for processed data streams ------------------- # all_data is a list of dicts [{"time_bin": , "stream": , "processed_data": }...] processed_first_date_data_entry, processed_last_date_data_entry, all_data = parse_patient_processed_data( study_id, participant) # ------- decide the first date of data entry from processed AND bytes data as well as put the data together ------ # but only if there are both processed and bytes data if chunks and all_data: if (processed_first_date_data_entry - first_date_data_entry).days < 0: first_date_data_entry = processed_first_date_data_entry if (processed_last_date_data_entry - last_date_data_entry).days < 0: last_date_data_entry = processed_last_date_data_entry if all_data and not chunks: first_date_data_entry = processed_first_date_data_entry last_date_data_entry = processed_last_date_data_entry # ---------------------- get next/past urls and unique dates, as long as data has been entered ------------------- if chunks or all_data: next_url, past_url = create_next_past_urls(first_date_data_entry, last_date_data_entry, start=start, end=end) unique_dates, _, _ = get_unique_dates(start, end, first_date_data_entry, last_date_data_entry) # --------------------- get all the data using the correct unique dates from both data sets ---------------------- # get the byte data for the dates that have data collected in that week if all_data: processed_byte_streams = OrderedDict((stream, [ get_bytes_patient_processed_match(all_data, date, stream) for date in unique_dates ]) for stream in processed_data_stream_dict) if chunks: byte_streams = OrderedDict((stream, [ get_bytes_data_stream_match(chunks, date, stream) for date in unique_dates ]) for stream in ALL_DATA_STREAMS) if chunks and all_data: byte_streams.update(processed_byte_streams) elif all_data and not chunks: byte_streams = OrderedDict((stream, [None for date in unique_dates]) for stream in ALL_DATA_STREAMS) byte_streams.update(processed_byte_streams) elif chunks and not all_data: processed_byte_streams = OrderedDict( (stream, [None for date in unique_dates]) for stream in processed_data_stream_dict) byte_streams.update(processed_byte_streams) # ------------------------- edge case if no data has been entered ----------------------------------- else: byte_streams = {} unique_dates = [] next_url = "" past_url = "" first_date_data_entry = "" last_date_data_entry = "" return render_template( 'dashboard/participant_dashboard.html', study=study, patient_id=patient_id, participant=participant, times=unique_dates, byte_streams=byte_streams, next_url=next_url, past_url=past_url, patient_ids=patient_ids, study_id=study_id, first_date_data=first_date_data_entry, last_date_data=last_date_data_entry, data_stream_dict=complete_data_stream_dict, allowed_studies=get_researcher_allowed_studies(), is_admin=researcher_is_an_admin(), page_location='dashboard_patient', )
def manage_credentials(): return render_template('manage_credentials.html', allowed_studies=get_researcher_allowed_studies(), is_admin=researcher_is_an_admin())