Skip to content
Merged

Dev #11

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,16 @@ WORKDIR /app
# Install runtime dependencies only
RUN apt-get update && apt-get install -y \
postgresql-client \
# weasyprint dependencies
libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz-subset0 \
locales \
libpq5 \
&& rm -rf /var/lib/apt/lists/*

RUN sed -i '/pt_BR.UTF-8/s/^# //g' /etc/locale.gen && \
locale-gen pt_BR.UTF-8 && \
update-locale LANG=pt_BR.UTF-8

# Copy Python dependencies from builder
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
Expand Down
6 changes: 6 additions & 0 deletions core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,19 @@ class CoreFilterView(
FilterView,
):
template_name = "core/list.html"
template_name_htmx = "core/includes/table_content.html"
permission_action = "view"
paginate_by = None
table_pagination = {"per_page": 10}

def get_table_data(self):
return self.object_list

def get_template_names(self):
if self.request.htmx:
return [self.template_name_htmx]
return [self.template_name]


class CoreDetailView(
CoreBaseMixin,
Expand Down
43 changes: 15 additions & 28 deletions presente/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

class ActivityFilter(django_filters.FilterSet):
STATUS_CHOICES = [
("", "---------"),
("active", _("Ativa")),
("not_started", _("Não Iniciada")),
("expired", _("Encerrada")),
Expand All @@ -26,17 +25,15 @@ class ActivityFilter(django_filters.FilterSet):
choices=STATUS_CHOICES,
label=_("Status"),
method="filter_status",
widget=forms.Select(
attrs={"class": "form-select", "data-tom-select": "simple"}
),
empty_label="---------",
widget=forms.Select(attrs={"class": "form-select"}),
)
tags = django_filters.ModelChoiceFilter(
queryset=Tag.objects.all(),
label=_("Tags"),
field_name="tags",
widget=forms.Select(
attrs={"class": "form-select", "data-tom-select": "simple"}
),
empty_label="---------",
widget=forms.Select(attrs={"class": "form-select"}),
)
start_time__gte = django_filters.DateFilter(
field_name="start_time",
Expand Down Expand Up @@ -89,9 +86,8 @@ class AttendanceFilter(django_filters.FilterSet):
queryset=Tag.objects.all(),
label=_("Tags"),
field_name="activity__tags",
widget=forms.Select(
attrs={"class": "form-select", "data-tom-select": "simple"}
),
empty_label="---------",
widget=forms.Select(attrs={"class": "form-select"}),
)
activity__start_time__gte = django_filters.DateFilter(
field_name="activity__start_time",
Expand Down Expand Up @@ -125,15 +121,13 @@ class ActivityAttendanceFilter(django_filters.FilterSet):
user__type = django_filters.ChoiceFilter(
choices=User.UserType.choices,
label=_("Tipo"),
widget=forms.Select(
attrs={"class": "form-select", "data-tom-select": "simple"}
),
empty_label="---------",
widget=forms.Select(attrs={"class": "form-select"}),
)
user__campus = django_filters.ChoiceFilter(
label=_("Campus"),
widget=forms.Select(
attrs={"class": "form-select", "data-tom-select": "simple"}
),
empty_label="---------",
widget=forms.Select(attrs={"class": "form-select"}),
)
user__curso = django_filters.ChoiceFilter(
label=_("Curso"),
Expand All @@ -143,9 +137,8 @@ class ActivityAttendanceFilter(django_filters.FilterSet):
)
user__periodo_referencia = django_filters.ChoiceFilter(
label=_("Período de Referência"),
widget=forms.Select(
attrs={"class": "form-select", "data-tom-select": "simple"}
),
empty_label="---------",
widget=forms.Select(attrs={"class": "form-select"}),
)

def __init__(self, *args, **kwargs):
Expand All @@ -162,9 +155,7 @@ def __init__(self, *args, **kwargs):
.distinct()
.order_by("user__campus")
]
self.filters["user__campus"].extra["choices"] = [
("", "---------")
] + campus_choices
self.filters["user__campus"].extra["choices"] = campus_choices

# Curso choices
curso_choices = [
Expand All @@ -175,9 +166,7 @@ def __init__(self, *args, **kwargs):
.distinct()
.order_by("user__curso")
]
self.filters["user__curso"].extra["choices"] = [
("", "---------")
] + curso_choices
self.filters["user__curso"].extra["choices"] = curso_choices

# Periodo_referencia choices
periodo_choices = [
Expand All @@ -190,9 +179,7 @@ def __init__(self, *args, **kwargs):
.distinct()
.order_by("user__periodo_referencia")
]
self.filters["user__periodo_referencia"].extra["choices"] = [
("", "---------")
] + periodo_choices
self.filters["user__periodo_referencia"].extra["choices"] = periodo_choices

class Meta:
model = Attendance
Expand Down
22 changes: 22 additions & 0 deletions presente/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from django.utils.translation import gettext_lazy as _
from django.utils import timezone
from taggit.managers import TaggableManager
from django.db.models import Case, When, Value, IntegerField

User = get_user_model()

Expand Down Expand Up @@ -41,6 +42,25 @@ class Meta:
ordering = ["name"]


class ActivityQuerySet(models.QuerySet):
def with_status_order(self):
now = timezone.now()
return self.annotate(
status_order=Case(
When(end_time__lt=now, then=Value(3)), # expired
When(start_time__gt=now, then=Value(1)), # not_started
When(is_enabled=False, then=Value(2)), # not_enabled
default=Value(0), # active
output_field=IntegerField(),
)
)


class ActivityManager(models.Manager):
def get_queryset(self):
return ActivityQuerySet(self.model, using=self._db).with_status_order()


class Activity(models.Model):
owners = models.ManyToManyField(
User,
Expand Down Expand Up @@ -87,6 +107,8 @@ class Activity(models.Model):
_("Modificação"), auto_now=True, null=True, blank=True
)

objects = ActivityManager()

def __str__(self):
return self.title

Expand Down
5 changes: 3 additions & 2 deletions presente/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ class CoreTable(django_tables2.Table):
class ActivityTable(CoreTable):
status = django_tables2.Column(
verbose_name=_("Status"),
orderable=False,
orderable=True,
order_by="status_order",
empty_values=(),
)
tags_list = django_tables2.TemplateColumn(
Expand Down Expand Up @@ -87,7 +88,7 @@ class ActivityAttendanceTable(django_tables2.Table):
accessor="user",
verbose_name=_("Nome"),
orderable=True,
order_by=("user__full_name",),
order_by=("user_name_collated", "user__full_name"),
)
user_type = django_tables2.Column(
accessor="user__type",
Expand Down
19 changes: 14 additions & 5 deletions presente/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,12 +322,21 @@ def get_page_title(self):
return _("Presenças - {}").format(activity.title)

def get_queryset(self):
from django.db import connection
from django.db.models import F

activity = self.get_activity()
return (
Attendance.objects.filter(activity=activity)
.select_related("user")
.order_by("-checked_in_at")
)
qs = Attendance.objects.filter(activity=activity).select_related("user")

if connection.vendor == "postgresql":
from django.contrib.postgres.fields import Collate

qs = qs.annotate(user_name_collated=Collate("user__full_name", "pt_BR"))
else:
# SQLite: just alias the field for compatibility
qs = qs.annotate(user_name_collated=F("user__full_name"))

return qs.order_by("-checked_in_at")

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
Expand Down
22 changes: 22 additions & 0 deletions templates/core/includes/table_content.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{% load render_table from django_tables2 %}
{% load filter_tags %}

{% if request.htmx %}
<div id="filter-button-wrapper" hx-swap-oob="true">
{% if filter %}
{% has_active_filters filter as filters_active %}
<button class="btn {% if filters_active %}btn-primary{% else %}btn-outline-secondary{% endif %}" type="button" data-bs-toggle="collapse" data-bs-target="#filterCollapse" {% if filters_active %}aria-expanded="true"{% endif %}>
<i class="bi bi-funnel{% if filters_active %}-fill{% endif %}"></i> Filtros
{% if filters_active %}
<span class="badge bg-light text-dark ms-1">{{ filter.qs.count }}</span>
{% endif %}
</button>
{% endif %}
</div>
{% endif %}

<div class="card">
<div class="card-body p-0">
{% render_table table %}
</div>
</div>
12 changes: 5 additions & 7 deletions templates/core/list.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
{% block app_content %}
<div class="container-fluid">
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<div id="filter-button-wrapper">
{% if filter %}
{% has_active_filters filter as filters_active %}
<button class="btn {% if filters_active %}btn-primary{% else %}btn-outline-secondary{% endif %}" type="button" data-bs-toggle="collapse" data-bs-target="#filterCollapse" {% if filters_active %}aria-expanded="true"{% endif %}>
Expand All @@ -41,7 +41,7 @@
<div class="collapse {% if filters_active %}show{% endif %} mb-3" id="filterCollapse">
<div class="card">
<div class="card-body">
<form method="get" class="row g-3">
<form method="get" class="row g-3" hx-get="{{ request.path }}" hx-target="#table-wrapper" hx-push-url="true" hx-trigger="change delay:300ms, submit">
{% for field in filter.form %}
{% if field.name != 'csrfmiddlewaretoken' %}
<div class="col-md-4">
Expand All @@ -59,7 +59,7 @@
<button type="submit" class="btn btn-primary">
<i class="bi bi-search"></i> Filtrar
</button>
<a href="{{ request.path }}" class="btn btn-secondary">
<a href="{{ request.path }}" class="btn btn-secondary" hx-get="{{ request.path }}" hx-target="#table-wrapper" hx-push-url="true" hx-on::after-request="const form = this.closest('form'); form.reset(); form.querySelectorAll('select').forEach(el => el.tomselect?.clear());">
<i class="bi bi-x-circle"></i> Limpar
</a>
</div>
Expand All @@ -69,10 +69,8 @@
</div>
{% endif %}

<div class="card">
<div class="card-body p-0">
{% render_table table %}
</div>
<div id="table-wrapper">
{% include "core/includes/table_content.html" %}
</div>
</div>
{% endblock %}
8 changes: 4 additions & 4 deletions templates/django_tables2/bootstrap5.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
{% for column in table.columns %}
{% if column.orderable %}
<th {{ column.attrs.th.as_html }}>
<a href="{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}" class="text-decoration-none text-body d-flex align-items-center justify-content-between">
<a href="{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}" class="text-decoration-none text-body d-flex align-items-center justify-content-between" hx-get="{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}" hx-target="#table-wrapper" hx-push-url="true">
<span>{{ column.header }}</span>
{% if column.is_ordered %}
{% if column.order_by_alias.next == column.name %}
Expand Down Expand Up @@ -85,7 +85,7 @@
<ul class="pagination pagination-sm mb-0">
{% if table.page.has_previous %}
<li class="page-item">
<a class="page-link" href="{% querystring table.prefixed_page_field=table.page.previous_page_number %}">
<a class="page-link" href="{% querystring table.prefixed_page_field=table.page.previous_page_number %}" hx-get="{% querystring table.prefixed_page_field=table.page.previous_page_number %}" hx-target="#table-wrapper" hx-push-url="true">
&laquo;
</a>
</li>
Expand All @@ -102,14 +102,14 @@
</li>
{% elif p >= table.page.number|add:"-2" and p <= table.page.number|add:"2" %}
<li class="page-item">
<a class="page-link" href="{% querystring table.prefixed_page_field=p %}">{{ p }}</a>
<a class="page-link" href="{% querystring table.prefixed_page_field=p %}" hx-get="{% querystring table.prefixed_page_field=p %}" hx-target="#table-wrapper" hx-push-url="true">{{ p }}</a>
</li>
{% endif %}
{% endfor %}

{% if table.page.has_next %}
<li class="page-item">
<a class="page-link" href="{% querystring table.prefixed_page_field=table.page.next_page_number %}">
<a class="page-link" href="{% querystring table.prefixed_page_field=table.page.next_page_number %}" hx-get="{% querystring table.prefixed_page_field=table.page.next_page_number %}" hx-target="#table-wrapper" hx-push-url="true">
&raquo;
</a>
</li>
Expand Down
24 changes: 9 additions & 15 deletions users/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,25 @@ class UserFilter(django_filters.FilterSet):
type = django_filters.ChoiceFilter(
choices=User.UserType.choices,
label=_("Tipo"),
widget=forms.Select(
attrs={"class": "form-select", "data-tom-select": "simple"}
),
empty_label="---------",
widget=forms.Select(attrs={"class": "form-select"}),
)
campus = django_filters.ChoiceFilter(
label=_("Campus"),
choices=lambda: [("", "---------")]
+ [
choices=lambda: [
(campus, campus)
for campus in User.objects.exclude(campus__isnull=True)
.exclude(campus="")
.values_list("campus", flat=True)
.distinct()
.order_by("campus")
],
widget=forms.Select(
attrs={"class": "form-select", "data-tom-select": "simple"}
),
empty_label="---------",
widget=forms.Select(attrs={"class": "form-select"}),
)
curso = django_filters.ChoiceFilter(
label=_("Curso"),
choices=lambda: [("", "---------")]
+ [
choices=lambda: [
(curso, curso)
for curso in User.objects.exclude(curso__isnull=True)
.exclude(curso="")
Expand All @@ -50,18 +46,16 @@ class UserFilter(django_filters.FilterSet):
)
periodo_referencia = django_filters.ChoiceFilter(
label=_("Período"),
choices=lambda: [("", "---------")]
+ [
choices=lambda: [
(periodo, periodo)
for periodo in User.objects.exclude(periodo_referencia__isnull=True)
.exclude(periodo_referencia="")
.values_list("periodo_referencia", flat=True)
.distinct()
.order_by("periodo_referencia")
],
widget=forms.Select(
attrs={"class": "form-select", "data-tom-select": "simple"}
),
empty_label="---------",
widget=forms.Select(attrs={"class": "form-select"}),
)

def filter_name(self, queryset, name, value):
Expand Down
Loading