Ejemplo n.º 1
0
def delete_process(request, process_id):
    """Deletes an existing physical process.

    :param request: the current HTTP Request object
    :param process_id: the process's ID

    :type request: HttpRequest
    :type process_id: unicode

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    process = get_object_or_404(Process,
                                pk=int_or_zero(process_id)).actual_instance
    affected_objects = permissions.assert_can_delete_physical_process(
        request.user, process)
    for instance in affected_objects:
        if isinstance(instance, models.Sample):
            utils.Reporter(request.user).report_deleted_sample(instance)
        elif isinstance(instance, models.Process):
            utils.Reporter(request.user).report_deleted_process(instance)
    success_message = _(
        "Process {process} was successfully deleted in the database.").format(
            process=process)
    process.delete()
    return utils.successful_response(request, success_message)
Ejemplo n.º 2
0
def edit(request, id):
    """View for changing the members of a particular topic, and to set the
    restriction status.  This is only allowed to heads of institute groups and
    topic managers.

    :param request: the current HTTP Request object
    :param id: the id of the topic

    :type request: HttpRequest
    :type name: unicode

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    topic = get_object_or_404(Topic, id=int_or_zero(id), parent_topic=None)
    permissions.assert_can_edit_topic(request.user, topic)
    if request.method == "POST":
        edit_topic_form = EditTopicForm(request.user, topic, request.POST)
        added_members = []
        removed_members = []
        if edit_topic_form.is_valid():
            old_manager = topic.manager
            new_manager = edit_topic_form.cleaned_data["topic_manager"]
            topic.manager = new_manager
            old_members = list(topic.members.all())
            new_members = edit_topic_form.cleaned_data["members"] + [new_manager]
            topic.members = new_members
            topic.confidential = edit_topic_form.cleaned_data["confidential"]
            topic.save()
            if old_manager != new_manager:
                if not old_manager.managed_topics.all():
                    old_manager.user_permissions.remove(PermissionsModels.topic_manager_permission)
                if not permissions.has_permission_to_edit_users_topics(new_manager):
                    new_manager.user_permissions.add(PermissionsModels.topic_manager_permission)
            for user in new_members:
                if user not in old_members:
                    added_members.append(user)
                    topic.auto_adders.add(user.samples_user_details)
            for user in old_members:
                if user not in new_members:
                    removed_members.append(user)
                    topic.auto_adders.remove(user.samples_user_details)
            if added_members:
                utils.Reporter(request.user).report_changed_topic_membership(added_members, topic, "added")
            if removed_members:
                utils.Reporter(request.user).report_changed_topic_membership(removed_members, topic, "removed")
            return utils.successful_response(
                request, _("Members of topic “{name}” were successfully updated.").format(name=topic.name))
    else:
        edit_topic_form = \
            EditTopicForm(request.user, topic, initial={"members": topic.members.values_list("pk", flat=True),
                                                        "topic_manager": topic.manager.pk})
    return render(request, "samples/edit_topic.html", {"title": _("Change topic memberships of “{0}”").format(topic.name),
                                                       "edit_topic": edit_topic_form})
Ejemplo n.º 3
0
def edit(request, process_id):
    """View for editing existing results, and for creating new ones.

    :param request: the current HTTP Request object
    :param process_id: the ID of the result process; ``None`` if we're creating
        a new one

    :type request: HttpRequest
    :type process_id: str

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    form_set = FormSet(request, process_id)
    if form_set.result:
        permissions.assert_can_edit_result_process(request.user, form_set.result)
    if request.method == "POST":
        form_set.from_post_data(request.POST, request.FILES)
        result = form_set.save_to_database(request.FILES)
        if result:
            utils.Reporter(request.user).report_result_process(
                result, form_set.edit_description_form.cleaned_data if form_set.edit_description_form else None)
            return utils.successful_response(request, json_response=result.pk)
    else:
        form_set.from_database()
    form_set.update_previous_dimensions_form()
    title = _("Edit result") if form_set.result else _("New result")
    context_dict = {"title": title}
    context_dict.update(form_set.get_context_dict())
    return render(request, "samples/edit_result.html", context_dict)
Ejemplo n.º 4
0
def new(request):
    """View for creating a new sample series.  Note that you can add arbitrary
    samples to a sample series, even those you can't see.

    :param request: the current HTTP Request object

    :type request: HttpRequest

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    if request.method == "POST":
        sample_series_form = SampleSeriesForm(request.user, request.POST)
        if sample_series_form.is_valid():
            timestamp = datetime.datetime.today()
            full_name = "{0}-{1}-{2}".format(
                request.user.username, timestamp.strftime("%y"),
                sample_series_form.cleaned_data["short_name"])
            if models.SampleSeries.objects.filter(name=full_name).exists():
                sample_series_form.add_error(
                    "short_name",
                    _("This sample series name is already given."))
            elif len(full_name) > models.SampleSeries._meta.get_field(
                    "name").max_length:
                overfull_letters = len(
                    full_name) - models.SampleSeries._meta.get_field(
                        "name").max_length
                error_message = ungettext(
                    "The name is {number} letter too long.",
                    "The name is {number} letters too long.",
                    overfull_letters).format(number=overfull_letters)
                sample_series_form.add_error("short_name", error_message)
            else:
                sample_series = sample_series_form.save(commit=False)
                sample_series.name = full_name
                sample_series.timestamp = timestamp
                sample_series.save()
                sample_series_form.save_m2m()
                utils.Reporter(
                    request.user).report_new_sample_series(sample_series)
                return utils.successful_response(
                    request,
                    _("Sample series {name} was successfully added to the database."
                      ).format(name=full_name))
    else:
        sample_series_form = SampleSeriesForm(request.user)
    return render(
        request, "samples/edit_sample_series.html", {
            "title":
            _("Create new sample series"),
            "sample_series":
            sample_series_form,
            "is_new":
            True,
            "name_prefix":
            "{0}-{1}".format(request.user.username,
                             datetime.datetime.today().strftime("%y"))
        })
Ejemplo n.º 5
0
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)
Ejemplo n.º 6
0
def add(request):
    """With this function, the messages are stored into the database.  It also gets
    the information for displaying the "add_status_message" template.

    :param request: the current HTTP Request object

    :type request: HttpRequest

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    if request.method == "POST":
        status_form = StatusForm(request.user, request.POST)
        if status_form.is_valid():
            status = status_form.save()
            for process_class in status.process_classes.all():
                utils.Reporter(request.user).report_status_message(
                    process_class, status)
            return utils.successful_response(
                request,
                _("The status message was successfully added to the database.")
            )
    else:
        status_form = StatusForm(request.user)
    title = _("Add status message")
    return render(request, "samples/add_status_message.html", {
        "title": title,
        "status": status_form
    })
Ejemplo n.º 7
0
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)
Ejemplo n.º 8
0
def split_and_rename_after_deposition(request, deposition_number):
    """View for renaming and/or splitting samples immediately after they have
    been deposited in the same run.

    Optionally, you can give query string parameters of the form
    ``new-name-21=super`` where 21 is the sample ID and “super” is the
    suggested new name of this sample.

    :param request: the current HTTP Request object
    :param deposition_number: the number of the deposition after which samples
        should be split and/or renamed

    :type request: HttpRequest
    :type deposition_number: unicode

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    deposition = get_object_or_404(models.Deposition, number=deposition_number).actual_instance
    permissions.assert_can_edit_physical_process(request.user, deposition)
    if not deposition.finished:
        raise Http404("This deposition is not finished yet.")
    if deposition.split_done:
        raise Http404("You can use the split and rename function only once after a deposition.")
    remote_client = is_json_requested(request)
    if request.POST:
        original_data_forms, new_name_form_lists, global_new_data_form = \
            forms_from_post_data(request.user, request.POST, deposition, remote_client)
        all_valid = is_all_valid(original_data_forms, new_name_form_lists, global_new_data_form)
        structure_changed = change_structure(request.user, original_data_forms, new_name_form_lists)
        referentially_valid = is_referentially_valid(original_data_forms, new_name_form_lists, deposition)
        if all_valid and referentially_valid and not structure_changed:
            sample_splits = save_to_database(original_data_forms, new_name_form_lists, global_new_data_form, deposition)
            for sample_split in sample_splits:
                utils.Reporter(request.user).report_sample_split(sample_split, sample_completely_split=True)
            return utils.successful_response(request, _("Samples were successfully split and/or renamed."),
                                             json_response=True)
    else:
        new_names = dict((int_or_zero(key[len("new-name-"):]), new_name)
                         for key, new_name in request.GET.items() if key.startswith("new-name-"))
        new_names.pop(0, None)
        original_data_forms, new_name_form_lists, global_new_data_form = \
            forms_from_database(request.user, deposition, remote_client, new_names)
    return render(request, "samples/split_after_deposition.html",
                  {"title": _("Bulk sample rename for {deposition}").format(deposition=deposition),
                   "samples": list(zip(original_data_forms, new_name_form_lists)),
                   "new_sample_data": global_new_data_form})
Ejemplo n.º 9
0
def edit(request, name):
    """View for editing an existing sample series.  Only the currently
    responsible person can edit a sample series.

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

    :type request: HttpRequest
    :type name: unicode

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    sample_series = get_object_or_404(models.SampleSeries, name=name)
    permissions.assert_can_edit_sample_series(request.user, sample_series)
    if request.method == "POST":
        sample_series_form = SampleSeriesForm(request.user, request.POST, instance=sample_series)
        edit_description_form = utils.EditDescriptionForm(request.POST)
        all_valid = sample_series_form.is_valid()
        all_valid = edit_description_form.is_valid() and all_valid
        referentially_valid = is_referentially_valid(sample_series, sample_series_form, edit_description_form)
        if all_valid and referentially_valid:
            edit_description = edit_description_form.cleaned_data
            feed_reporter = utils.Reporter(request.user)
            if sample_series.currently_responsible_person != sample_series_form.cleaned_data["currently_responsible_person"]:
                feed_reporter.report_new_responsible_person_sample_series(sample_series, edit_description)
            if sample_series.topic != sample_series_form.cleaned_data["topic"]:
                feed_reporter.report_changed_sample_series_topic(sample_series, sample_series.topic, edit_description)
            feed_reporter.report_edited_sample_series(sample_series, edit_description)
            sample_series = sample_series_form.save()
            feed_reporter.report_edited_sample_series(sample_series, edit_description)
            return utils.successful_response(
                request,
                _("Sample series {name} was successfully updated in the database.").format(name=sample_series.name))
    else:
        sample_series_form = \
            SampleSeriesForm(request.user, instance=sample_series,
                             initial={"short_name": sample_series.name.split("-", 2)[-1],
                                      "currently_responsible_person": sample_series.currently_responsible_person.pk,
                                      "topic": sample_series.topic.pk,
                                      "samples": [sample.pk for sample in sample_series.samples.all()]})
        edit_description_form = utils.EditDescriptionForm()
    return render(request, "samples/edit_sample_series.html",
                  {"title": _("Edit sample series “{name}”").format(name=sample_series.name),
                   "sample_series": sample_series_form,
                   "is_new": False, "edit_description": edit_description_form})
Ejemplo n.º 10
0
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")
Ejemplo n.º 11
0
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
        })
Ejemplo n.º 12
0
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
    })
Ejemplo n.º 13
0
def save_to_database(user, my_samples_form, action_form):
    """Execute the things that should be done with the selected “My Samples”.
    I do also the feed generation here.

    :param user: the user whose “My Samples” should be edited
    :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 user: django.contrib.auth.models.User
    :type my_samples_form: `MySamplesForm`
    :type action_form: `ActionForm`
    """
    action_data = action_form.cleaned_data
    if action_data["remove_from_my_samples"]:
        current_user_my_samples = user.my_samples
    samples = my_samples_form.cleaned_data["samples"]
    samples_with_new_responsible_person = []
    samples_with_new_topic = {}
    for sample in samples:
        old_topic, old_responsible_person = sample.topic, sample.currently_responsible_person
        if action_data["new_currently_responsible_person"] and \
                action_data["new_currently_responsible_person"] != sample.currently_responsible_person:
            sample.currently_responsible_person = action_data[
                "new_currently_responsible_person"]
            samples_with_new_responsible_person.append(sample)
        if action_data[
                "new_topic"] and action_data["new_topic"] != sample.topic:
            if sample.topic not in samples_with_new_topic:
                samples_with_new_topic[sample.topic] = [sample]
            else:
                samples_with_new_topic[sample.topic].append(sample)
            sample.topic = action_data["new_topic"]
        if action_data["new_current_location"]:
            sample.current_location = action_data["new_current_location"]
        if action_data["new_tags"]:
            sample.tags = "{old},{new}".format(
                old=sample.tags, new=action_data["new_tags"]).strip(",")
        sample.save()
        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)
        if sample.currently_responsible_person != old_responsible_person:
            sample.currently_responsible_person.my_samples.add(sample)
        if action_data["copy_to_user"]:
            for copy_user in action_data["copy_to_user"]:
                copy_user.my_samples.add(sample)
                if action_data["clearance"] is not None:
                    utils.enforce_clearance(user, action_data["clearance"],
                                            copy_user, sample)
        if action_data["remove_from_my_samples"]:
            current_user_my_samples.remove(sample)
    feed_reporter = utils.Reporter(user)
    edit_description = {
        "important": True,
        "description": action_data["comment"]
    }
    if samples_with_new_responsible_person:
        feed_reporter.report_new_responsible_person_samples(
            samples_with_new_responsible_person, edit_description)
    for old_topic, samples in samples_with_new_topic.items():
        feed_reporter.report_changed_sample_topic(samples, old_topic,
                                                  edit_description)
    if action_data["new_currently_responsible_person"] or action_data["new_current_location"] or action_data["new_topic"]\
        or action_data["new_tags"]:
        feed_reporter.report_edited_samples(samples, edit_description)
    # Now a separate(!) message for copied samples
    if action_data["copy_to_user"]:
        for copy_user in action_data["copy_to_user"]:
            utils.Reporter(user).report_copied_my_samples(
                samples, copy_user, action_data["comment"])
Ejemplo n.º 14
0
def add(request):
    """View for adding new samples.

    :param request: the current HTTP Request object

    :type request: HttpRequest

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    user = request.user
    if request.method == "POST":
        add_samples_form = AddSamplesForm(user, request.POST)
        if add_samples_form.is_valid():
            # FixMe: Find more reliable way to find stared sample names
            max_cycles = 10
            while max_cycles > 0:
                max_cycles -= 1
                try:
                    savepoint_without_samples = transaction.savepoint()
                    new_names, samples = add_samples_to_database(
                        add_samples_form, user)
                except IntegrityError:
                    if max_cycles > 0:
                        transaction.savepoint_rollback(
                            savepoint_without_samples)
                    else:
                        raise
                else:
                    break
            ids = [sample.pk for sample in samples]
            utils.Reporter(user).report_new_samples(samples)
            if add_samples_form.cleaned_data["topic"]:
                for watcher in (user_details.user
                                for user_details in add_samples_form.
                                cleaned_data["topic"].auto_adders.all()):
                    for sample in samples:
                        watcher.my_samples.add(sample)
            if len(new_names) > 1:
                success_report = \
                    _("Your samples have the names from {first_name} to {last_name}.  "
                      "They were added to “My Samples”.").format(first_name=new_names[0], last_name=new_names[-1])
            else:
                success_report = _("Your sample has the name {name}.  It was added to “My Samples”."). \
                    format(name=new_names[0])
            if add_samples_form.cleaned_data["rename"] == "new-style":
                return utils.successful_response(
                    request,
                    success_report,
                    "samples:bulk_rename",
                    query_string="ids=" + ",".join(str(id_) for id_ in ids),
                    forced=True,
                    json_response=ids)
            else:
                return utils.successful_response(request,
                                                 success_report,
                                                 json_response=ids)
    else:
        add_samples_form = AddSamplesForm(user)
    return render(
        request, "samples/add_samples.html", {
            "title": _("Add samples"),
            "add_samples": add_samples_form,
            "external_operators_available": user.external_contacts.exists()
        })