Exemple #1
0
def get_all_addable_physical_process_models():
    """Get all physical process classes (depositions, measurements; no sample
    splits) that one can add or edit.  Never call this routine from top-level
    module code because it may cause cyclic imports.

    :return:
      Dictionary mapping all physical processes one can to add.  Every process
      class is mapped to a dictionary with three keys, namely ``"url"`` with
      the url to the “add” view for the process, ``"label"`` with the name of
      the process (starting lowercase), and ``"type"`` with the process'
      class name.

    :rtype: dict mapping class to dict mapping str to unicode
    """
    global all_addable_physical_process_models
    if all_addable_physical_process_models is None:
        all_addable_physical_process_models = {}
        for process_class in utils.get_all_models().values():
            if issubclass(process_class, samples.models.PhysicalProcess):
                url = process_class.get_add_link()
                if url:
                    all_addable_physical_process_models[process_class] = {
                        "url": url,
                        "label": process_class._meta.verbose_name,
                        "label_plural":
                        process_class._meta.verbose_name_plural,
                        "type": process_class.__name__
                    }
    return all_addable_physical_process_models
Exemple #2
0
def get_all_searchable_models():
    """Returns all model classes which have a ``get_search_tree_node`` method.

    :return:
      all searchable model classes

    :rtype: frozenset of ``class``
    """
    global all_searchable_models
    if not isinstance(all_searchable_models, frozenset):
        if isinstance(all_searchable_models, set):
            raise SetLockedException
        all_searchable_models = set()
        for model in utils.get_all_models().values():
            if hasattr(model, "get_search_tree_node"):
                try:
                    model.get_search_tree_node()
                except NotImplementedError:
                    pass
                except SetLockedException:
                    all_searchable_models.add(model)
                else:
                    all_searchable_models.add(model)
    all_searchable_models = frozenset(all_searchable_models)
    return all_searchable_models
Exemple #3
0
def get_all_addable_physical_process_models():
    """Get all physical process classes (depositions, measurements; no sample
    splits) that one can add or edit.  Never call this routine from top-level
    module code because it may cause cyclic imports.

    :return:
      Dictionary mapping all physical processes one can to add.  Every process
      class is mapped to a dictionary with three keys, namely ``"url"`` with
      the url to the “add” view for the process, ``"label"`` with the name of
      the process (starting lowercase), and ``"type"`` with the process'
      class name.

    :rtype: dict mapping class to dict mapping str to unicode
    """
    global all_addable_physical_process_models
    if all_addable_physical_process_models is None:
        all_addable_physical_process_models = {}
        for process_class in utils.get_all_models().values():
            if issubclass(process_class, samples.models.PhysicalProcess):
                url = process_class.get_add_link()
                if url:
                    all_addable_physical_process_models[process_class] = {
                        "url": url, "label": process_class._meta.verbose_name,
                        "label_plural": process_class._meta.verbose_name_plural, "type": process_class.__name__}
    return all_addable_physical_process_models
Exemple #4
0
def get_all_searchable_models():
    """Returns all model classes which have a ``get_search_tree_node`` method.

    :return:
      all searchable model classes

    :rtype: frozenset of ``class``
    """
    global all_searchable_models
    if not isinstance(all_searchable_models, frozenset):
        if isinstance(all_searchable_models, set):
            raise SetLockedException
        all_searchable_models = set()
        for model in utils.get_all_models().values():
            if hasattr(model, "get_search_tree_node"):
                try:
                    model.get_search_tree_node()
                except NotImplementedError:
                    pass
                except SetLockedException:
                    all_searchable_models.add(model)
                else:
                    all_searchable_models.add(model)
    all_searchable_models = frozenset(all_searchable_models)
    return all_searchable_models
Exemple #5
0
def show(request, process_name, year_and_month):
    """View for showing one month of the lab notebook for a particular
    physical process.  In ``urls.py``, you must give the entry for this view
    the name ``"lab_notebook_<camel_case_process_name>"``.

    :param request: the current HTTP Request object
    :param process_name: the class name of the model of the physical process,
        e.g. ``"LargeAreaDeposition"``
    :param year_and_month: the year and month to be displayed in the format
        ``YYYY/MM`` (the month may be single-digit)

    :type request: HttpRequest
    :type process_name: str
    :type year_and_month: str

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    process_class = get_all_models()[process_name]
    process_name = camel_case_to_underscores(process_name)
    permissions.assert_can_view_lab_notebook(request.user, process_class)
    if not year_and_month:
        try:
            timestamp = process_class.objects.latest().timestamp
        except process_class.DoesNotExist:
            timestamp = datetime.datetime.today()
        return HttpResponseSeeOther("{0}/{1}".format(timestamp.year, timestamp.month))
    year, month = parse_year_and_month(year_and_month)
    if request.method == "POST":
        year_month_form = YearMonthForm(request.POST)
        if year_month_form.is_valid():
            return HttpResponseSeeOther(django.core.urlresolvers.reverse(
                    "lab_notebook_" + process_name,
                    kwargs={"year_and_month": "{year}/{month}".format(**year_month_form.cleaned_data)}))
    else:
        year_month_form = YearMonthForm(initial={"year": year, "month": month})
    template = loader.get_template("samples/lab_notebook_" + process_name + ".html")
    template_context = RequestContext(request, process_class.get_lab_notebook_context(year, month))
    html_body = template.render(template_context)
    previous_url, next_url = get_previous_next_urls(process_name, year, month)
    try:
        export_url = django.core.urlresolvers.reverse(
            "export_lab_notebook_" + process_name,
            kwargs={"year_and_month": year_and_month}) + "?next=" + urlquote_plus(request.path)
    except django.core.urlresolvers.NoReverseMatch:
        export_url = None
    return render(request, "samples/lab_notebook.html",
                  {"title": capitalize_first_letter(_("lab notebook for {process_name}")
                                                    .format(process_name=process_class._meta.verbose_name_plural)),
                   "year": year, "month": month, "year_month": year_month_form,
                   "html_body": html_body, "previous_url": previous_url, "next_url": next_url,
                   "export_url": export_url})
Exemple #6
0
    def parse_data(self, data, prefix):
        """Create all forms associated with this node (all the seach fields,
        and the `SearchModelForm` for all children), and create recursively the
        tree by creating the children.

        :param data: the GET dictionary of the request; may be ``None`` if the
            forms of this node are supposed to be unbound (because it was newly
            created and there's nothing to be parsed into them)
        :param prefix: The prefix for the forms.  Note that the form to select a
            model does not belong to the model to be selected but to its parent
            model.  This is also true for the prefixes: The top-level selection
            form (called ``root_form`` in
            ``samples.view.sample.advanced_search``) doesn't have a prefix,
            neither have the top-level search fields.  The children of a node
            have the next nesting depth of the prefix, including the
            `SearchModelForm` in which they were selected.  The starting number
            of prefixes is 1, and the nesting levels are separated by dashs.

        :type data: QueryDict or NoneType
        :type prefix: str
        """
        for search_field in self.search_fields:
            search_field.parse_data(data, prefix)
        data = data or {}
        depth = prefix.count("-") + (2 if prefix else 1)
        keys = [key for key in data if key.count("-") == depth]
        i = 1
        while True:
            new_prefix = prefix + ("-" if prefix else "") + str(i)
            if not data.get(new_prefix + "-_model"):
                break
            search_model_form = SearchModelForm(self.related_models.keys(),
                                                data,
                                                prefix=new_prefix)
            if not search_model_form.is_valid():
                break
            model_name = data[new_prefix + "-_model"]
            node = utils.get_all_models()[model_name].get_search_tree_node()
            parse_node = search_model_form.cleaned_data[
                "_model"] == search_model_form.cleaned_data["_old_model"]
            node.parse_data(data if parse_node else None, new_prefix)
            search_model_form = SearchModelForm(
                self.related_models.keys(),
                initial={
                    "_old_model": search_model_form.cleaned_data["_model"],
                    "_model": search_model_form.cleaned_data["_model"]
                },
                prefix=new_prefix)
            self.children.append((search_model_form, node))
            i += 1
        if self.related_models:
            self.children.append((SearchModelForm(self.related_models.keys(),
                                                  prefix=new_prefix), None))
Exemple #7
0
def get_addable_models(user):
    """Return a list with all registered addable model classes the user is
    allowed to see (the classes per se; this is not about the visibility of the
    model instances).  Their type is of `PermissionsModels`, which
    means that they contain information about the users who have permissions
    for that model.

    You can see all addable model classes of your department.  A superuser
    can see all classes.

    The result is used to build the list of apparatuses for which one can set
    permissions.

    :param user:  The user for which the classes are returned that he is allowed
        to see.

    :type user: django.contrib.auth.models.User

    :return:
      all addable models for the user

    :rtype: list of `django.db.models.Model`
    """
    all_addable_models = []
    for model in get_all_models().values():
        if model._meta.app_label not in ["samples", "jb_common"]:
            permission_codename = "edit_permissions_for_{0}".format(
                model.__name__.lower())
            content_type = ContentType.objects.get_for_model(model)
            try:
                Permission.objects.get(codename=permission_codename,
                                       content_type=content_type)
            except Permission.DoesNotExist:
                continue
            else:
                all_addable_models.append(model)

    if not user.is_superuser:
        user_department = user.jb_user_details.department
        if user_department:
            all_addable_models = [
                model for model in all_addable_models
                if model._meta.app_label == user_department.app_label
            ]
        else:
            all_addable_models = []
    all_addable_models.sort(
        key=lambda model: model._meta.verbose_name_plural.lower())
    all_addable_models = [
        PermissionsModels(model) for model in all_addable_models
    ]
    return all_addable_models
Exemple #8
0
def show_process(request, process_id, process_name="Process"):
    """Show an existing physical process.  This is some sort of fallback view in
    case a process doesn't provide its own show view (which is mostly the
    case).

    The ``process_id`` needn't be the ``"id"`` field: If `process_name` is not
    ``None``, its ``JBMeta.identifying_field``, it given, is used instead for
    the lookup.

    :param request: the current HTTP Request object
    :param process_id: the ID or the process's identifying field value
    :param process_name: the class name of the process; if ``None``, ``Process``
        is assumed

    :type request: HttpRequest
    :type process_id: unicode
    :type process_name: unicode

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    process_class = get_all_models()[process_name]
    try:
        identifying_field = process_class.JBMeta.identifying_field
    except AttributeError:
        identifying_field = "id"
    try:
        process = get_object_or_404(process_class, **{
            identifying_field: process_id
        }).actual_instance
    except ValueError:
        raise Http404("Invalid value for {} passed: {}".format(
            identifying_field, repr(process_id)))
    if not isinstance(process, models.PhysicalProcess):
        raise Http404("No physical process with that ID was found.")
    permissions.assert_can_view_physical_process(request.user, process)
    if is_json_requested(request):
        return respond_in_json(process.get_data())
    template_context = {
        "title": six.text_type(process),
        "samples": process.samples.all(),
        "process": process
    }
    template_context.update(utils.digest_process(process, request.user))
    return render(request, "samples/show_process.html", template_context)
Exemple #9
0
    def parse_data(self, data, prefix):
        """Create all forms associated with this node (all the seach fields,
        and the `SearchModelForm` for all children), and create recursively the
        tree by creating the children.

        :param data: the GET dictionary of the request; may be ``None`` if the
            forms of this node are supposed to be unbound (because it was newly
            created and there's nothing to be parsed into them)
        :param prefix: The prefix for the forms.  Note that the form to select a
            model does not belong to the model to be selected but to its parent
            model.  This is also true for the prefixes: The top-level selection
            form (called ``root_form`` in
            ``samples.view.sample.advanced_search``) doesn't have a prefix,
            neither have the top-level search fields.  The children of a node
            have the next nesting depth of the prefix, including the
            `SearchModelForm` in which they were selected.  The starting number
            of prefixes is 1, and the nesting levels are separated by dashs.

        :type data: QueryDict or NoneType
        :type prefix: str
        """
        for search_field in self.search_fields:
            search_field.parse_data(data, prefix)
        data = data or {}
        depth = prefix.count("-") + (2 if prefix else 1)
        keys = [key for key in data if key.count("-") == depth]
        i = 1
        while True:
            new_prefix = prefix + ("-" if prefix else "") + str(i)
            if not data.get(new_prefix + "-_model"):
                break
            search_model_form = SearchModelForm(self.related_models.keys(), data, prefix=new_prefix)
            if not search_model_form.is_valid():
                break
            model_name = data[new_prefix + "-_model"]
            node = utils.get_all_models()[model_name].get_search_tree_node()
            parse_node = search_model_form.cleaned_data["_model"] == search_model_form.cleaned_data["_old_model"]
            node.parse_data(data if parse_node else None, new_prefix)
            search_model_form = SearchModelForm(self.related_models.keys(),
                                                initial={"_old_model": search_model_form.cleaned_data["_model"],
                                                         "_model": search_model_form.cleaned_data["_model"]},
                                                prefix=new_prefix)
            self.children.append((search_model_form, node))
            i += 1
        if self.related_models:
            self.children.append((SearchModelForm(self.related_models.keys(), prefix=new_prefix), None))
Exemple #10
0
def get_addable_models(user):
    """Return a list with all registered addable model classes the user is
    allowed to see (the classes per se; this is not about the visibility of the
    model instances).  Their type is of `PermissionsModels`, which
    means that they contain information about the users who have permissions
    for that model.

    You can see all addable model classes of your department.  A superuser
    can see all classes.

    The result is used to build the list of apparatuses for which one can set
    permissions.

    :param user:  The user for which the classes are returned that he is allowed
        to see.

    :type user: django.contrib.auth.models.User

    :return:
      all addable models for the user

    :rtype: list of `django.db.models.Model`
    """
    all_addable_models = []
    for model in get_all_models().values():
        if model._meta.app_label not in ["samples", "jb_common"]:
            permission_codename = "edit_permissions_for_{0}".format(model.__name__.lower())
            content_type = ContentType.objects.get_for_model(model)
            try:
                Permission.objects.get(codename=permission_codename, content_type=content_type)
            except Permission.DoesNotExist:
                continue
            else:
                all_addable_models.append(model)

    if not user.is_superuser:
        user_department = user.jb_user_details.department
        if user_department:
            all_addable_models = [model for model in all_addable_models
                                      if model._meta.app_label == user_department.app_label]
        else:
            all_addable_models = []
    all_addable_models.sort(key=lambda model: model._meta.verbose_name_plural.lower())
    all_addable_models = [PermissionsModels(model) for model in all_addable_models]
    return all_addable_models
Exemple #11
0
def export(request, process_name, year_and_month):
    """View for exporting the data of a month of a lab notebook.  Thus, the
    return value is not an HTML response but a CSV or JSON response.  In
    ``urls.py``, you must give the entry for this view the name
    ``"export_lab_notebook_<process_name>"``.

    :param request: the current HTTP Request object
    :param process_name: the class name of the model of the physical process,
        e.g. ``"LargeAreaDeposition"``
    :param year_and_month: the year and month to be displayed in the format
        ``YYYY/MM`` (the month may be single-digit)

    :type request: HttpRequest
    :type process_name: str
    :type year_and_month: str

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    process_class = get_all_models()[process_name]
    permissions.assert_can_view_lab_notebook(request.user, process_class)
    year, month = parse_year_and_month(year_and_month)
    data = process_class.get_lab_notebook_data(year, month)
    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", "")
        })
Exemple #12
0
def export(request, process_name, year_and_month):
    """View for exporting the data of a month of a lab notebook.  Thus, the
    return value is not an HTML response but a CSV or JSON response.  In
    ``urls.py``, you must give the entry for this view the name
    ``"export_lab_notebook_<process_name>"``.

    :param request: the current HTTP Request object
    :param process_name: the class name of the model of the physical process,
        e.g. ``"LargeAreaDeposition"``
    :param year_and_month: the year and month to be displayed in the format
        ``YYYY/MM`` (the month may be single-digit)

    :type request: HttpRequest
    :type process_name: str
    :type year_and_month: str

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    process_class = get_all_models()[process_name]
    permissions.assert_can_view_lab_notebook(request.user, process_class)
    year, month = parse_year_and_month(year_and_month)
    data = process_class.get_lab_notebook_data(year, month)
    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", "")})
Exemple #13
0
 def __init__(self, user, *args, **kwargs):
     super(UserDetailsForm, self).__init__(*args, **kwargs)
     self.fields["auto_addition_topics"].queryset = user.topics
     choices = []
     processes = [process_class for process_class in jb_common_utils.get_all_models().values()
                 if issubclass(process_class, models.Process) and not process_class._meta.abstract
                 and process_class not in [models.Process, models.Deposition]]
     for department in user.samples_user_details.show_users_from_departments.iterator():
         process_from_department = set(process for process in processes
                                       if process._meta.app_label == department.app_label)
         choices.append((department.name, utils.choices_of_content_types(process_from_department)))
     if not choices:
         choices = (("", 9 * "-"),)
     self.fields["default_folded_process_classes"].choices = choices
     self.fields["default_folded_process_classes"].initial = [content_type.id for content_type
                                                  in user.samples_user_details.default_folded_process_classes.iterator()]
     self.fields["default_folded_process_classes"].widget.attrs["size"] = "15"
     self.fields["subscribed_feeds"].choices = utils.choices_of_content_types(
         list(get_all_addable_physical_process_models()) + [models.Sample, models.SampleSeries, Topic])
     self.fields["subscribed_feeds"].widget.attrs["size"] = "15"
     self.fields["show_users_from_departments"].choices = [(department.pk, department.name)
                                                         for department in Department.objects.iterator()]
     self.fields["show_users_from_departments"].initial = \
                 user.samples_user_details.show_users_from_departments.values_list("id", flat=True)
Exemple #14
0
 def __init__(self, user, *args, **kwargs):
     super(UserDetailsForm, self).__init__(*args, **kwargs)
     self.fields["auto_addition_topics"].queryset = user.topics
     choices = []
     processes = [process_class for process_class in jb_common_utils.get_all_models().values()
                 if issubclass(process_class, models.Process) and not process_class._meta.abstract
                 and process_class not in [models.Process, models.Deposition]]
     for department in user.samples_user_details.show_users_from_departments.iterator():
         processes_from_department = set(process for process in processes
                                         if process._meta.app_label == department.app_label)
         choices.append((department.name, utils.choices_of_content_types(processes_from_department)))
     if not choices:
         choices = (("", 9 * "-"),)
     self.fields["default_folded_process_classes"].choices = choices
     self.fields["default_folded_process_classes"].initial = [content_type.id for content_type
                                                  in user.samples_user_details.default_folded_process_classes.iterator()]
     self.fields["default_folded_process_classes"].widget.attrs["size"] = "15"
     self.fields["subscribed_feeds"].choices = utils.choices_of_content_types(
         list(get_all_addable_physical_process_models()) + [models.Sample, models.SampleSeries, Topic])
     self.fields["subscribed_feeds"].widget.attrs["size"] = "15"
     self.fields["show_users_from_departments"].choices = [(department.pk, department.name)
                                                         for department in Department.objects.iterator()]
     self.fields["show_users_from_departments"].initial = \
                 user.samples_user_details.show_users_from_departments.values_list("id", flat=True)
Exemple #15
0
def show(request, process_name, year_and_month):
    """View for showing one month of the lab notebook for a particular
    physical process.  In ``urls.py``, you must give the entry for this view
    the name ``"lab_notebook_<camel_case_process_name>"``.

    :param request: the current HTTP Request object
    :param process_name: the class name of the model of the physical process,
        e.g. ``"LargeAreaDeposition"``
    :param year_and_month: the year and month to be displayed in the format
        ``YYYY/MM`` (the month may be single-digit)

    :type request: HttpRequest
    :type process_name: str
    :type year_and_month: str

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    process_class = get_all_models()[process_name]
    process_name = camel_case_to_underscores(process_name)
    namespace = process_class._meta.app_label
    permissions.assert_can_view_lab_notebook(request.user, process_class)
    if not year_and_month:
        try:
            timestamp = process_class.objects.latest().timestamp
        except process_class.DoesNotExist:
            timestamp = datetime.datetime.today()
        return HttpResponseSeeOther("{0}/{1}".format(timestamp.year,
                                                     timestamp.month))
    year, month = parse_year_and_month(year_and_month)
    if request.method == "POST":
        year_month_form = YearMonthForm(request.POST)
        if year_month_form.is_valid():
            return HttpResponseSeeOther(
                django.core.urlresolvers.reverse(
                    "{}:lab_notebook_{}".format(namespace, process_name),
                    kwargs={
                        "year_and_month":
                        "{year}/{month}".format(**year_month_form.cleaned_data)
                    }))
    else:
        year_month_form = YearMonthForm(initial={"year": year, "month": month})
    template = loader.get_template("samples/lab_notebook_" + process_name +
                                   ".html")
    template_context = RequestContext(
        request, process_class.get_lab_notebook_context(year, month))
    html_body = template.render(template_context.flatten())
    previous_url, next_url = get_previous_next_urls(process_name, namespace,
                                                    year, month)
    try:
        export_url = django.core.urlresolvers.reverse(
            "{}:export_lab_notebook_{}".format(namespace, process_name),
            kwargs={"year_and_month": year_and_month
                    }) + "?next=" + urlquote_plus(request.path)
    except django.core.urlresolvers.NoReverseMatch:
        export_url = None
    return render(
        request, "samples/lab_notebook.html", {
            "title":
            capitalize_first_letter(
                _("lab notebook for {process_name}").format(
                    process_name=process_class._meta.verbose_name_plural)),
            "year":
            year,
            "month":
            month,
            "year_month":
            year_month_form,
            "html_body":
            html_body,
            "previous_url":
            previous_url,
            "next_url":
            next_url,
            "export_url":
            export_url
        })
Exemple #16
0
def advanced_search(request):
    """View for searching for samples, sample series, physical processes, and
    results.  The visibility rules of the search results are the same as for
    the sample search.  Additionally, you can only see sample series you are
    the currently responsible person of or that are in one of your topics.

    A POST request on this URL will add samples to the “My Samples” list.
    *All* search parameters are in the query string, so if you just want to
    search, this is a GET requets.  Therefore, this view has two submit
    buttons.

    :param request: the current HTTP Request object

    :type request: HttpRequest

    :return:
      the HTTP response object

    :rtype: HttpResponse
    """
    model_list = [model for model in jb_common.search.get_all_searchable_models() if hasattr(model, "get_absolute_url")]
    search_tree = None
    results, add_forms = [], []
    too_many_results = False
    root_form = jb_common.search.SearchModelForm(model_list, request.GET)
    search_performed = False
    no_permission_message = None
    _search_parameters_hash = hashlib.sha1(json.dumps(sorted(dict((key, value) for key, value in request.GET.items()
                                    if not "__" in key and key != "_search_parameters_hash").items())).encode("utf-8")).hexdigest()
    column_groups_form = columns_form = table = switch_row_forms = old_data_form = None
    if root_form.is_valid() and root_form.cleaned_data["_model"]:
        search_tree = get_all_models()[root_form.cleaned_data["_model"]].get_search_tree_node()
        parse_tree = root_form.cleaned_data["_model"] == root_form.cleaned_data["_old_model"]
        search_tree.parse_data(request.GET if parse_tree else None, "")
        if search_tree.is_valid():
            if search_tree.model_class == models.Sample:
                base_query = utils.restricted_samples_query(request.user)
            elif search_tree.model_class == models.SampleSeries:
                base_query = models.SampleSeries.objects.filter(
                    Q(topic__confidential=False) | Q(topic__members=request.user) |
                    Q(currently_responsible_person=request.user)).distinct()
            else:
                base_query = None
            results, too_many_results = jb_common.search.get_search_results(search_tree, max_results, base_query)
            if search_tree.model_class == models.Sample:
                if request.method == "POST":
                    sample_ids = set(int_or_zero(key[2:].partition("-")[0]) for key, value in request.POST.items() if value == "on")
                    samples = base_query.in_bulk(sample_ids).values()
                    request.user.my_samples.add(*samples)
                my_samples = request.user.my_samples.all()
                add_forms = [AddToMySamplesForm(prefix="0-" + str(sample.pk)) if sample not in my_samples else None
                             for sample in results]
            else:
                add_forms = len(results) * [None]
            if results and root_form.cleaned_data["_search_parameters_hash"] == _search_parameters_hash:
                data_node = data_tree.DataNode(_("search results"))
                for result in results:
                    insert = False
                    if isinstance(result, models.PhysicalProcess) \
                        and permissions.has_permission_to_view_physical_process(request.user, result):
                            insert = True
                    elif isinstance(result, models.Result) \
                        and permissions.has_permission_to_view_result_process(request.user, result):
                            insert = True
                    elif isinstance(result, models.Sample) \
                        and permissions.has_permission_to_fully_view_sample(request.user, result):
                            insert = True
                    elif isinstance(result, models.SampleSeries) \
                        and permissions.has_permission_to_view_sample_series(request.user, result):
                            insert = True
                    if insert:
                        data_node.children.append(result.get_data_for_table_export())
                if len(data_node.children) == 0:
                    no_permission_message = _("You don't have the permission to see any content of the search results.")
                else:
                    export_result = utils.table_export(request, data_node, "")
                    if isinstance(export_result, tuple):
                        column_groups_form, columns_form, table, switch_row_forms, old_data_form = export_result
                    elif isinstance(export_result, HttpResponse):
                        return export_result
            search_performed = True
        root_form = jb_common.search.SearchModelForm(
            model_list, initial={"_old_model": root_form.cleaned_data["_model"], "_model": root_form.cleaned_data["_model"],
                                 "_search_parameters_hash": _search_parameters_hash})
    else:
        root_form = jb_common.search.SearchModelForm(model_list)
    root_form.fields["_model"].label = ""
    content_dict = {"title": capfirst(_("advanced search")), "search_root": root_form, "search_tree": search_tree,
                    "results": list(zip(results, add_forms)), "search_performed": search_performed,
                    "something_to_add": any(add_forms), "too_many_results": too_many_results, "max_results": max_results,
                    "column_groups": column_groups_form, "columns": columns_form, "old_data": old_data_form,
                    "rows": list(zip(table, switch_row_forms)) if table else None,
                    "no_permission_message": no_permission_message}
    return render(request, "samples/advanced_search.html", content_dict)