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", "")})
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", "")})
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)
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)
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
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
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)
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)
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
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