Skip to content

Commit

Permalink
improved case-list-view
Browse files Browse the repository at this point in the history
* added range slider widget

* added "party" to fulltext index #43

* adding info modal to fulltext search #43

* included search info modal also in case-listview

* wip form for case search; #43
some js-magic was needed to align select forms initially hidden in collapsed accordion

* more case-form (styling) things #43
  • Loading branch information
csae8092 authored Apr 22, 2024
1 parent 1fab67b commit 97a2b94
Show file tree
Hide file tree
Showing 23 changed files with 284 additions and 23 deletions.
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[flake8]
ignore = D203 W504
ignore = D203 W504 W503
max-line-length = 120
exclude =
*/migrations,
Expand Down
Empty file added acdh_django_widgets/__init__.py
Empty file.
3 changes: 3 additions & 0 deletions acdh_django_widgets/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# from django.contrib import admin

# Register your models here.
6 changes: 6 additions & 0 deletions acdh_django_widgets/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class AcdhDjangoWidgetsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "acdh_django_widgets"
Empty file.
3 changes: 3 additions & 0 deletions acdh_django_widgets/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# from django.db import models

# Create your models here.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<div id="{{ name }}_slider" class="slider"></div>
<div class="pt-2 container d-flex justify-content-center {% if attrs.hide_input_fileds %} d-none {% endif %}">
<div class="row">
<div class="col-5">
<input id="id_{{ name }}_0" name="{{ name }}_min" type="text" value="{{ value.0 }}"
class="textinput form-control">
</div>
<div class="col-auto align-self-center">
<span class="fs-4"></span>
</div>
<div class="col-5">
<input id="id_{{ name }}_1" name="{{ name }}_max" type="text" value="{{ value.1 }}"
class="textinput form-control">
</div>
</div>
</div>
<script>
var minValue = {{ attrs.min }};
var maxValue = {{ attrs.max }};
var sliderId = "{{ name }}_slider";
var minValForm = document.getElementById("id_{{ name }}_0");
var maxValForm = document.getElementById("id_{{ name }}_1");
var startValue = minValForm.value;
var endValue = maxValForm.value;
var slider = document.getElementById(sliderId);

noUiSlider.create(slider, {
start: [startValue, endValue],
step: 1,
connect: true,
tooltips: true,
format: wNumb({
decimals: 0
}),
range: {
"min": minValue,
"max": maxValue
}
});

slider.noUiSlider.on('update', function (values, handle) {

var value = values[handle];
if (handle) {
maxValForm.value = value;
} else {
minValForm.value = value;
}
});

minValForm.addEventListener('change', function () {
slider.noUiSlider.set([this.value, null]);
});

maxValForm.addEventListener('change', function () {
slider.noUiSlider.set([null, this.value]);
});
</script>
3 changes: 3 additions & 0 deletions acdh_django_widgets/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# from django.test import TestCase

# Create your tests here.
3 changes: 3 additions & 0 deletions acdh_django_widgets/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# from django.shortcuts import render

# Create your views here.
25 changes: 25 additions & 0 deletions acdh_django_widgets/widgets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import django_filters
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe


class RangeSliderWidget(django_filters.widgets.RangeWidget):
template_name = "acdh_django_widgets/range_slider_widget.html"

class Media:
css = {
"all": (
"https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.7.1/nouislider.css",
)
}
js = (
"https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.7.1/nouislider.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/wnumb/1.2.0/wNumb.min.js",
)

def render(self, name, value, attrs=None, renderer=None):
if value[0] is None:
value = [self.attrs.get("min", 0), self.attrs.get("max", 100)]
context = {"name": name, "value": value, "attrs": self.attrs}
rendered = render_to_string(self.template_name, context)
return mark_safe(rendered)
13 changes: 12 additions & 1 deletion archiv/dal_views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# generated by appcreator
from dal import autocomplete
from django.db.models import Q
from django.utils.html import format_html

from .models import Court
from .models import CourtDecission
Expand Down Expand Up @@ -30,11 +31,18 @@ def get_queryset(self):


class CourtAC(autocomplete.Select2QuerySetView):
def get_result_label(self, item):
return f"{item.name} ({item.name_english}) {item.abbreviation}"

def get_queryset(self):
qs = Court.objects.all()

if self.q:
qs = qs.filter(Q(name__icontains=self.q))
qs = qs.filter(
Q(name__icontains=self.q)
| Q(name_english__icontains=self.q)
| Q(abbreviation__startswith=self.q)
)
return qs


Expand All @@ -48,6 +56,9 @@ def get_queryset(self):


class KeyWordAC(autocomplete.Select2QuerySetView):
def get_result_label(self, result):
return format_html('<span class="badge rounded-pill text-bg-primary ">{}</span>', result.name)

def get_queryset(self):
qs = KeyWord.objects.all()

Expand Down
18 changes: 17 additions & 1 deletion archiv/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from django.contrib.postgres.search import SearchRank
from django.db.utils import ProgrammingError

from acdh_django_widgets.widgets import RangeSliderWidget

from .models import Court
from .models import CourtDecission
from .models import KeyWord
Expand All @@ -16,8 +18,10 @@

START_SELECTOR = '<span class="text-nowrap bg-warning border-warning rounded border-5">'


FT_HELPTEXT = """
Fuzzy Fulltext Search
<i class="bi bi-info-circle" role="button" title="Click for information about the full text search"
data-bs-toggle="modal" data-bs-target="#searchInfoModal"></i>
"""


Expand All @@ -38,6 +42,9 @@ class YearBookListFilter(django_filters.FilterSet):
year = django_filters.RangeFilter(
help_text=YearBook._meta.get_field("year").help_text,
label=YearBook._meta.get_field("year").verbose_name,
widget=RangeSliderWidget(
attrs={"min": "1990", "max": "2022", "hide_input_fileds": False}
),
)

class Meta:
Expand Down Expand Up @@ -125,6 +132,11 @@ class CourtDecissionListFilter(django_filters.FilterSet):
help_text=CourtDecission._meta.get_field("file_number").help_text,
label=CourtDecission._meta.get_field("file_number").verbose_name,
)
ecli = django_filters.CharFilter(
lookup_expr="icontains",
help_text=CourtDecission._meta.get_field("ecli").help_text,
label=CourtDecission._meta.get_field("ecli").verbose_name,
)
party = django_filters.CharFilter(
lookup_expr="icontains",
help_text=CourtDecission._meta.get_field("party").help_text,
Expand Down Expand Up @@ -166,6 +178,7 @@ class CourtDecissionListFilter(django_filters.FilterSet):
label=CourtDecission._meta.get_field("keyword").verbose_name,
widget=autocomplete.Select2Multiple(
url="archiv-ac:keyword-autocomplete",
attrs={'data-html': True}
),
)
author = django_filters.ModelMultipleChoiceFilter(
Expand All @@ -179,6 +192,9 @@ class CourtDecissionListFilter(django_filters.FilterSet):
decission_date__year = django_filters.RangeFilter(
help_text="Year of Decision.",
label=CourtDecission._meta.get_field("decission_date").verbose_name,
widget=RangeSliderWidget(
attrs={"min": "1990", "max": "2022", "hide_input_fileds": False}
),
)

def search_fulltext(self, queryset, field_name, value):
Expand Down
30 changes: 18 additions & 12 deletions archiv/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,25 +116,31 @@ def __init__(self, *args, **kwargs):
BS5Accordion(
AccordionGroup(
"Basic Search",
"id",
"ft_search",
"partial_legal_system",
"court",
"year_book_title",
"decission_date__year",
"file_number",
"party",
"location",
"short_description",
"situation",
"motto",
"commentary",
"additional_information",
# "location",
# "short_description",
# "situation",
# "motto",
# "commentary",
# "additional_information",
"keyword",
"tag",
css_id="basic",
),
AccordionGroup(
"Extended search fields",
"party",
"file_number",
"id",
"ecli",
"year_book_title",
"author",
css_id="more",
)
css_id="extended",
),
always_open=True,
)
)

Expand Down
1 change: 1 addition & 0 deletions archiv/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@ def search_field_names(self):
"motto",
"commentary",
"additional_information",
"party",
]
return text_fields

Expand Down
4 changes: 3 additions & 1 deletion archiv/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ class CourtDecissionTable(tables.Table):
keyword = tables.columns.ManyToManyColumn(verbose_name="Keywords")
author = tables.columns.ManyToManyColumn(verbose_name="Authors")
kwic = tables.columns.TemplateColumn(
template_code="{{ record.kwic|safe }}", verbose_name="Keyword in Context"
template_code="{{ record.kwic|safe }}",
verbose_name="Keyword in Context",
orderable=False,
)
rank = tables.columns.TemplateColumn(
template_code="{{ record.rank|safe }}", verbose_name="Search Rank"
Expand Down
87 changes: 87 additions & 0 deletions archiv/templates/archiv/case_listview.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
{% extends "webpage/base.html" %}
{% load static %}
{% load django_tables2 %}
{% load browsing_extras %}
{% load crispy_forms_field %}
{% load crispy_forms_tags %}
{% block title %} Browse {{ class_name }} {% endblock %}
{% block scriptHeader %}

{% endblock %}
{% block content %}
{% include 'archiv/partials/listview_breadcrumb.html' %}
<div class="container-fluid">
<div class="card-header text-center">

<h1 class="text-center">{{table.paginator.count }} {{ verbose_name_plural }}</h1>
{% if user.is_authenticated %}
{% if create_view_link %}
<div class="d-grid gap-2">
<a class="btn btn-primary float-center ms-5 me-5" href="{{ create_view_link }}">Create new {{ class_name
}}</a>
</div>
{% endif %}
{% endif %}
</div>
<div>
<div class="row">
<div class="col-md-4">

<div>
{% block create_button %}{% endblock %}
<!--Search mask-->
{% load django_tables2 crispy_forms_tags %}
<form action="." class="uniForm" method="get">
{% crispy filter.form filter.form.helper %}
{% include 'browsing/partials/column_selector.html' %}
<a class="btn btn-primary" href=".">Reset</a>
<button type="submit" class="btn btn-primary">Search</button>
</form>
{% include 'browsing/partials/chart_form.html' %}
</div>
</div>
<div class="col-md-8" id="results">


<div class="card-body table-responsive">
{% block table %}
{% include 'browsing/partials/table.html' %}
{% endblock table %}
{% block pagination.allpages %}
{% include 'browsing/partials/pagination.html' %}
{% endblock pagination.allpages %}
<div class="float-end">
{% include "browsing/partials/download_menu.html" %}
</div>
</div>

</div>
</div>
</div>
</div>
{% include 'archiv/partials/info_modal.html' %}



{% endblock %}
{% block scripts2 %}
<script src="{% static 'browsing/js/set-form-attributes.js' %}"></script>
<script src="{% static 'browsing/js/filter-for-blank-fields.js' %}"></script>
<script>

document.addEventListener('DOMContentLoaded', function () {
var accordion = document.getElementsByClassName('accordion')[0];
accordion.addEventListener('shown.bs.collapse', function (event) {
var shownElement = event.target;
var allSelect2Spans = document.getElementsByClassName("select2");
var firstSpan = allSelect2Spans[0];
var widthToset = allSelect2Spans[0].style["width"];
for (var i = 0; i < allSelect2Spans.length; i++) {
allSelect2Spans[i].style.width = widthToset;
}
});
});


</script>
{% endblock scripts2 %}
23 changes: 23 additions & 0 deletions archiv/templates/archiv/partials/info_modal.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<div class="modal" id="searchInfoModal" tabindex="-1" aria-labelledby="searchInfoModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Search information</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>With this search, the fields "Short Description", "Facts", "Decision", "Commentary", "Additional Information" and "Parties" of a decision can be searched together. In order to keep the search efficient, the corresponding fields have been indexed. For this index, the individual texts are normalised back to their original forms. This also applies to the search term entered. Such a search index also allows more complex search queries such as:
<ul>
<li><strong>Phrase search</strong>: Terms in inverted commas are searched for together: "medical records".</li>
<li><strong>medical records</strong> finds all documents in which either <em>medical</em> or <em>recods</em> appears</li>
<li>A <strong>*</strong> at the end of a search term serves as a wildcard. <em>share*</em> also finds entries with <em>shareholder</em>, for example.</li>
</ul>
</p>
<p>For information: Displaying many hits as so called <em>Keywords in context (KWIC)</em> like e.g. <span class="text-nowrap bg-warning border-warning rounded border-5">example</span> can take some time. It is therefore advisable to narrow down the search results using additional filter options. </p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
Loading

0 comments on commit 97a2b94

Please sign in to comment.