def by_id(request, sample_id, path_suffix): """Pure re-direct view in case a sample is accessed by ID instead of by name. It redirects to the URL with the name. The query string, if given, is passed, too. :param request: the current HTTP Request object :param sample_id: the numeric ID of the sample :param path_suffix: the trailing path, e.g. ``"/split/"``; if you just view a sample, it is empty (or only the query string) :type request: HttpRequest :type sample_id: unicode :type path_suffix: unicode :return: the HTTP response object :rtype: HttpResponse """ sample = get_object_or_404(models.Sample, pk=utils.convert_id_to_int(sample_id)) if is_json_requested(request): # No redirect for the remote client. This also makes a POST request # possible. if path_suffix == "/edit/": return edit(request, sample.name) else: # FixMe: More path_suffixes should be tested return show(request, sample.name) # Necessary so that the sample's name isn't exposed through the URL permissions.get_sample_clearance(request.user, sample) query_string = request.META["QUERY_STRING"] or "" return HttpResponseSeeOther( django.core.urlresolvers.reverse("show_sample_by_name", kwargs={"sample_name": sample.name}) + path_suffix + ("?" + query_string if query_string else ""))
def show_stack(request, sample_id, thumbnail): """Shows a particular informal layer stack. Although its response is a bitmap rather than an HTML file, it is served by Django in order to enforce user permissions. :param request: the current HTTP Request object :param sample_id: the database ID of the sample :param thumbnail: whether we should deliver a thumbnail version :type request: HttpRequest :type process_id: unicode :type thumbnail: bool :return: the HTTP response object with the image :rtype: HttpResponse """ sample_details = get_object_or_404(models.SampleDetails, pk=utils.convert_id_to_int(sample_id)) sample = sample_details.sample permissions.get_sample_clearance(request.user, sample) if not sample_details.has_producible_stack_diagram(): raise Http404("No stack diagram available.") locations = sample_details.get_stack_diagram_locations() filepath = locations["thumbnail_file" if thumbnail else "diagram_file"] content = jb_common.utils.base.get_cached_file_content( filepath, partial(generate_stack, thumbnail, locations, sample, sample_details), timestamps=[sample.last_modified]) return jb_common.utils.base.static_response( content, None if thumbnail else "{0}_stack.pdf".format( defaultfilters.slugify(six.text_type(sample))), "image/png" if thumbnail else "application/pdf")
def show_stack(request, sample_id, thumbnail): """Shows a particular informal layer stack. Although its response is a bitmap rather than an HTML file, it is served by Django in order to enforce user permissions. :param request: the current HTTP Request object :param sample_id: the database ID of the sample :param thumbnail: whether we should deliver a thumbnail version :type request: HttpRequest :type process_id: unicode :type thumbnail: bool :return: the HTTP response object with the image :rtype: HttpResponse """ sample_details = get_object_or_404(models.SampleDetails, pk=utils.convert_id_to_int(sample_id)) sample = sample_details.sample permissions.get_sample_clearance(request.user, sample) if not sample_details.has_producible_stack_diagram(): raise Http404("No stack diagram available.") locations = sample_details.get_stack_diagram_locations() filepath = locations["thumbnail_file" if thumbnail else "diagram_file"] content = jb_common.utils.base.get_cached_file_content( filepath, partial(generate_stack, thumbnail, locations, sample, sample_details), timestamps=[sample.last_modified]) return jb_common.utils.base.static_response( content, None if thumbnail else "{0}_stack.pdf".format(defaultfilters.slugify(six.text_type(sample))), "image/png" if thumbnail else "application/pdf")
def show_stack(request, sample_id, thumbnail): """Shows a particular informal layer stack. Although its response is a bitmap rather than an HTML file, it is served by Django in order to enforce user permissions. :param request: the current HTTP Request object :param sample_id: the database ID of the sample :param thumbnail: whether we should deliver a thumbnail version :type request: HttpRequest :type process_id: unicode :type thumbnail: bool :return: the HTTP response object with the image :rtype: HttpResponse """ sample_details = get_object_or_404(models.SampleDetails, pk=utils.convert_id_to_int(sample_id)) sample = sample_details.sample permissions.get_sample_clearance(request.user, sample) if not sample_details.has_producible_stack_diagram(): raise Http404("No stack diagram available.") locations = sample_details.get_stack_diagram_locations() filepath = locations["thumbnail_file" if thumbnail else "diagram_file"] if jb_common.utils.base.is_update_necessary( filepath, timestamps=[sample.last_modified]): jb_common.utils.base.mkdirs(filepath) if not thumbnail or jb_common.utils.base.is_update_necessary( locations["diagram_file"], timestamps=[sample.last_modified]): jb_common.utils.base.mkdirs(locations["diagram_file"]) informal_stacks.generate_diagram( locations["diagram_file"], [ informal_stacks.Layer(layer) for layer in sample_details.informal_layers.all() ], six.text_type(sample), _("Layer stack of {0}").format(sample)) if thumbnail: subprocess.call([ "gs", "-q", "-dNOPAUSE", "-dBATCH", "-sDEVICE=pngalpha", "-r100", "-dEPSCrop", "-sOutputFile=" + locations["thumbnail_file"], locations["diagram_file"] ]) storage_changed.send(models.SampleDetails) return jb_common.utils.base.static_file_response( filepath, None if thumbnail else "{0}_stack.pdf".format( defaultfilters.slugify(six.text_type(sample))))
def show_stack(request, sample_id, thumbnail): """Shows a particular informal layer stack. Although its response is a bitmap rather than an HTML file, it is served by Django in order to enforce user permissions. :param request: the current HTTP Request object :param sample_id: the database ID of the sample :param thumbnail: whether we should deliver a thumbnail version :type request: HttpRequest :type process_id: unicode :type thumbnail: bool :return: the HTTP response object with the image :rtype: HttpResponse """ sample_details = get_object_or_404(models.SampleDetails, pk=utils.convert_id_to_int(sample_id)) sample = sample_details.sample permissions.get_sample_clearance(request.user, sample) if not sample_details.has_producible_stack_diagram(): raise Http404("No stack diagram available.") locations = sample_details.get_stack_diagram_locations() filepath = locations["thumbnail_file" if thumbnail else "diagram_file"] if jb_common.utils.base.is_update_necessary(filepath, timestamps=[sample.last_modified]): jb_common.utils.base.mkdirs(filepath) if not thumbnail or jb_common.utils.base.is_update_necessary(locations["diagram_file"], timestamps=[sample.last_modified]): jb_common.utils.base.mkdirs(locations["diagram_file"]) informal_stacks.generate_diagram( locations["diagram_file"], [informal_stacks.Layer(layer) for layer in sample_details.informal_layers.all()], six.text_type(sample), _("Layer stack of {0}").format(sample)) if thumbnail: subprocess.call(["gs", "-q", "-dNOPAUSE", "-dBATCH", "-sDEVICE=pngalpha", "-r100", "-dEPSCrop", "-sOutputFile=" + locations["thumbnail_file"], locations["diagram_file"]]) storage_changed.send(models.SampleDetails) return jb_common.utils.base.static_file_response( filepath, None if thumbnail else "{0}_stack.pdf".format(defaultfilters.slugify(six.text_type(sample))))
def printer_label(request, sample_id): """Generates a PDF for the label printer in 9×45 mm² format. It contains the name and the QR code of a sample. :param request: the current 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, pk=utils.convert_id_to_int(sample_id)) permissions.get_sample_clearance(request.user, sample) pdf_output = printer_labels.printer_label(sample) response = HttpResponse() response.write(pdf_output) response["Content-Type"] = "application/pdf" response["Content-Length"] = len(pdf_output) return response
def printer_label(request, sample_id): """Generates a PDF for the label printer in 9×45 mm² format. It contains the name and the QR code of a sample. :param request: the current 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, pk=utils.convert_id_to_int(sample_id)) permissions.get_sample_clearance(request.user, sample) pdf_output = printer_labels.printer_label(sample) response = HttpResponse() response.write(pdf_output) response["Content-Type"] = "application/pdf" response["Content-Length"] = len(pdf_output) return response
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 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