Exemplo n.º 1
0
def export(request, name):
    """View for exporting sample series data in CSV or JSON format.  Thus, the
    return value is not an HTML response.  Note that you must also be allowed
    to see all *samples* in this sample series for the export.

    :param request: the current HTTP Request object
    :param name: the name of the sample series

    :type request: HttpRequest
    :type name: unicode

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    sample_series = get_object_or_404(models.SampleSeries, name=name)
    permissions.assert_can_view_sample_series(request.user, sample_series)
    for sample in sample_series.samples.all():
        permissions.assert_can_fully_view_sample(request.user, sample)

    data = sample_series.get_data_for_table_export()
    result = utils.table_export(request, data, _("sample"))
    if isinstance(result, tuple):
        column_groups_form, columns_form, table, switch_row_forms, old_data_form = result
    elif isinstance(result, HttpResponse):
        return result
    title = _("Table export for “{name}”").format(name=data.descriptive_name)
    return render(request, "samples/table_export.html", {"title": title, "column_groups": column_groups_form,
                                                         "columns": columns_form,
                                                         "rows": list(zip(table, switch_row_forms)) if table else None,
                                                         "old_data": old_data_form,
                                                         "backlink": request.GET.get("next", "")})
Exemplo n.º 2
0
def export(request, name):
    """View for exporting sample series data in CSV or JSON format.  Thus, the
    return value is not an HTML response.  Note that you must also be allowed
    to see all *samples* in this sample series for the export.

    :param request: the current HTTP Request object
    :param name: the name of the sample series

    :type request: HttpRequest
    :type name: unicode

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    sample_series = get_object_or_404(models.SampleSeries, name=name)
    permissions.assert_can_view_sample_series(request.user, sample_series)
    for sample in sample_series.samples.all():
        permissions.assert_can_fully_view_sample(request.user, sample)

    data = sample_series.get_data_for_table_export()
    result = utils.table_export(request, data, _("sample"))
    if isinstance(result, tuple):
        column_groups_form, columns_form, table, switch_row_forms, old_data_form = result
    elif isinstance(result, HttpResponse):
        return result
    title = _("Table export for “{name}”").format(name=data.descriptive_name)
    return render(request, "samples/table_export.html", {"title": title, "column_groups": column_groups_form,
                                                         "columns": columns_form,
                                                         "rows": list(zip(table, switch_row_forms)) if table else None,
                                                         "old_data": old_data_form,
                                                         "backlink": request.GET.get("next", "")})
Exemplo n.º 3
0
def get_matching_solarsimulator_measurement(request, sample_id, irradiation,
                                            cell_position, date):
    """Finds the solarsimulator measurement which is best suited for the given
    data file.  This view is to solve the problem that for non-standard-Jülich
    cell layouts, many single data files must be merged into one solarsimulator
    measurements.  When importing them one by one, one has to find the already
    existing measurement to which they must be added.  This is done by this
    view.

    It returns the ID of the measurement, or ``None`` if none was found.

    :param request: the HTTP request object
    :param sample_id: the ID of the sample which was measured
    :param irradiation: the irradiation (AM1.5, BG7 etc) which was used
    :param cell_position: the position of the cell on the layout; don't mix it
        up with the *index* of the cell, which is the number used in the
        Solarsimulator datafile in the first column
    :param date: the day (not the time) of the measurement in YYYY-MM-DD format

    :type request: HttpRequest
    :type sample_id: unicode
    :type irradiation: unicode
    :type cell_position: unicode
    :type date: unicode

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    try:
        filepath = request.GET["filepath"]
    except KeyError:
        raise JSONRequestException(3, '"filepath" missing')
    try:
        return respond_in_json(
            _get_solarsimulator_measurement_by_filepath(
                filepath, request.user))
    except Http404:
        sample = get_object_or_404(models.Sample, id=sample_id)
        start_date = django.utils.timezone.make_aware(
            datetime.datetime.strptime(date, "%Y-%m-%d"))
        end_date = start_date + datetime.timedelta(days=1)
        matching_measurements = institute.models.SolarsimulatorMeasurement.objects.filter(
            samples__id=sample_id, irradiation=irradiation, timestamp__gte=start_date, timestamp__lt=end_date). \
            exclude(cells__position=cell_position).order_by("timestamp")
        if matching_measurements.exists():
            solarsimulator_measurement = matching_measurements[0]
            permissions.assert_can_fully_view_sample(request.user, sample)
            permissions.assert_can_view_physical_process(
                request.user, solarsimulator_measurement)
            return respond_in_json(solarsimulator_measurement.id)
        else:
            return respond_in_json(None)
Exemplo n.º 4
0
def get_matching_solarsimulator_measurement(request, sample_id, irradiation, cell_position, date):
    """Finds the solarsimulator measurement which is best suited for the given
    data file.  This view is to solve the problem that for non-standard-Jülich
    cell layouts, many single data files must be merged into one solarsimulator
    measurements.  When importing them one by one, one has to find the already
    existing measurement to which they must be added.  This is done by this
    view.

    It returns the ID of the measurement, or ``None`` if none was found.

    :param request: the HTTP request object
    :param sample_id: the ID of the sample which was measured
    :param irradiation: the irradiation (AM1.5, BG7 etc) which was used
    :param cell_position: the position of the cell on the layout; don't mix it
        up with the *index* of the cell, which is the number used in the
        Solarsimulator datafile in the first column
    :param date: the day (not the time) of the measurement in YYYY-MM-DD format

    :type request: HttpRequest
    :type sample_id: unicode
    :type irradiation: unicode
    :type cell_position: unicode
    :type date: unicode

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    try:
        filepath = request.GET["filepath"]
    except KeyError:
        raise JSONRequestException(3, '"filepath" missing')
    try:
        return respond_in_json(_get_solarsimulator_measurement_by_filepath(filepath, request.user))
    except Http404:
        sample = get_object_or_404(models.Sample, id=sample_id)
        start_date = datetime.datetime.strptime(date, "%Y-%m-%d")
        end_date = start_date + datetime.timedelta(days=1)
        matching_measurements = institute.models.SolarsimulatorMeasurement.objects.filter(
            samples__id=sample_id, irradiation=irradiation, timestamp__gte=start_date, timestamp__lt=end_date). \
            exclude(cells__position=cell_position).order_by("timestamp")
        if matching_measurements.exists():
            solarsimulator_measurement = matching_measurements[0]
            permissions.assert_can_fully_view_sample(request.user, sample)
            permissions.assert_can_view_physical_process(request.user, solarsimulator_measurement)
            return respond_in_json(solarsimulator_measurement.id)
        else:
            return respond_in_json(None)
Exemplo n.º 5
0
def lookup_sample(sample_name, user, with_clearance=False):
    """Looks up the ``sample_name`` in the database (also among the aliases),
    and returns that sample if it was found *and* the current user is allowed
    to view it.  Shortened provisional names like “\*2” are also found.  If
    nothing is found or the permissions are not sufficient, it raises an
    exception.

    :param sample_name: name of the sample
    :param user: the currently logged-in user
    :param with_clearance: whether also clearances should be serached for and
        returned

    :type sample_name: unicode
    :type user: django.contrib.auth.models.User
    :type with_clearance: bool

    :return:
      the single found sample; or the sample and the clearance instance if this
      is necessary to view the sample and ``with_clearance=True``

    :rtype: `samples.models.Sample` or `samples.models.Sample`,
      `samples.models.Clearance`

    :raises Http404: if the sample name could not be found
    :raises AmbiguityException: if more than one matching alias was found
    :raises samples.permissions.PermissionError: if the user is not allowed to
      view the sample
    """
    name_format, match = sample_name_format(sample_name,
                                            with_match_object=True)
    if name_format == "provisional":
        sample_name = "*{0:05}".format(int(match.group("id")))
    sample = get_sample(sample_name)
    if not sample:
        raise Http404(
            "Sample {name} could not be found (neither as an alias).".format(
                name=sample_name))
    if isinstance(sample, list):
        raise AmbiguityException(sample_name, sample)
    if with_clearance:
        clearance = permissions.get_sample_clearance(user, sample)
        return sample, clearance
    else:
        permissions.assert_can_fully_view_sample(user, sample)
        return sample
Exemplo n.º 6
0
def lookup_sample(sample_name, user, with_clearance=False):
    """Looks up the ``sample_name`` in the database (also among the aliases),
    and returns that sample if it was found *and* the current user is allowed
    to view it.  Shortened provisional names like “\*2” are also found.  If
    nothing is found or the permissions are not sufficient, it raises an
    exception.

    :param sample_name: name of the sample
    :param user: the currently logged-in user
    :param with_clearance: whether also clearances should be serached for and
        returned

    :type sample_name: unicode
    :type user: django.contrib.auth.models.User
    :type with_clearance: bool

    :return:
      the single found sample; or the sample and the clearance instance if this
      is necessary to view the sample and ``with_clearance=True``

    :rtype: `samples.models.Sample` or `samples.models.Sample`,
      `samples.models.Clearance`

    :raises Http404: if the sample name could not be found
    :raises AmbiguityException: if more than one matching alias was found
    :raises samples.permissions.PermissionError: if the user is not allowed to
      view the sample
    """
    name_format, match = sample_names.sample_name_format(sample_name, with_match_object=True)
    if name_format == "provisional":
        sample_name = "*{0:05}".format(int(match.group("id")))
    sample = sample_names.get_sample(sample_name)
    if not sample:
        raise Http404("Sample {name} could not be found (neither as an alias).".format(name=sample_name))
    if isinstance(sample, list):
        raise AmbiguityException(sample_name, sample)
    if with_clearance:
        clearance = permissions.get_sample_clearance(user, sample)
        return sample, clearance
    else:
        permissions.assert_can_fully_view_sample(user, sample)
        return sample
Exemplo n.º 7
0
def get_current_structuring(request, sample_id):
    """Find the structuring process which is active for the given sample at a
    given timestamp.  The “``timestamp``” is an optional parameter in the query
    string in the format ``YYYY-MM-YY HH:MM:SS``.  If given, find the latest
    structuring process before that timestamp.  Otherwise, find the lastest
    structuring of the sample.  Typically, the timestamp is the timestamp of
    the process which needs the structuring, e.g. a solarsimulator measurement.

    It returns the ID of the structuring process, or ``None`` if none was
    found.

    :param request: the HTTP request object
    :param sample_id: the ID of the sample

    :type request: HttpRequest
    :type sample_id: unicode

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    sample = get_object_or_404(models.Sample, id=sample_id)
    try:
        timestamp = django.utils.timezone.make_aware(
            datetime.datetime.strptime(
                request.GET["timestamp"].partition(".")[0],
                "%Y-%m-%d %H:%M:%S"))
    except KeyError:
        timestamp = None
    except ValueError:
        raise JSONRequestException(5, '"timestamp" has invalid format')
    try:
        structuring = layouts.get_current_structuring(sample, timestamp)
    except layouts.NoStructuringFound:
        result = None
    else:
        permissions.assert_can_fully_view_sample(request.user, sample)
        permissions.assert_can_view_physical_process(request.user, structuring)
        result = structuring.id
    return respond_in_json(result)
Exemplo n.º 8
0
def get_current_structuring(request, sample_id):
    """Find the structuring process which is active for the given sample at a
    given timestamp.  The “``timestamp``” is an optional parameter in the query
    string in the format ``YYYY-MM-YY HH:MM:SS``.  If given, find the latest
    structuring process before that timestamp.  Otherwise, find the lastest
    structuring of the sample.  Typically, the timestamp is the timestamp of
    the process which needs the structuring, e.g. a solarsimulator measurement.

    It returns the ID of the structuring process, or ``None`` if none was
    found.

    :param request: the HTTP request object
    :param sample_id: the ID of the sample

    :type request: HttpRequest
    :type sample_id: unicode

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    sample = get_object_or_404(models.Sample, id=sample_id)
    try:
        timestamp = datetime.datetime.strptime(request.GET["timestamp"].partition(".")[0], "%Y-%m-%d %H:%M:%S")
    except KeyError:
        timestamp = None
    except ValueError:
        raise JSONRequestException(5, '"timestamp" has invalid format')
    try:
        structuring = layouts.get_current_structuring(sample, timestamp)
    except layouts.NoStructuringFound:
        result = None
    else:
        permissions.assert_can_fully_view_sample(request.user, sample)
        permissions.assert_can_view_physical_process(request.user, structuring)
        result = structuring.id
    return respond_in_json(result)
Exemplo n.º 9
0
def is_referentially_valid(current_user, my_samples_form, action_form):
    """Test whether all forms are consistent with each other and with the
    database.  For example, you must not change data for samples for which
    you're not the currently responsible person.

    :param current_user: the currently logged-in user
    :param my_samples_form: the form with the selected “My Samples”
    :param action_form: the form with the things to be done with the selected
        samples.

    :type current_user: django.contrib.auth.models.User
    :type my_samples_form: `MySamplesForm`
    :type action_form: `ActionForm`

    :return:
      whether all forms are consistent with each other and the database

    :rtype: bool
    """
    referentially_valid = True
    if my_samples_form.is_valid() and action_form.is_valid():
        action_data = action_form.cleaned_data
        if not current_user.is_superuser:
            if action_data["new_currently_responsible_person"] or action_data["new_topic"] or \
                        action_data["new_current_location"]:
                try:
                    for sample in my_samples_form.cleaned_data["samples"]:
                        permissions.assert_can_edit_sample(
                            current_user, sample)
                except permissions.PermissionError:
                    action_form.add_error(
                        None,
                        ValidationError(_(
                            "You must be the currently responsible person for samples you'd like to change "
                            "or the topic manager of the samples topics."),
                                        code="forbidden"))
                    referentially_valid = False
        if action_data["clearance"] is None and action_data["copy_to_user"]:
            try:
                action_data["copy_to_user"] = list(action_data["copy_to_user"])
                action_data["copy_to_user"].remove(
                    action_data["new_currently_responsible_person"])
            except ValueError:
                pass
            try:
                for sample in my_samples_form.cleaned_data["samples"]:
                    for user in action_data["copy_to_user"]:
                        permissions.assert_can_fully_view_sample(user, sample)
            except permissions.PermissionError:
                action_form.add_error(
                    "clearance",
                    ValidationError(_(
                        "If you copy samples over to another person who cannot fully view one of the "
                        "samples, you must select a clearance option."),
                                    code="required"))
                referentially_valid = False
        if action_data.get("clearance") is not None:
            failed_samples = []
            for sample in my_samples_form.cleaned_data["samples"]:
                try:
                    permissions.get_sample_clearance(current_user, sample)
                except permissions.PermissionError:
                    failed_samples.append(sample)
            if failed_samples:
                my_samples_form.add_error(
                    "samples",
                    ValidationError(_(
                        "You cannot grant clearances for the following samples: %(failed_samples)s"
                    ),
                                    params={
                                        "failed_samples":
                                        format_enumeration(failed_samples)
                                    },
                                    code="forbidden"))
                referentially_valid = False
    return referentially_valid
Exemplo n.º 10
0
def is_referentially_valid(current_user, my_samples_form, action_form):
    """Test whether all forms are consistent with each other and with the
    database.  For example, you must not change data for samples for which
    you're not the currently responsible person.

    :param current_user: the currently logged-in user
    :param my_samples_form: the form with the selected “My Samples”
    :param action_form: the form with the things to be done with the selected
        samples.

    :type current_user: django.contrib.auth.models.User
    :type my_samples_form: `MySamplesForm`
    :type action_form: `ActionForm`

    :return:
      whether all forms are consistent with each other and the database

    :rtype: bool
    """
    referentially_valid = True
    if my_samples_form.is_valid() and action_form.is_valid():
        action_data = action_form.cleaned_data
        if not current_user.is_superuser:
            if action_data["new_currently_responsible_person"] or action_data["new_topic"] or \
                        action_data["new_current_location"]:
                try:
                    for sample in my_samples_form.cleaned_data["samples"]:
                        permissions.assert_can_edit_sample(current_user, sample)
                except permissions.PermissionError:
                    action_form.add_error(None, ValidationError(
                        _("You must be the currently responsible person for samples you'd like to change "
                          "or the topic manager of the samples topics."), code="forbidden"))
                    referentially_valid = False
        if action_data["clearance"] is None and action_data["copy_to_user"]:
            try:
                action_data["copy_to_user"] = list(action_data["copy_to_user"])
                action_data["copy_to_user"].remove(action_data["new_currently_responsible_person"])
            except ValueError:
                pass
            try:
                for sample in my_samples_form.cleaned_data["samples"]:
                    for user in action_data["copy_to_user"]:
                        permissions.assert_can_fully_view_sample(user, sample)
            except permissions.PermissionError:
                action_form.add_error("clearance", ValidationError(
                    _("If you copy samples over to another person who cannot fully view one of the "
                      "samples, you must select a clearance option."), code="required"))
                referentially_valid = False
        if action_data.get("clearance") is not None:
            failed_samples = []
            for sample in my_samples_form.cleaned_data["samples"]:
                try:
                    permissions.get_sample_clearance(current_user, sample)
                except permissions.PermissionError:
                    failed_samples.append(sample)
            if failed_samples:
                my_samples_form.add_error("samples", ValidationError(
                    _("You cannot grant clearances for the following samples: %(failed_samples)s"),
                    params={"failed_samples": format_enumeration(failed_samples)}, code="forbidden"))
                referentially_valid = False
    return referentially_valid