def get_folded_processes(request, sample_id): """Get all the IDs from the processes, who have to be folded. :param request: The current HTTP Request object. It must contain all the process IDs of the processes from the selected sample. :param sample_id: The sample ID represent the data sheet the user wants to see. :type request: HttpRequest :type sample_id: unicode :return: The process IDs of the processes, who have to be folded on the samples data sheet. :rtype: HttpResponse """ try: process_ids = [utils.convert_id_to_int(id_) for id_ in request.GET["process_ids"].split(",")] except KeyError: raise JSONRequestException(3, '"process_ids" missing') utils.convert_id_to_int(sample_id) folded_process_classes = ContentType.objects.filter(dont_show_to_user=request.user.samples_user_details) exceptional_processes_by_sample_id = json.loads(request.user.samples_user_details.folded_processes).get(sample_id, []) folded_process_ids = [] for process_id in process_ids: if _is_folded(process_id, folded_process_classes, exceptional_processes_by_sample_id, switch=False): folded_process_ids.append(process_id) return respond_in_json(folded_process_ids)
def show_layout(request, process_id, sample_id): sample = get_object_or_404(models.Sample, pk=utils.convert_id_to_int(sample_id)) process = get_object_or_404(models.Process, pk=utils.convert_id_to_int(process_id)).actual_instance png_filename = os.path.join("layouts", "{0}-{1}.png".format(process.id, sample.id)) content = jb_common.utils.base.get_cached_file_content( png_filename, partial(generate_layout, sample, process), timestamps=[sample.last_modified, process.last_modified]) return jb_common.utils.base.static_response(content, png_filename)
def get_folded_processes(request, sample_id): """Get all the IDs from the processes, who have to be folded. :param request: The current HTTP Request object. It must contain all the process IDs of the processes from the selected sample. :param sample_id: The sample ID represent the data sheet the user wants to see. :type request: HttpRequest :type sample_id: unicode :return: The process IDs of the processes, who have to be folded on the samples data sheet. :rtype: HttpResponse """ try: process_ids = [ utils.convert_id_to_int(id_) for id_ in request.GET["process_ids"].split(",") ] except KeyError: raise JSONRequestException(3, '"process_ids" missing') utils.convert_id_to_int(sample_id) folded_process_classes = ContentType.objects.filter( dont_show_to_user=request.user.samples_user_details) exceptional_processes_by_sample_id = json.loads( request.user.samples_user_details.folded_processes).get(sample_id, []) folded_process_ids = [] for process_id in process_ids: if _is_folded(process_id, folded_process_classes, exceptional_processes_by_sample_id, switch=False): folded_process_ids.append(process_id) return respond_in_json(folded_process_ids)
def add_sample(request): """Adds a new sample to the database. It is added without processes. This view can only be used by admin accounts. :param request: the current HTTP Request object; it must contain the sample data in the POST data. :return: The primary key of the created sample. ``False`` if something went wrong. It may return a 404 if the topic or the currently responsible person wasn't found. :rtype: HttpResponse """ try: name = request.POST["name"] current_location = request.POST["current_location"] currently_responsible_person = request.POST["currently_responsible_person"] purpose = request.POST.get("purpose", "") tags = request.POST.get("tags", "") topic = request.POST.get("topic") except KeyError as error: raise JSONRequestException(3, "'{}' parameter missing.".format(error.args[0])) if len(name) > 30: raise JSONRequestException(5, "The sample name is too long.") name_format = utils.sample_name_format(name) if name_format is None or \ not request.user.is_superuser and \ name_format not in settings.SAMPLE_NAME_FORMATS["provisional"].get("possible_renames", set()): raise JSONRequestException(5, "The sample name is invalid.") eligible_users = django.contrib.auth.models.User.objects.filter(is_active=True, jb_user_details__department__isnull=False) try: currently_responsible_person = eligible_users.get(pk=utils.convert_id_to_int(currently_responsible_person)) except django.contrib.auth.models.User.DoesNotExist: raise Http404("Currently reponsible user not found.") if topic: all_topics = Topic.objects.all() if request.user.is_superuser else \ Topic.objects.filter(Q(confidential=False) | Q(members=request.user)) try: topic = all_topics.get(pk=utils.convert_id_to_int(topic)) except Topic.DoesNotExist: raise Http404("Topic not found") try: sample = models.Sample.objects.create(name=name, current_location=current_location, currently_responsible_person=currently_responsible_person, purpose=purpose, tags=tags, topic=topic) # They will be shadowed anyway. Nevertheless, this action is an # emergency measure. Probably the samples the aliases point to should # be merged with the sample but this can't be decided automatically. models.SampleAlias.objects.filter(name=name).delete() except IntegrityError as error: error_message = "The sample with this data could not be added." if request.user.is_superuser: error_message += " {}".format(error) raise JSONRequestException(5, error_message) sample.watchers.add(request.user) return respond_in_json(sample.pk)
def show_layout(request, process_id, sample_id): sample = get_object_or_404(models.Sample, pk=utils.convert_id_to_int(sample_id)) process = get_object_or_404( models.Process, pk=utils.convert_id_to_int(process_id)).actual_instance png_filename = os.path.join("layouts", "{0}-{1}.png".format(process.id, sample.id)) content = jb_common.utils.base.get_cached_file_content( png_filename, partial(generate_layout, sample, process), timestamps=[sample.last_modified, process.last_modified]) return jb_common.utils.base.static_response(content, png_filename)
def add_alias(request): """Adds a new sample alias name to the database. This view can only be used by admin accounts. :param request: the current HTTP Request object; it must contain the sample's primary key and the alias name in the POST data. :type request: HttpRequest :return: ``True`` if it worked, ``False`` if something went wrong. It returns a 404 if the sample wasn't found. :rtype: HttpResponse """ if not request.user.is_superuser: return respond_in_json(False) try: sample_pk = request.POST["sample"] alias = request.POST["alias"] except KeyError: return respond_in_json(False) sample = get_object_or_404(models.Sample, pk=utils.convert_id_to_int(sample_pk)) try: models.models.SampleAlias.create(name=alias, sample=sample) except IntegrityError: # Alias already present return respond_in_json(False) return respond_in_json(True)
def show_thumbnail(request, process_id): """Shows the thumnail of a particular result image. Although its response is an image rather than an HTML file, it is served by Django in order to enforce user permissions. :param request: the current HTTP Request object :param process_id: the database ID of the result to show :type request: HttpRequest :type process_id: unicode :return: the HTTP response object with the thumbnail image :rtype: HttpResponse """ result = get_object_or_404(models.Result, pk=utils.convert_id_to_int(process_id)) permissions.assert_can_view_result_process(request.user, result) image_locations = result.get_image_locations() image_file = image_locations["image_file"] thumbnail_file = image_locations["thumbnail_file"] if is_update_necessary(thumbnail_file, [image_file]): mkdirs(thumbnail_file) subprocess.check_call([ "convert", image_file + ("[0]" if result.image_type == "pdf" else ""), "-resize", "{0}x{0}".format(settings.THUMBNAIL_WIDTH), thumbnail_file ]) storage_changed.send(models.Result) return static_file_response(thumbnail_file)
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 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 withdraw(request, id_): """This function withdraws a status message for good. Note that it withdraws it for all its connected process types. It is idempotent. :param request: the current HTTP Request object :param id_: the id of the message to be withdrawn :type request: HttpRequest :type id_: unicode :return: the HTTP response object :rtype: HttpResponse """ status_message = get_object_or_404(models.StatusMessage, withdrawn=False, pk=utils.convert_id_to_int(id_)) if request.user != status_message.operator: raise PermissionError( request.user, "You cannot withdraw status messages of another user.") status_message.withdrawn = True status_message.save() for process_class in status_message.process_classes.all(): utils.Reporter(request.user).report_withdrawn_status_message( process_class, status_message) return utils.successful_response( request, _("The status message was successfully withdrawn."), show)
def show_thumbnail(request, process_id): """Shows the thumnail of a particular result image. Although its response is an image rather than an HTML file, it is served by Django in order to enforce user permissions. :param request: the current HTTP Request object :param process_id: the database ID of the result to show :type request: HttpRequest :type process_id: unicode :return: the HTTP response object with the thumbnail image :rtype: HttpResponse """ result = get_object_or_404(models.Result, pk=utils.convert_id_to_int(process_id)) permissions.assert_can_view_result_process(request.user, result) image_locations = result.get_image_locations() image_file = image_locations["image_file"] thumbnail_file = image_locations["thumbnail_file"] if is_update_necessary(thumbnail_file, [image_file]): mkdirs(thumbnail_file) subprocess.check_call(["convert", image_file + ("[0]" if result.image_type == "pdf" else ""), "-resize", "{0}x{0}".format(settings.THUMBNAIL_WIDTH), thumbnail_file]) storage_changed.send(models.Result) return static_file_response(thumbnail_file)
def show(request, process_id): """Shows a particular result process. The main purpose of this view is to be able to visit a result directly from a feed entry about a new/edited result. :param request: the current HTTP Request object :param process_id: the database ID of the result to show :type request: HttpRequest :type process_id: unicode :return: the HTTP response object :rtype: HttpResponse """ result = get_object_or_404(models.Result, pk=utils.convert_id_to_int(process_id)) permissions.assert_can_view_result_process(request.user, result) if jb_common.utils.base.is_json_requested(request): return jb_common.utils.base.respond_in_json(result.get_data()) template_context = { "title": _("Result “{title}”").format(title=result.title), "result": result, "samples": result.samples.all(), "sample_series": result.sample_series.all() } template_context.update(utils.digest_process(result, request.user)) return render(request, "samples/show_single_result.html", template_context)
def edit(request, external_operator_id): """View for editing existing external operators. You can also give the operator initials here. :param request: the current HTTP Request object :param external_operator_id: the database ID for the external operator :type request: HttpRequest :type external_operator_id: unicode :return: the HTTP response object :rtype: HttpResponse """ external_operator = get_object_or_404(models.ExternalOperator, pk=utils.convert_id_to_int(external_operator_id)) permissions.assert_can_edit_external_operator(request.user, external_operator) if request.method == "POST": external_operator_form = EditExternalOperatorForm(request.user, request.POST, instance=external_operator) if external_operator_form.is_valid(): external_operator = external_operator_form.save() return utils.successful_response( request, _("The external operator “{operator}” was successfully changed.").format(operator=external_operator.name)) else: external_operator_form = EditExternalOperatorForm(request.user, instance=external_operator) initials_form = utils.InitialsForm(external_operator, initials_mandatory=True) return render(request, "samples/edit_external_operator.html", {"title": _("Edit external operator “{operator}”").format(operator=external_operator.name), "external_operator": external_operator_form, "initials": initials_form})
def export(request, process_id): """View for exporting result process data in CSV or JSON format. Thus, the return value is not an HTML response. :param request: the current HTTP Request object :param process_id: the database ID of the result to show :type request: HttpRequest :type process_id: unicode :return: the HTTP response object :rtype: HttpResponse """ result = get_object_or_404(models.Result, pk=utils.convert_id_to_int(process_id)) permissions.assert_can_view_result_process(request.user, result) data = result.get_data_for_table_export() # Translators: In a table result = utils.table_export(request, data, _("row")) 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 show(request, external_operator_id): """View for displaying existing external operators. Only users who are allowed to see all samples, and the current contact person are allowed to see it. :param request: the current HTTP Request object :param external_operator_id: the database ID for the external operator :type request: HttpRequest :type external_operator_id: unicode :return: the HTTP response object :rtype: HttpResponse """ external_operator = get_object_or_404(models.ExternalOperator, pk=utils.convert_id_to_int(external_operator_id)) contact_persons = external_operator.contact_persons.all() if permissions.has_permission_to_view_external_operator(request.user, external_operator): try: initials = external_operator.initials except models.Initials.DoesNotExist: initials = None title = _("External operator “{name}”").format(name=external_operator.name) else: title = _("Confidential operator #{number}").format(number=external_operator.pk) external_operator = None initials = None return render(request, "samples/show_external_operator.html", {"title": title, "external_operator": external_operator, "initials": initials, "contact_persons" : contact_persons, "can_edit": request.user in contact_persons})
def add_alias(request): """Adds a new sample alias name to the database. This view can only be used by admin accounts. :param request: the current HTTP Request object; it must contain the sample's primary key and the alias name in the POST data. :type request: HttpRequest :return: ``True`` if it worked, ``False`` if something went wrong. It returns a 404 if the sample wasn't found. :rtype: HttpResponse """ if not request.user.is_staff: return respond_in_json(False) try: sample_pk = request.POST["sample"] alias = request.POST["alias"] except KeyError: return respond_in_json(False) sample = get_object_or_404(models.Sample, pk=utils.convert_id_to_int(sample_pk)) try: models.models.SampleAlias.create(name=alias, sample=sample) except IntegrityError: # Alias already present return respond_in_json(False) return respond_in_json(True)
def show_layout(request, process_id, sample_id): sample = get_object_or_404(models.Sample, pk=utils.convert_id_to_int(sample_id)) process = get_object_or_404(models.Process, pk=utils.convert_id_to_int(process_id)).actual_instance pdf_filename = "/tmp/layouts_{0}_{1}.pdf".format(process.id, sample.id) jb_common.utils.base.mkdirs(pdf_filename) layout = layouts.get_layout(sample, process) if not layout: raise Http404("error") layout.generate_pdf(pdf_filename) png_filename = os.path.join(settings.CACHE_ROOT, "layouts", "{0}-{1}.png".format(process.id, sample.id)) jb_common.utils.base.mkdirs(png_filename) resolution = settings.THUMBNAIL_WIDTH / (layout.width / 72) subprocess.check_call(["gs", "-q", "-dNOPAUSE", "-dBATCH", "-sDEVICE=pngalpha", "-r{0}".format(resolution), "-dEPSCrop", "-sOutputFile=" + png_filename, pdf_filename]) return jb_common.utils.base.static_file_response(png_filename)
def split_and_rename(request, parent_name=None, old_split_id=None): """Both splitting of a sample and re-split of an already existing split are handled here. *Either* ``parent_name`` *or* ``old_split`` are unequal to ``None``. :param request: the current HTTP Request object :param parent_name: if given, the name of the sample to be split :param old_split_id: if given the process ID of the split to be modified :type request: HttpRequest :type parent_name: unicode or NoneType :type old_split_id: int or NoneType :return: the HTTP response object :rtype: HttpResponse """ assert (parent_name or old_split_id) and not (parent_name and old_split_id) if parent_name: old_split = None parent = utils.lookup_sample(parent_name, request.user) else: old_split = get_object_or_404(models.SampleSplit, pk=utils.convert_id_to_int(old_split_id)) parent = old_split.parent permissions.assert_can_edit_sample(request.user, parent) if parent.last_process_if_split() != old_split: raise Http404("This split is not the last one in the sample's process list.") number_of_old_pieces = old_split.pieces.count() if old_split else 0 automatic_split_form = AutomaticSplitForm(request.POST) if request.method == "POST": new_name_forms, global_data_form, automatic_split_form, structure_changed, next_prefix = \ forms_from_post_data(request.POST, parent, request.user) all_valid = is_all_valid(new_name_forms, global_data_form, automatic_split_form) referentially_valid = is_referentially_valid(new_name_forms, global_data_form, number_of_old_pieces) if all_valid and referentially_valid and not structure_changed: sample_split, new_pieces = save_to_database(new_name_forms, global_data_form, parent, old_split, request.user) utils.Reporter(request.user).report_sample_split( sample_split, global_data_form.cleaned_data["sample_completely_split"]) return utils.successful_response( request, _("Sample “{sample}” was successfully split.").format(sample=parent), "show_sample_by_name", {"sample_name": parent.name}, json_response=new_pieces) else: new_name_forms, global_data_form, automatic_split_form = forms_from_database(parent, request.user) next_prefix = "0" new_name_forms.append(NewNameForm(request.user, parent.name, initial={"new_name": parent.name, "new_purpose": parent.purpose}, prefix=next_prefix)) return render(request, "samples/split_and_rename.html", {"title": _("Split sample “{sample}”").format(sample=parent), "new_names": list(zip(range(number_of_old_pieces + 1, number_of_old_pieces + 1 + len(new_name_forms)), new_name_forms)), "automatic_split": automatic_split_form, "global_data": global_data_form, "old_split": old_split})
def __init__(self, request, process_id): """Class constructor. :param request: the current HTTP Request object :param process_id: the ID of the result to be edited; ``None`` if we create a new one :type request: HttpRequest :type process_id: unicode or NoneType """ self.result = get_object_or_404(models.Result, pk=utils.convert_id_to_int(process_id)) if process_id else None self.user = request.user self.query_string_dict = request.GET if not self.result else None
def show_layout(request, process_id, sample_id): sample = get_object_or_404(models.Sample, pk=utils.convert_id_to_int(sample_id)) process = get_object_or_404( models.Process, pk=utils.convert_id_to_int(process_id)).actual_instance pdf_filename = "/tmp/layouts_{0}_{1}.pdf".format(process.id, sample.id) jb_common.utils.base.mkdirs(pdf_filename) layout = layouts.get_layout(sample, process) if not layout: raise Http404("error") layout.generate_pdf(pdf_filename) png_filename = os.path.join(settings.CACHE_ROOT, "layouts", "{0}-{1}.png".format(process.id, sample.id)) jb_common.utils.base.mkdirs(png_filename) resolution = settings.THUMBNAIL_WIDTH / (layout.width / 72) subprocess.check_call([ "gs", "-q", "-dNOPAUSE", "-dBATCH", "-sDEVICE=pngalpha", "-r{0}".format(resolution), "-dEPSCrop", "-sOutputFile=" + png_filename, pdf_filename ]) return jb_common.utils.base.static_file_response(png_filename)
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_plot(request, process_id, plot_id, thumbnail): """Shows a particular plot. 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 process_id: the database ID of the process to show :param plot_id: the plot_id of the image. This is mostly ``u""`` because most measurement models have only one graphics. :param thumbnail: whether we serve a thumbnail instead of a real PDF plot :type request: HttpRequest :type process_id: unicode :type plot_id: unicode :type thumbnail: bool :return: the HTTP response object with the image :rtype: HttpResponse """ process = get_object_or_404(models.Process, pk=utils.convert_id_to_int(process_id)) process = process.actual_instance permissions.assert_can_view_physical_process(request.user, process) plot_filepath = process.calculate_plot_locations( plot_id)["thumbnail_file" if thumbnail else "plot_file"] datafile_name = process.get_datafile_name(plot_id) if datafile_name is None: raise Http404("No such plot available.") timestamps = [] if thumbnail else [ sample.last_modified for sample in process.samples.all() ] timestamps.append(process.last_modified) datafile_names = datafile_name if isinstance(datafile_name, list) else [datafile_name] if not all(os.path.exists(filename) for filename in datafile_names): raise Http404("One of the raw datafiles was not found.") content = get_cached_file_content( plot_filepath, partial(generate_plot, process, plot_id, thumbnail, datafile_name), datafile_names, timestamps) return static_response( content, None if thumbnail else process.get_plotfile_basename(plot_id) + ".pdf", mimetypes.guess_type(plot_filepath))
def remove(request, task_id): """Deletes a task from the database. :Paramerters: :param request: the current HTTP Request object :param task_id: the id from the task, which has to be deleted :return: the HTTP response object :rtype: HttpResponse """ task = get_object_or_404(Task, id=utils.convert_id_to_int(task_id)) if task.customer != request.user: raise permissions.PermissionError(request.user, _("You are not the customer of this task.")) utils.Reporter(request.user).report_removed_task(task) task.delete() return utils.successful_response(request, _("The task was successfully removed."), show)
def edit(request, external_operator_id): """View for editing existing external operators. You can also give the operator initials here. :param request: the current HTTP Request object :param external_operator_id: the database ID for the external operator :type request: HttpRequest :type external_operator_id: unicode :return: the HTTP response object :rtype: HttpResponse """ external_operator = get_object_or_404( models.ExternalOperator, pk=utils.convert_id_to_int(external_operator_id)) permissions.assert_can_edit_external_operator(request.user, external_operator) if request.method == "POST": external_operator_form = EditExternalOperatorForm( request.user, request.POST, instance=external_operator) if external_operator_form.is_valid(): external_operator = external_operator_form.save() return utils.successful_response( request, _("The external operator “{operator}” was successfully changed." ).format(operator=external_operator.name)) else: external_operator_form = EditExternalOperatorForm( request.user, instance=external_operator) initials_form = utils.InitialsForm(external_operator, initials_mandatory=True) return render( request, "samples/edit_external_operator.html", { "title": _("Edit external operator “{operator}”").format( operator=external_operator.name), "external_operator": external_operator_form, "initials": initials_form })
def show_image(request, process_id): """Shows a particular result image. Although its response is an image rather than an HTML file, it is served by Django in order to enforce user permissions. :param request: the current HTTP Request object :param process_id: the database ID of the result to show :type request: HttpRequest :type process_id: unicode :return: the HTTP response object with the image :rtype: HttpResponse """ result = get_object_or_404(models.Result, pk=utils.convert_id_to_int(process_id)) permissions.assert_can_view_result_process(request.user, result) image_locations = result.get_image_locations() return static_file_response(image_locations["image_file"], image_locations["sluggified_filename"])
def show(request, external_operator_id): """View for displaying existing external operators. Only users who are allowed to see all samples, and the current contact person are allowed to see it. :param request: the current HTTP Request object :param external_operator_id: the database ID for the external operator :type request: HttpRequest :type external_operator_id: unicode :return: the HTTP response object :rtype: HttpResponse """ external_operator = get_object_or_404( models.ExternalOperator, pk=utils.convert_id_to_int(external_operator_id)) contact_persons = external_operator.contact_persons.all() if permissions.has_permission_to_view_external_operator( request.user, external_operator): try: initials = external_operator.initials except models.Initials.DoesNotExist: initials = None title = _("External operator “{name}”").format( name=external_operator.name) else: title = _("Confidential operator #{number}").format( number=external_operator.pk) external_operator = None initials = None return render( request, "samples/show_external_operator.html", { "title": title, "external_operator": external_operator, "initials": initials, "contact_persons": contact_persons, "can_edit": request.user in contact_persons })
def remove(request, task_id): """Deletes a task from the database. :Paramerters: :param request: the current HTTP Request object :param task_id: the id from the task, which has to be deleted :return: the HTTP response object :rtype: HttpResponse """ task = get_object_or_404(Task, id=utils.convert_id_to_int(task_id)) if task.customer != request.user: raise permissions.PermissionError( request.user, _("You are not the customer of this task.")) utils.Reporter(request.user).report_removed_task(task) task.delete() return utils.successful_response(request, _("The task was successfully removed."), "samples:show_task_lists")
def show_image(request, process_id): """Shows a particular result image. Although its response is an image rather than an HTML file, it is served by Django in order to enforce user permissions. :param request: the current HTTP Request object :param process_id: the database ID of the result to show :type request: HttpRequest :type process_id: unicode :return: the HTTP response object with the image :rtype: HttpResponse """ result = get_object_or_404(models.Result, pk=utils.convert_id_to_int(process_id)) permissions.assert_can_view_result_process(request.user, result) image_locations = result.get_image_locations() return static_file_response(jb_common.utils.blobs.storage.export(image_locations["image_file"]), image_locations["sluggified_filename"])
def rename_sample(request): """Rename a sample given by its id. This view should only be available for adminstative users. For normal users use ``samples.views.bulk_rename.bulk_rename`` :param request: the current HTTP Request object :type request: HttpRequest :return: the HTTP response object :rtype: HttpResponse """ sample_id = request.GET.get("sample_id") if sample_id: sample = get_object_or_404(models.Sample, pk=utils.convert_id_to_int(sample_id)) permissions.assert_can_rename_sample(request.user, sample) else: sample = None if request.method == "POST": sample_rename_form = SampleRenameForm(request.user, request.POST) if sample_rename_form.is_valid(): old_name = sample_rename_form.cleaned_data["old_name"] if not sample: sample = models.Sample.objects.get(name=old_name) sample.name = sample_rename_form.cleaned_data["new_name"] sample.save() if sample_rename_form.cleaned_data["create_alias"]: models.SampleAlias.objects.create(name=old_name, sample=sample) feed_reporter = utils.Reporter(request.user) feed_reporter.report_edited_samples([sample], {"important": True, "description": _("Sample {old_name} was renamed to {new_name}"). format(new_name=sample.name, old_name=old_name)}) return utils.successful_response( request, _("Sample {sample} was successfully changed in the database.").format(sample=sample)) else: sample_rename_form = SampleRenameForm(request.user, initial={"old_name": sample.name if sample else ""}) title = capfirst(_("rename sample")) + " “{sample}”".format(sample=sample) if sample else "" return render(request, "samples/rename_sample.html", {"title": title, "sample_rename": sample_rename_form})
def substrate_by_sample(request, sample_id): """Searches for the substrate of a sample. It returns a dictionary with the substrate data. If the sample isn't found, a 404 is returned. If something else went wrong (in particular, no substrate was found), ``False`` is returned. :param request: the HTTP request object :param sample_id: the primary key of the sample :type request: HttpRequest :type sample_id: unicode :return: the HTTP response object :rtype: HttpResponse """ if not request.user.is_staff: return respond_in_json(False) sample = get_object_or_404(models.Sample, pk=utils.convert_id_to_int(sample_id)) substrate = get_substrate(sample) return respond_in_json(substrate.get_data())
def substrate_by_sample(request, sample_id): """Searches for the substrate of a sample. It returns a dictionary with the substrate data. If the sample isn't found, a 404 is returned. If something else went wrong (in particular, no substrate was found), ``False`` is returned. :param request: the HTTP request object :param sample_id: the primary key of the sample :type request: HttpRequest :type sample_id: unicode :return: the HTTP response object :rtype: HttpResponse """ if not request.user.is_superuser: return respond_in_json(False) sample = get_object_or_404(models.Sample, pk=utils.convert_id_to_int(sample_id)) substrate = get_substrate(sample) return respond_in_json(substrate.get_data())
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 withdraw(request, id_): """This function withdraws a status message for good. Note that it withdraws it for all its connected process types. It is idempotent. :param request: the current HTTP Request object :param id_: the id of the message to be withdrawn :type request: HttpRequest :type id_: unicode :return: the HTTP response object :rtype: HttpResponse """ status_message = get_object_or_404(models.StatusMessage, withdrawn=False, pk=utils.convert_id_to_int(id_)) if request.user != status_message.operator: raise PermissionError(request.user, "You cannot withdraw status messages of another user.") status_message.withdrawn = True status_message.save() for process_class in status_message.process_classes.all(): utils.Reporter(request.user).report_withdrawn_status_message(process_class, status_message) return utils.successful_response(request, _("The status message was successfully withdrawn."), show)
def show_thumbnail(request, process_id): """Shows the thumnail of a particular result image. Although its response is an image rather than an HTML file, it is served by Django in order to enforce user permissions. :param request: the current HTTP Request object :param process_id: the database ID of the result to show :type request: HttpRequest :type process_id: unicode :return: the HTTP response object with the thumbnail image :rtype: HttpResponse """ result = get_object_or_404(models.Result, pk=utils.convert_id_to_int(process_id)) permissions.assert_can_view_result_process(request.user, result) image_locations = result.get_image_locations() image_filename = image_locations["image_file"] thumbnail_file = image_locations["thumbnail_file"] content = get_cached_file_content(thumbnail_file, partial(generate_thumbnail, result, image_filename), [image_filename]) return static_response(content, content_type="image/png")
def show(request, process_id): """Shows a particular result process. The main purpose of this view is to be able to visit a result directly from a feed entry about a new/edited result. :param request: the current HTTP Request object :param process_id: the database ID of the result to show :type request: HttpRequest :type process_id: unicode :return: the HTTP response object :rtype: HttpResponse """ result = get_object_or_404(models.Result, pk=utils.convert_id_to_int(process_id)) permissions.assert_can_view_result_process(request.user, result) if jb_common.utils.base.is_json_requested(request): return jb_common.utils.base.respond_in_json(result.get_data()) template_context = {"title": _("Result “{title}”").format(title=result.title), "result": result, "samples": result.samples.all(), "sample_series": result.sample_series.all()} template_context.update(utils.digest_process(result, request.user)) return render(request, "samples/show_single_result.html", template_context)
def bulk_rename(request): """View for bulk-renaming samples that have had a provisional name so far. If the user doesn't have initials yet, he is redirected to his preferences page. :param request: the current HTTP Request object :type request: HttpRequest :return: the HTTP response object :rtype: HttpResponse """ # FixMe: Get rid of the "numbers" parameter. I think it is only used in # the remote client. if "numbers" in request.GET: numbers_list = request.GET.get("numbers", "") samples = [get_object_or_404(models.Sample, name="*" + number.zfill(5)) for number in numbers_list.split(",")] elif "ids" in request.GET: ids = request.GET["ids"].split(",") samples = [get_object_or_404(models.Sample, pk=utils.convert_id_to_int(id_)) for id_ in ids] if not all(utils.sample_name_format(sample.name) in utils.get_renamable_name_formats() for sample in samples): raise Http404("Some given samples cannot be renamed.") else: samples = None if not samples: raise Http404("No samples given.") for sample in samples: permissions.assert_can_edit_sample(request.user, sample) available_prefixes = find_prefixes(request.user) if not available_prefixes and any("{user_initials}" in format_ for format_ in settings.NAME_PREFIX_TEMPLATES) \ and not models.Initials.objects.filter(user=request.user).exists(): query_string = "initials_mandatory=True&next=" + django.utils.http.urlquote_plus( request.path + "?" + request.META["QUERY_STRING"], safe="/") messages.info(request, _("You may change the sample names, but you must choose initials first.")) return utils.successful_response(request, view="samples.views.user_details.edit_preferences", kwargs={"login_name": request.user.username}, query_string=query_string, forced=True) single_prefix = available_prefixes[0][1] if len(available_prefixes) == 1 else None if request.method == "POST": if single_prefix: prefixes_form = None prefix = single_prefix elif available_prefixes: prefixes_form = PrefixesForm(available_prefixes, request.POST) prefix = prefixes_form.cleaned_data["prefix"] if prefixes_form.is_valid() else "" else: prefixes_form = None prefix = "" if prefix == "*": prefix = "" new_name_forms = [NewNameForm(request.user, prefix, sample, request.POST, prefix=str(sample.pk)) for sample in samples] all_valid = prefixes_form is None or prefixes_form.is_valid() all_valid = all([new_name_form.is_valid() for new_name_form in new_name_forms]) and all_valid referentially_valid = is_referentially_valid(samples, new_name_forms) if all_valid and referentially_valid: for sample, new_name_form in zip(samples, new_name_forms): if not sample.name.startswith("*"): models.SampleAlias(name=sample.name, sample=sample).save() sample.name = new_name_form.cleaned_data["name"] sample.save() return utils.successful_response(request, _("Successfully renamed the samples.")) else: prefixes_form = PrefixesForm(available_prefixes, initial={"prefix": available_prefixes[0][0]}) \ if available_prefixes else None new_name_forms = [NewNameForm(request.user, "", sample, prefix=str(sample.pk)) for sample in samples] return render(request, "samples/bulk_rename.html", {"title": _("Rename samples"), "prefixes": prefixes_form, "single_prefix": single_prefix, "samples": list(zip(samples, new_name_forms))})
def show_plot(request, process_id, plot_id, thumbnail): """Shows a particular plot. 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 process_id: the database ID of the process to show :param plot_id: the plot_id of the image. This is mostly ``u""`` because most measurement models have only one graphics. :param thumbnail: whether we serve a thumbnail instead of a real PDF plot :type request: HttpRequest :type process_id: unicode :type plot_id: unicode :type thumbnail: bool :return: the HTTP response object with the image :rtype: HttpResponse """ process = get_object_or_404(models.Process, pk=utils.convert_id_to_int(process_id)) process = process.actual_instance permissions.assert_can_view_physical_process(request.user, process) plot_filepath = process.calculate_plot_locations(plot_id)["thumbnail_file" if thumbnail else "plot_file"] datafile_name = process.get_datafile_name(plot_id) if datafile_name is None: raise Http404("No such plot available.") timestamps = [] if thumbnail else [sample.last_modified for sample in process.samples.all()] timestamps.append(process.last_modified) if datafile_name: datafile_names = datafile_name if isinstance(datafile_name, list) else [datafile_name] if not all(os.path.exists(filename) for filename in datafile_names): raise Http404("One of the raw datafiles was not found.") update_necessary = jb_common.utils.base.is_update_necessary(plot_filepath, datafile_names, timestamps) else: update_necessary = jb_common.utils.base.is_update_necessary(plot_filepath, timestamps=timestamps) if update_necessary: try: if thumbnail: figure = Figure(frameon=False, figsize=(4, 3)) canvas = FigureCanvasAgg(figure) axes = figure.add_subplot(111) axes.set_position((0.17, 0.16, 0.78, 0.78)) axes.grid(True) process.draw_plot(axes, plot_id, datafile_name, for_thumbnail=True) jb_common.utils.base.mkdirs(plot_filepath) canvas.print_figure(plot_filepath, dpi=settings.THUMBNAIL_WIDTH / 4) else: figure = Figure() canvas = FigureCanvasAgg(figure) axes = figure.add_subplot(111) axes.grid(True) axes.set_title(six.text_type(process)) process.draw_plot(axes, plot_id, datafile_name, for_thumbnail=False) # FixMe: Activate this line with Matplotlib 1.1.0. # figure.tight_layout() jb_common.utils.base.mkdirs(plot_filepath) canvas.print_figure(plot_filepath, format="pdf") storage_changed.send(models.Process) except PlotError as e: raise Http404(six.text_type(e) or "Plot could not be generated.") return jb_common.utils.base.static_file_response(plot_filepath, None if thumbnail else process.get_plotfile_basename(plot_id) + ".pdf")
def show_plot(request, process_id, plot_id, thumbnail): """Shows a particular plot. 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 process_id: the database ID of the process to show :param plot_id: the plot_id of the image. This is mostly ``u""`` because most measurement models have only one graphics. :param thumbnail: whether we serve a thumbnail instead of a real PDF plot :type request: HttpRequest :type process_id: unicode :type plot_id: unicode :type thumbnail: bool :return: the HTTP response object with the image :rtype: HttpResponse """ process = get_object_or_404(models.Process, pk=utils.convert_id_to_int(process_id)) process = process.actual_instance permissions.assert_can_view_physical_process(request.user, process) plot_filepath = process.calculate_plot_locations(plot_id)["thumbnail_file" if thumbnail else "plot_file"] datafile_name = process.get_datafile_name(plot_id) if datafile_name is None: raise Http404("No such plot available.") timestamps = [] if thumbnail else [sample.last_modified for sample in process.samples.all()] timestamps.append(process.last_modified) if datafile_name: datafile_names = datafile_name if isinstance(datafile_name, list) else [datafile_name] if not all(os.path.exists(filename) for filename in datafile_names): raise Http404("One of the raw datafiles was not found.") update_necessary = jb_common.utils.base.is_update_necessary(plot_filepath, datafile_names, timestamps) else: update_necessary = jb_common.utils.base.is_update_necessary(plot_filepath, timestamps=timestamps) if update_necessary: try: if thumbnail: figure = Figure(frameon=False, figsize=(4, 3)) canvas = FigureCanvasAgg(figure) axes = figure.add_subplot(111) axes.set_position((0.17, 0.16, 0.78, 0.78)) axes.grid(True) process.draw_plot(axes, plot_id, datafile_name, for_thumbnail=True) jb_common.utils.base.mkdirs(plot_filepath) canvas.print_figure(plot_filepath, dpi=settings.THUMBNAIL_WIDTH / 4) else: figure = Figure() canvas = FigureCanvasAgg(figure) axes = figure.add_subplot(111) axes.grid(True) axes.set_title(six.text_type(process)) process.draw_plot(axes, plot_id, datafile_name, for_thumbnail=False) # FixMe: Activate this line with Matplotlib 1.1.0. # figure.tight_layout() jb_common.utils.base.mkdirs(plot_filepath) canvas.print_figure(plot_filepath, format="pdf") storage_changed.send(models.Process) except PlotError as e: raise Http404(six.text_type(e) or "Plot could not be generated.") except ValueError as e: raise Http404("Plot could not be generated: " + e.args[0]) return jb_common.utils.base.static_file_response(plot_filepath, None if thumbnail else process.get_plotfile_basename(plot_id) + ".pdf")
def add_sample(request): """Adds a new sample to the database. It is added without processes. This view can only be used by admin accounts. :param request: the current HTTP Request object; it must contain the sample data in the POST data. :return: The primary key of the created sample. ``False`` if something went wrong. It may return a 404 if the topic or the currently responsible person wasn't found. :rtype: HttpResponse """ try: name = request.POST["name"] current_location = request.POST["current_location"] currently_responsible_person = request.POST[ "currently_responsible_person"] purpose = request.POST.get("purpose", "") tags = request.POST.get("tags", "") topic = request.POST.get("topic") except KeyError as error: raise JSONRequestException( 3, "'{}' parameter missing.".format(error.args[0])) if len(name) > 30: raise JSONRequestException(5, "The sample name is too long.") name_format = utils.sample_name_format(name) if name_format is None or \ not request.user.is_staff and name_format not in settings.SAMPLE_NAME_FORMATS["provisional"].get("possible_renames", set()): raise JSONRequestException(5, "The sample name is invalid.") eligible_users = django.contrib.auth.models.User.objects.filter( is_active=True, jb_user_details__department__isnull=False) try: currently_responsible_person = eligible_users.get( pk=utils.convert_id_to_int(currently_responsible_person)) except django.contrib.auth.models.User.DoesNotExist: raise Http404("Currently reponsible user not found.") if topic: all_topics = Topic.objects.all() if request.user.is_staff else \ Topic.objects.filter(Q(confidential=False) | Q(members=request.user)) try: topic = all_topics.get(pk=utils.convert_id_to_int(topic)) except Topic.DoesNotExist: raise Http404("Topic not found") try: sample = models.Sample.objects.create( name=name, current_location=current_location, currently_responsible_person=currently_responsible_person, purpose=purpose, tags=tags, topic=topic) # They will be shadowed anyway. Nevertheless, this action is an # emergency measure. Probably the samples the aliases point to should # be merged with the sample but this can't be decided automatically. models.SampleAlias.objects.filter(name=name).delete() except IntegrityError as error: error_message = "The sample with this data could not be added." if request.user.is_staff: error_message += " {}".format(error) raise JSONRequestException(5, error_message) sample.watchers.add(request.user) return respond_in_json(sample.pk)
def edit(request, task_id): """Edit or create a task. :param request: the current HTTP Request object :param task_id: number of the task to be edited. If this is ``None``, create a new one. :type request: HttpRequest :type task_id: unicode :return: the HTTP response object :rtype: HttpResponse """ task = get_object_or_404( Task, id=utils.convert_id_to_int(task_id)) if task_id else None user = request.user if task and user != task.customer: permissions.assert_can_add_physical_process( request.user, task.process_class.model_class()) preset_sample = utils.extract_preset_sample( request) if not task_id else None if request.method == "POST": task_form = TaskForm(user, request.POST, instance=task) samples_form = SamplesForm(user, preset_sample, task, request.POST) if task_id: old_task = copy.copy(task) old_samples = set(task.samples.all()) if task_form.is_valid() and (not samples_form.is_bound or samples_form.is_valid()): task = save_to_database(task_form, samples_form, old_task=old_task if task_id else None) if task_id: edit_description = {"important": True, "description": ""} if old_task.status != task.status: edit_description["description"] += \ _("* Status is now “{new_status}”.\n").format(new_status=task.get_status_display()) if old_task.priority != task.priority: edit_description["description"] += \ _("* Priority is now “{new_priority}̣”.\n").format(new_priority=task.get_priority_display()) if old_task.finished_process != task.finished_process: edit_description["description"] += _( "* Connected process.\n") if old_task.operator != task.operator: if task.operator: edit_description["description"] += _( "* Operator is now {operator}.\n").format( operator=common_utils.get_really_full_name( task.operator)) else: edit_description["description"] += _( "* No operator is set anymore.\n") if old_samples != set(task.samples.all()): edit_description["description"] += "* {0}.\n".format( common_utils.capitalize_first_letter(_("samples"))) if old_task.comments != task.comments: edit_description["description"] += "* {0}.\n".format( common_utils.capitalize_first_letter(_("comments"))) else: edit_description = None utils.Reporter(request.user).report_task(task, edit_description) message = _("Task was {verb} successfully.").format( verb=_("edited") if task_id else _("added")) return utils.successful_response(request, message, "samples:show_task_lists") else: samples_form = SamplesForm(user, preset_sample, task) initial = {} if "process_class" in request.GET: initial["process_class"] = request.GET["process_class"] task_form = TaskForm(request.user, instance=task, initial=initial) title = capfirst(_("edit task")) if task else capfirst(_("add task")) return render(request, "samples/edit_task.html", { "title": title, "task": task_form, "samples": samples_form })
def edit(request, task_id): """Edit or create a task. :param request: the current HTTP Request object :param task_id: number of the task to be edited. If this is ``None``, create a new one. :type request: HttpRequest :type task_id: unicode :return: the HTTP response object :rtype: HttpResponse """ task = get_object_or_404(Task, id=utils.convert_id_to_int(task_id)) if task_id else None user = request.user if task and user != task.customer: permissions.assert_can_add_physical_process(request.user, task.process_class.model_class()) preset_sample = utils.extract_preset_sample(request) if not task_id else None if request.method == "POST": task_form = TaskForm(user, request.POST, instance=task) samples_form = SamplesForm(user, preset_sample, task, request.POST) if task_id: old_task = copy.copy(task) old_samples = set(task.samples.all()) if task_form.is_valid() and (not samples_form.is_bound or samples_form.is_valid()): task = save_to_database(task_form, samples_form, old_task=old_task if task_id else None) if task_id: edit_description = {"important": True, "description": ""} if old_task.status != task.status: edit_description["description"] += \ _("* Status is now “{new_status}”.\n").format(new_status=task.get_status_display()) if old_task.priority != task.priority: edit_description["description"] += \ _("* Priority is now “{new_priority}̣”.\n").format(new_priority=task.get_priority_display()) if old_task.finished_process != task.finished_process: edit_description["description"] += _("* Connected process.\n") if old_task.operator != task.operator: if task.operator: edit_description["description"] += _("* Operator is now {operator}.\n").format( operator=common_utils.get_really_full_name(task.operator)) else: edit_description["description"] += _("* No operator is set anymore.\n") if old_samples != set(task.samples.all()): edit_description["description"] += "* {0}.\n".format(common_utils.capitalize_first_letter(_("samples"))) if old_task.comments != task.comments: edit_description["description"] += "* {0}.\n".format(common_utils.capitalize_first_letter(_("comments"))) else: edit_description = None utils.Reporter(request.user).report_task(task, edit_description) message = _("Task was {verb} successfully.").format(verb=_("edited") if task_id else _("added")) return utils.successful_response(request, message, "samples.views.task_lists.show") else: samples_form = SamplesForm(user, preset_sample, task) initial = {} if "process_class" in request.GET: initial["process_class"] = request.GET["process_class"] task_form = TaskForm(request.user, instance=task, initial=initial) title = capfirst(_("edit task")) if task else capfirst(_("add task")) return render(request, "samples/edit_task.html", {"title": title, "task": task_form, "samples": samples_form})
def show(request, claim_id): """View for reviewing a claim. :param request: the current HTTP Request object :param claim_id: the primary key of the claim to be viewed :type request: HttpRequest :type claim_id: unicode :return: the HTTP response object :rtype: HttpResponse """ _ = ugettext claim = get_object_or_404(models.SampleClaim, pk=utils.convert_id_to_int(claim_id)) is_reviewer = request.user == claim.reviewer or request.user.is_staff is_requester = request.user == claim.requester if not is_reviewer and not is_requester: raise permissions.PermissionError( request.user, _("You are neither the requester nor the reviewer of this claim.")) if request.method == "POST" and not claim.closed: withdraw_form = CloseForm(_("withdraw claim"), request.POST, prefix="withdraw") if is_requester else None approve_form = CloseForm(_("approve claim"), request.POST, prefix="approve") if is_reviewer else None all_valid = (withdraw_form is None or withdraw_form.is_valid()) and ( approve_form is None or approve_form.is_valid()) referencially_valid = is_referentially_valid(withdraw_form, approve_form) if all_valid and referencially_valid: approved = approve_form and approve_form.cleaned_data["close"] closed = approved or (withdraw_form and withdraw_form.cleaned_data["close"]) response = None if approved: sample_list = list(claim.samples.all()) for sample in sample_list: sample.currently_responsible_person = claim.requester sample.save() sample_enumeration = " " + ",\n ".join( six.text_type(sample) for sample in sample_list) _ = lambda x: x send_email( _("Sample request approved"), _("""Hello {requester}, your sample claim was approved. You are now the “currently responsible person” of the following samples: {samples} JuliaBase. """), claim.requester, { "requester": get_really_full_name(claim.requester), "samples": sample_enumeration }) _ = ugettext response = \ utils.successful_response(request, _("Sample claim {id_} was successfully approved.").format(id_=claim.pk)) if closed: claim.closed = True claim.save() response = response or \ utils.successful_response(request, _("Sample claim {id_} was successfully withdrawn.").format(id_=claim.pk)) return response else: withdraw_form = CloseForm(_("withdraw claim"), prefix="withdraw") if is_requester else None approve_form = CloseForm(_("approve claim"), prefix="approve") if is_reviewer else None return render( request, "samples/show_claim.html", { "title": _("Claim #{number}").format(number=claim_id), "claim": claim, "is_reviewer": is_reviewer, "is_requester": is_requester, "withdraw": withdraw_form, "approve": approve_form })
def split_and_rename(request, parent_name=None, old_split_id=None): """Both splitting of a sample and re-split of an already existing split are handled here. *Either* ``parent_name`` *or* ``old_split`` are unequal to ``None``. :param request: the current HTTP Request object :param parent_name: if given, the name of the sample to be split :param old_split_id: if given the process ID of the split to be modified :type request: HttpRequest :type parent_name: unicode or NoneType :type old_split_id: int or NoneType :return: the HTTP response object :rtype: HttpResponse """ assert (parent_name or old_split_id) and not (parent_name and old_split_id) if parent_name: old_split = None parent = utils.lookup_sample(parent_name, request.user) else: old_split = get_object_or_404(models.SampleSplit, pk=utils.convert_id_to_int(old_split_id)) parent = old_split.parent permissions.assert_can_edit_sample(request.user, parent) if parent.last_process_if_split() != old_split: raise Http404( "This split is not the last one in the sample's process list.") number_of_old_pieces = old_split.pieces.count() if old_split else 0 automatic_split_form = AutomaticSplitForm(request.POST) if request.method == "POST": new_name_forms, global_data_form, automatic_split_form, structure_changed, next_prefix = \ forms_from_post_data(request.POST, parent, request.user) all_valid = is_all_valid(new_name_forms, global_data_form, automatic_split_form) referentially_valid = is_referentially_valid(new_name_forms, global_data_form, number_of_old_pieces) if all_valid and referentially_valid and not structure_changed: sample_split, new_pieces = save_to_database( new_name_forms, global_data_form, parent, old_split, request.user) utils.Reporter(request.user).report_sample_split( sample_split, global_data_form.cleaned_data["sample_completely_split"]) return utils.successful_response( request, _("Sample “{sample}” was successfully split.").format( sample=parent), "samples:show_sample_by_name", {"sample_name": parent.name}, json_response=new_pieces) else: new_name_forms, global_data_form, automatic_split_form = forms_from_database( parent, request.user) next_prefix = "0" new_name_forms.append( NewNameForm(request.user, parent.name, initial={ "new_name": parent.name, "new_purpose": parent.purpose }, prefix=next_prefix)) return render( request, "samples/split_and_rename.html", { "title": _("Split sample “{sample}”").format(sample=parent), "new_names": list( zip( range(number_of_old_pieces + 1, number_of_old_pieces + 1 + len(new_name_forms)), new_name_forms)), "automatic_split": automatic_split_form, "global_data": global_data_form, "old_split": old_split })
def show(request, claim_id): """View for reviewing a claim. :param request: the current HTTP Request object :param claim_id: the primary key of the claim to be viewed :type request: HttpRequest :type claim_id: unicode :return: the HTTP response object :rtype: HttpResponse """ _ = ugettext claim = get_object_or_404(models.SampleClaim, pk=utils.convert_id_to_int(claim_id)) is_reviewer = request.user == claim.reviewer or request.user.is_superuser is_requester = request.user == claim.requester if not is_reviewer and not is_requester: raise permissions.PermissionError(request.user, _("You are neither the requester nor the reviewer of this claim.")) if request.method == "POST" and not claim.closed: withdraw_form = CloseForm(_("withdraw claim"), request.POST, prefix="withdraw") if is_requester else None approve_form = CloseForm(_("approve claim"), request.POST, prefix="approve") if is_reviewer else None all_valid = (withdraw_form is None or withdraw_form.is_valid()) and (approve_form is None or approve_form.is_valid()) referencially_valid = is_referentially_valid(withdraw_form, approve_form) if all_valid and referencially_valid: approved = approve_form and approve_form.cleaned_data["close"] closed = approved or (withdraw_form and withdraw_form.cleaned_data["close"]) response = None if approved: sample_list = list(claim.samples.all()) for sample in sample_list: sample.currently_responsible_person = claim.requester sample.save() sample_enumeration = " " + ",\n ".join(six.text_type(sample) for sample in sample_list) _ = lambda x: x send_email(_("Sample request approved"), _("""Hello {requester}, your sample claim was approved. You are now the “currently responsible person” of the following samples: {samples} JuliaBase. """), claim.requester, {"requester": get_really_full_name(claim.requester), "samples": sample_enumeration}) _ = ugettext response = \ utils.successful_response(request, _("Sample claim {id_} was successfully approved.").format(id_=claim.pk)) if closed: claim.closed = True claim.save() response = response or \ utils.successful_response(request, _("Sample claim {id_} was successfully withdrawn.").format(id_=claim.pk)) return response else: withdraw_form = CloseForm(_("withdraw claim"), prefix="withdraw") if is_requester else None approve_form = CloseForm(_("approve claim"), prefix="approve") if is_reviewer else None return render(request, "samples/show_claim.html", {"title": _("Claim #{number}").format(number=claim_id), "claim": claim, "is_reviewer": is_reviewer, "is_requester": is_requester, "withdraw": withdraw_form, "approve": approve_form})