Exemple #1
0
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,
    )
Exemple #2
0
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))
Exemple #3
0
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 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))
Exemple #6
0
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 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 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())
Exemple #9
0
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())
Exemple #13
0
def choose_study():
    allowed_studies = get_researcher_allowed_studies_as_query_set()

    # If the admin is authorized to view exactly 1 study, redirect to that study
    if allowed_studies.count() == 1:
        return redirect('/view_study/{:d}'.format(
            allowed_studies.values_list('pk', flat=True).get()))

    # Otherwise, show the "Choose Study" page
    allowed_studies_json = Study.query_set_as_native_json(allowed_studies)
    return render_template('choose_study.html',
                           studies=allowed_studies_json,
                           allowed_studies=allowed_studies_json,
                           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),
    )
Exemple #15
0
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
    )
Exemple #16
0
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',
    )
Exemple #17
0
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))
Exemple #22
0
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',
    )
Exemple #23
0
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',
    )
Exemple #24
0
def manage_credentials():
    return render_template('manage_credentials.html',
                           allowed_studies=get_researcher_allowed_studies(),
                           is_admin=researcher_is_an_admin())