def export(request, sample_name): """View for exporting sample data in CSV or JSON format. Thus, the return value is not an HTML response. :param request: the current HTTP Request object :param sample_name: the name of the sample :type request: HttpRequest :type sample_name: unicode :return: the HTTP response object :rtype: HttpResponse """ sample = utils.lookup_sample(sample_name, request.user) data = sample.get_data_for_table_export() result = utils.table_export(request, data, _("process")) 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 new(request, sample_name): """View for killing samples. Note that it is not possible to re-kill an already dead sample. Furthermore, you must be the currently responsible person to be able to kill a sample. :param request: the current HTTP Request object :param sample_name: name of the sample to be killed :type request: HttpRequest :type sample_name: unicode :return: the HTTP response object :rtype: HttpResponse """ sample = utils.lookup_sample(sample_name, request.user) permissions.assert_can_edit_sample(request.user, sample) if sample.is_dead(): raise Http404("Sample is already dead.") if request.method == "POST": sample_death_form = SampleDeathForm(sample, request.POST) if sample_death_form.is_valid(): sample_death = sample_death_form.save(commit=False) sample_death.timestamp = django.utils.timezone.now() sample_death.operator = request.user sample_death.save() sample_death.samples = [sample] # FixMe: Feed entries return utils.successful_response(request, _("Sample “{sample}” was killed.").format(sample=sample), "samples:show_sample_by_name", {"sample_name": sample_name}) else: sample_death_form = SampleDeathForm(sample) return render(request, "samples/edit_sample_death.html", {"title": _("Kill sample “{sample}”").format(sample=sample), "sample_death": sample_death_form})
def new(request, sample_name): """View for killing samples. Note that it is not possible to re-kill an already dead sample. Furthermore, you must be the currently responsible person to be able to kill a sample. :param request: the current HTTP Request object :param sample_name: name of the sample to be killed :type request: HttpRequest :type sample_name: unicode :return: the HTTP response object :rtype: HttpResponse """ sample = utils.lookup_sample(sample_name, request.user) permissions.assert_can_edit_sample(request.user, sample) if sample.is_dead(): raise Http404("Sample is already dead.") if request.method == "POST": sample_death_form = SampleDeathForm(sample, request.POST) if sample_death_form.is_valid(): sample_death = sample_death_form.save(commit=False) sample_death.timestamp = datetime.datetime.now() sample_death.operator = request.user sample_death.save() sample_death.samples = [sample] # FixMe: Feed entries return utils.successful_response(request, _("Sample “{sample}” was killed.").format(sample=sample), "show_sample_by_name", {"sample_name": sample_name}) else: sample_death_form = SampleDeathForm(sample) return render(request, "samples/edit_sample_death.html", {"title": _("Kill sample “{sample}”").format(sample=sample), "sample_death": sample_death_form})
def copy_informal_stack(request, sample_name): """View for copying the informal stack of a sample to other samples. :param request: the current HTTP Request object :param sample_name: the name of the sample :type request: HttpRequest :type sample_name: unicode :return: the HTTP response object :rtype: HttpResponse """ sample, __ = utils.lookup_sample(sample_name, request.user, with_clearance=True) if request.method == "POST": destination_samples_form = DestinationSamplesForm( request.user, sample, request.POST) if destination_samples_form.is_valid(): destination_samples = destination_samples_form.cleaned_data[ "samples"] informal_layers = sample.sample_details.informal_layers.all() for destination_sample in destination_samples: destination_sample.sample_details.informal_layers.all().delete( ) for layer in informal_layers: layer.sample_details = destination_sample.sample_details layer.id = None layer.save() feed_reporter = utils.Reporter(request.user) message = _( "Informal stack was copied from sample {sample}.".format( sample=sample)) feed_reporter.report_edited_samples(destination_samples, edit_description={ "important": False, "description": message }) return utils.successful_response( request, _("Informal stack of {sample} was successfully copied."). format(sample=sample), "samples:show_sample_by_id", { "sample_id": sample.id, "path_suffix": "" }) else: destination_samples_form = DestinationSamplesForm(request.user, sample) context = { "title": _("Copy informal stack of “{sample}”").format(sample=sample), "sample": sample, "destination_samples": destination_samples_form } context.update(sample.sample_details.get_context_for_user( request.user, {})) return render(request, "samples/copy_informal_stack.html", context)
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 edit(request, sample_name): """View for editing existing samples. You can't use it to add new samples. :param request: the current HTTP Request object :param sample_name: the name of the sample :type request: HttpRequest :type sample_name: unicode :return: the HTTP response object :rtype: HttpResponse """ sample = utils.lookup_sample(sample_name, request.user) permissions.assert_can_edit_sample(request.user, sample) old_topic, old_responsible_person = sample.topic, sample.currently_responsible_person user_details = request.user.samples_user_details sample_details = sample.get_sample_details() if request.method == "POST": sample_form = SampleForm(request.user, request.POST, instance=sample) edit_description_form = utils.EditDescriptionForm(request.POST) all_valid = all([sample_form.is_valid(), edit_description_form.is_valid()]) referentially_valid = is_referentially_valid(sample, sample_form, edit_description_form) if sample_details: sample_details_context, sample_details_valid = \ sample_details.process_post(request.user, request.POST, sample_form, edit_description_form) all_valid = all_valid and sample_details_valid else: sample_details_context = {} if all_valid and referentially_valid: sample = sample_form.save() if sample_details: sample_details.save_form_data(sample_details_context) feed_reporter = utils.Reporter(request.user) if sample.currently_responsible_person != old_responsible_person: sample.currently_responsible_person.my_samples.add(sample) feed_reporter.report_new_responsible_person_samples([sample], edit_description_form.cleaned_data) if sample.topic and sample.topic != old_topic: for watcher in (user_details.user for user_details in sample.topic.auto_adders.all()): watcher.my_samples.add(sample) feed_reporter.report_changed_sample_topic([sample], old_topic, edit_description_form.cleaned_data) feed_reporter.report_edited_samples([sample], edit_description_form.cleaned_data) return utils.successful_response( request, _("Sample {sample} was successfully changed in the database.").format(sample=sample), by_id, {"sample_id": sample.pk, "path_suffix": ""}) else: sample_form = SampleForm(request.user, instance=sample) edit_description_form = utils.EditDescriptionForm() sample_details_context = sample_details.process_get(request.user) if sample_details else {} context = {"title": _("Edit sample “{sample}”").format(sample=sample), "sample": sample_form, "edit_description": edit_description_form} context.update(sample_details_context) return render(request, "samples/edit_sample.html", context)
def samples_and_processes(sample_name, user, post_data=None): """Returns the data structure used in the template to display the sample with all its processes. :param sample_name: the sample or alias of the sample to display :param user: the currently logged-in user :param post_data: the POST dictionary if it was an HTTP POST request, or ``None`` otherwise :type sample_name: unicode :type user: django.contrib.auth.models.User :type post_data: QueryDict :return: a list with all result processes of this sample in chronological order. :rtype: `SamplesAndProcesses` """ sample, clearance = utils.lookup_sample(sample_name, user, with_clearance=True) cache_key = "sample:{0}-{1}".format(sample.pk, user.jb_user_details.get_data_hash()) # The following ``10`` is the expectation value of the number of # processes. To get accurate results, use # ``samples.processes.count()`` instead. However, this would slow down # JuliaBase. samples_and_processes = get_from_cache(cache_key, hits=10) if samples_and_processes is None: samples_and_processes = SamplesAndProcesses(sample, clearance, user, post_data) keys_list_key = "sample-keys:{0}".format(sample.pk) with cache_key_locked("sample-lock:{0}".format(sample.pk)): keys = cache.get(keys_list_key, []) keys.append(cache_key) cache.set(keys_list_key, keys, settings.CACHES["default"].get("TIMEOUT", 300) + 10) # FixMe: Remove try block when it is clear that request.user is # a SimpleLazyObject which is not pickable. Maybe this can be # removed completely if # https://code.djangoproject.com/ticket/16563 is fixed. try: samples_and_processes.user = unlazy_object(samples_and_processes.user) except AttributeError: pass cache.set(cache_key, samples_and_processes) samples_and_processes.remove_noncleared_process_contexts(user, clearance) else: samples_and_processes.personalize(user, clearance, post_data) return samples_and_processes
def show(request, sample_name): """A view for showing existing samples. :param request: the current HTTP Request object :param sample_name: the name of the sample :type request: HttpRequest :type sample_name: unicode :return: the HTTP response object :rtype: HttpResponse """ start = time.time() if request.method == "POST": samples_and_processes = SamplesAndProcesses.samples_and_processes(sample_name, request.user, request.POST) if samples_and_processes.is_valid(): added, removed = samples_and_processes.save_to_database() if is_json_requested(request): return respond_in_json(True) if added: success_message = ungettext("Sample {samples} was added to My Samples.", "Samples {samples} were added to My Samples.", len(added)).format(samples=format_enumeration(added)) else: success_message = "" if removed: if added: success_message += " " success_message += ungettext("Sample {samples} was removed from My Samples.", "Samples {samples} were removed from My Samples.", len(removed)).format(samples=format_enumeration(removed)) elif not added: success_message = _("Nothing was changed.") messages.success(request, success_message) else: if is_json_requested(request): sample = utils.lookup_sample(sample_name, request.user) return respond_in_json(sample.get_data()) samples_and_processes = SamplesAndProcesses.samples_and_processes(sample_name, request.user) messages.debug(request, "DB-Zugriffszeit: {0:.1f} ms".format((time.time() - start) * 1000)) return render(request, "samples/show_sample.html", {"title": _("Sample “{sample}”").format(sample=samples_and_processes.sample_context["sample"]), "samples_and_processes": samples_and_processes})
def latest_split(request, sample_name): """Get the database ID of the latest split of a sample, if it is also the very latest process for that sample. In all other cases, return ``None`` (or an error HTML page if the sample didn't exist). :param request: the current HTTP Request object :param sample_name: the name of the sample :type request: HttpRequest :type sample_name: unicode :return: the HTTP response object :rtype: HttpResponse """ sample = utils.lookup_sample(sample_name, request.user) split = sample.last_process_if_split() return respond_in_json(split.pk if split else None)
def latest_split(request, sample_name): """Get the database ID of the latest split of a sample, if it is also the very latest process for that sample. In all other cases, return ``None`` (or an error HTML page if the sample didn't exist). :param request: the current HTTP Request object :param sample_name: the name of the sample :type request: HttpRequest :type sample_name: unicode :return: the HTTP response object :rtype: HttpResponse """ sample = utils.lookup_sample(sample_name, request.user) split = sample.last_process_if_split() return respond_in_json(split.pk if split else None)
def add_process(request, sample_name): """View for appending a new process to the process list of a sample. :param request: the current HTTP Request object :param sample_name: the name of the sample :type request: HttpRequest :type sample_name: unicode :return: the HTTP response object :rtype: HttpResponse """ sample = utils.lookup_sample(sample_name, request.user) sample_processes, general_processes = get_allowed_processes(request.user, sample) for process in general_processes: process["url"] += "?sample={0}&next={1}".format(urlquote_plus(sample_name), sample.get_absolute_url()) return render(request, "samples/add_process.html", {"title": _("Add process to sample “{sample}”").format(sample=sample), "processes": sample_processes + general_processes})
def copy_informal_stack(request, sample_name): """View for copying the informal stack of a sample to other samples. :param request: the current HTTP Request object :param sample_name: the name of the sample :type request: HttpRequest :type sample_name: unicode :return: the HTTP response object :rtype: HttpResponse """ sample, __ = utils.lookup_sample(sample_name, request.user, with_clearance=True) if request.method == "POST": destination_samples_form = DestinationSamplesForm(request.user, sample, request.POST) if destination_samples_form.is_valid(): destination_samples = destination_samples_form.cleaned_data["samples"] informal_layers = sample.sample_details.informal_layers.all() for destination_sample in destination_samples: destination_sample.sample_details.informal_layers.all().delete() for layer in informal_layers: layer.sample_details = destination_sample.sample_details layer.id = None layer.save() feed_reporter = utils.Reporter(request.user) message = _("Informal stack was copied from sample {sample}.".format(sample=sample)) feed_reporter.report_edited_samples(destination_samples, edit_description={"important": False, "description": message}) return utils.successful_response( request, _("Informal stack of {sample} was successfully copied.").format(sample=sample), "samples.views.sample.by_id", {"sample_id": sample.id, "path_suffix": ""}) else: destination_samples_form = DestinationSamplesForm(request.user, sample) context = {"title": _("Copy informal stack of “{sample}”").format(sample=sample), "sample": sample, "destination_samples": destination_samples_form} context.update(sample.sample_details.get_context_for_user(request.user, {})) return render(request, "samples/copy_informal_stack.html", context)
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 })