From 7ca533edcad6ff2a844e25c73619fcfb42785c8a Mon Sep 17 00:00:00 2001 From: Diego Cirilo Date: Tue, 2 Dec 2025 10:15:04 -0300 Subject: [PATCH 1/4] created_at nas atividades --- ...ctivity_created_at_activity_modified_at.py | 23 +++++++++++++++++++ presente/models.py | 6 +++++ presente/views.py | 7 +++++- requirements.txt | 2 ++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 presente/migrations/0015_activity_created_at_activity_modified_at.py diff --git a/presente/migrations/0015_activity_created_at_activity_modified_at.py b/presente/migrations/0015_activity_created_at_activity_modified_at.py new file mode 100644 index 0000000..8c15c7d --- /dev/null +++ b/presente/migrations/0015_activity_created_at_activity_modified_at.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.7 on 2025-12-02 05:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('presente', '0014_alter_activity_is_enabled'), + ] + + operations = [ + migrations.AddField( + model_name='activity', + name='created_at', + field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='Criado em'), + ), + migrations.AddField( + model_name='activity', + name='modified_at', + field=models.DateTimeField(auto_now=True, null=True, verbose_name='Modificado em'), + ), + ] diff --git a/presente/models.py b/presente/models.py index bf887b3..ebd142e 100644 --- a/presente/models.py +++ b/presente/models.py @@ -80,6 +80,12 @@ class Activity(models.Model): blank=True, help_text=_("Selecione as redes que podem acessar esta atividade"), ) + created_at = models.DateTimeField( + _("Criado em"), auto_now_add=True, null=True, blank=True + ) + modified_at = models.DateTimeField( + _("Modificado em"), auto_now=True, null=True, blank=True + ) def __str__(self): return self.title diff --git a/presente/views.py b/presente/views.py index 80b3a9e..7f806f0 100644 --- a/presente/views.py +++ b/presente/views.py @@ -66,7 +66,9 @@ class ActivityListView(CoreFilterView): permission_required = [] def get_queryset(self): - return Activity.objects.filter(owners=self.request.user) + return Activity.objects.filter(owners=self.request.user).order_by( + "-modified_at", "-start_time" + ) class AdminActivitiesView(SuperuserRequiredMixin, CoreFilterView): @@ -75,6 +77,9 @@ class AdminActivitiesView(SuperuserRequiredMixin, CoreFilterView): table_class = ActivityTable filterset_class = ActivityFilter + def get_queryset(self): + return Activity.objects.all().order_by("-modified_at", "-start_time") + class ActivityCreateView(CoreCreateView): model = Activity diff --git a/requirements.txt b/requirements.txt index 202a4f9..d6c0021 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,12 +21,14 @@ identify==2.6.15 idna==3.10 nodeenv==1.9.1 oauthlib==3.3.1 +pillow==12.0.0 platformdirs==4.4.0 pre_commit==4.3.0 pycparser==2.23 PyJWT==2.10.1 python-dotenv==1.1.1 PyYAML==6.0.3 +qrcode==8.2 requests==2.32.5 ruff==0.13.3 sqlparse==0.5.3 From e561c06f9bcea3793cf9ac9a9778b0db11cfa675 Mon Sep 17 00:00:00 2001 From: Diego Cirilo Date: Wed, 3 Dec 2025 10:47:18 -0300 Subject: [PATCH 2/4] =?UTF-8?q?badge=20com=20contagem=20de=20presen=C3=A7a?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- presente/views.py | 1 + static/js/qrcode.js | 2 ++ templates/presente/activity_detail.html | 1 + 3 files changed, 4 insertions(+) diff --git a/presente/views.py b/presente/views.py index 7f806f0..f6fb3a9 100644 --- a/presente/views.py +++ b/presente/views.py @@ -120,6 +120,7 @@ def get_fields(self): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["attendances"] = self.object.attendances.select_related("user").all() + context["attendance_count"] = self.object.attendances.count() context["encoded_id"] = encode_activity_id(self.object.id) return context diff --git a/static/js/qrcode.js b/static/js/qrcode.js index 2a987d5..787aa74 100644 --- a/static/js/qrcode.js +++ b/static/js/qrcode.js @@ -1,3 +1,5 @@ +"use strict"; + // Helper function to format countdown time function formatCountdown(totalSeconds) { const days = Math.floor(totalSeconds / 86400); diff --git a/templates/presente/activity_detail.html b/templates/presente/activity_detail.html index f4f3171..a5d4ca4 100644 --- a/templates/presente/activity_detail.html +++ b/templates/presente/activity_detail.html @@ -3,6 +3,7 @@ {% block card_footer_actions %} Ver Presenças + {{ attendance_count }} {{ block.super }} {% endblock %} From c6ed47ecaec4b036e4cc89863415e28cc9189082 Mon Sep 17 00:00:00 2001 From: Diego Cirilo Date: Wed, 3 Dec 2025 13:13:11 -0300 Subject: [PATCH 3/4] renomeando alguns campos --- ...ted_at_alter_activity_end_time_and_more.py | 33 +++++++++++++++++++ presente/models.py | 8 ++--- 2 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 presente/migrations/0016_alter_activity_created_at_alter_activity_end_time_and_more.py diff --git a/presente/migrations/0016_alter_activity_created_at_alter_activity_end_time_and_more.py b/presente/migrations/0016_alter_activity_created_at_alter_activity_end_time_and_more.py new file mode 100644 index 0000000..7aa1bbd --- /dev/null +++ b/presente/migrations/0016_alter_activity_created_at_alter_activity_end_time_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 5.2.7 on 2025-12-03 16:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('presente', '0015_activity_created_at_activity_modified_at'), + ] + + operations = [ + migrations.AlterField( + model_name='activity', + name='created_at', + field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='Criação'), + ), + migrations.AlterField( + model_name='activity', + name='end_time', + field=models.DateTimeField(verbose_name='Término'), + ), + migrations.AlterField( + model_name='activity', + name='modified_at', + field=models.DateTimeField(auto_now=True, null=True, verbose_name='Modificação'), + ), + migrations.AlterField( + model_name='activity', + name='start_time', + field=models.DateTimeField(verbose_name='Início'), + ), + ] diff --git a/presente/models.py b/presente/models.py index ebd142e..c56e7e8 100644 --- a/presente/models.py +++ b/presente/models.py @@ -55,8 +55,8 @@ class Activity(models.Model): ), blank=True, ) - start_time = models.DateTimeField(_("Data/hora de início")) - end_time = models.DateTimeField(_("Data/hora de término")) + start_time = models.DateTimeField(_("Início")) + end_time = models.DateTimeField(_("Término")) is_enabled = models.BooleanField( default=True, verbose_name=_("Habilitar?"), @@ -81,10 +81,10 @@ class Activity(models.Model): help_text=_("Selecione as redes que podem acessar esta atividade"), ) created_at = models.DateTimeField( - _("Criado em"), auto_now_add=True, null=True, blank=True + _("Criação"), auto_now_add=True, null=True, blank=True ) modified_at = models.DateTimeField( - _("Modificado em"), auto_now=True, null=True, blank=True + _("Modificação"), auto_now=True, null=True, blank=True ) def __str__(self): From 35d49836183f0067ff18eafb39754bf141696bd1 Mon Sep 17 00:00:00 2001 From: Diego Cirilo Date: Wed, 3 Dec 2025 13:23:33 -0300 Subject: [PATCH 4/4] crud de redes --- presente/forms.py | 20 +++++++++++++++++++- presente/menus.py | 10 ++++++++++ presente/tables.py | 9 ++++++++- presente/urls.py | 14 ++++++++++++++ presente/views.py | 43 ++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 91 insertions(+), 5 deletions(-) diff --git a/presente/forms.py b/presente/forms.py index 796ea8f..c125ecf 100644 --- a/presente/forms.py +++ b/presente/forms.py @@ -2,7 +2,7 @@ from django.contrib.auth import get_user_model from django.utils.translation import gettext_lazy as _ from taggit.models import Tag -from .models import Activity +from .models import Activity, Network User = get_user_model() @@ -172,3 +172,21 @@ def clean_columns(self): # Default columns if none selected return ["number", "name", "matricula", "checked_in_at"] return columns + + +class NetworkForm(forms.ModelForm): + class Meta: + model = Network + fields = ["name", "description", "ip_addresses", "is_active"] + widgets = { + "name": forms.TextInput(attrs={"class": "form-control"}), + "description": forms.Textarea(attrs={"class": "form-control", "rows": 3}), + "ip_addresses": forms.Textarea( + attrs={ + "class": "form-control font-monospace", + "rows": 10, + "placeholder": "200.137.2.62\n192.168.1.0/24\n10.0.0.1", + } + ), + "is_active": forms.CheckboxInput(attrs={"class": "form-check-input"}), + } diff --git a/presente/menus.py b/presente/menus.py index cbaacce..67060d9 100644 --- a/presente/menus.py +++ b/presente/menus.py @@ -51,3 +51,13 @@ check=lambda r: r.user.is_superuser, ), ) + +Menu.add_item( + "presente", + MenuItem( + "Redes", + reverse("presente:network_list"), + icon="bi bi-hdd-network", + check=lambda r: r.user.is_superuser, + ), +) diff --git a/presente/tables.py b/presente/tables.py index bac15b2..61a9d4d 100644 --- a/presente/tables.py +++ b/presente/tables.py @@ -1,7 +1,7 @@ import django_tables2 from django.utils.translation import gettext_lazy as _ from django.utils.safestring import mark_safe -from .models import Activity, Attendance +from .models import Activity, Attendance, Network class CoreTable(django_tables2.Table): @@ -135,3 +135,10 @@ class Meta: "checked_in_at", ) order_by = "-checked_in_at" + + +class NetworkTable(CoreTable): + class Meta: + model = Network + fields = ("name", "description", "is_active") + attrs = {"class": "table table-striped"} diff --git a/presente/urls.py b/presente/urls.py index c54dbb5..0b506fd 100644 --- a/presente/urls.py +++ b/presente/urls.py @@ -44,6 +44,20 @@ views.AttendanceDeleteView.as_view(), name="attendance_delete", ), + # Network CRUD URLs + path("network/", views.NetworkListView.as_view(), name="network_list"), + path("network/add", views.NetworkCreateView.as_view(), name="network_add"), + path("network//", views.NetworkDetailView.as_view(), name="network_view"), + path( + "network//update/", + views.NetworkUpdateView.as_view(), + name="network_change", + ), + path( + "network//delete/", + views.NetworkDeleteView.as_view(), + name="network_delete", + ), # User attendances path("my-attendances/", views.MyAttendancesView.as_view(), name="my_attendances"), # Public attendance URLs diff --git a/presente/views.py b/presente/views.py index f6fb3a9..43b8d4b 100644 --- a/presente/views.py +++ b/presente/views.py @@ -9,6 +9,7 @@ from django_filters.views import FilterView from core.mixins import PageTitleMixin, SuperuserRequiredMixin from core.views import ( + CoreListView, CoreCreateView, CoreDetailView, CoreUpdateView, @@ -19,9 +20,14 @@ import qrcode.image.svg from io import BytesIO import base64 -from .models import Activity, Attendance -from .tables import ActivityTable, AttendanceTable, ActivityAttendanceTable -from .forms import ActivityForm, AttendancePrintConfigForm +from .models import Activity, Attendance, Network +from .tables import ( + ActivityTable, + AttendanceTable, + ActivityAttendanceTable, + NetworkTable, +) +from .forms import ActivityForm, AttendancePrintConfigForm, NetworkForm from .filters import ActivityFilter, AttendanceFilter, ActivityAttendanceFilter from .mixins import ActivityOwnerMixin from .utils import ( @@ -453,3 +459,34 @@ def get_context_data(self, **kwargs): context["generated_at"] = timezone.now() return context + + +# Network CRUD Views + + +class NetworkListView(SuperuserRequiredMixin, CoreListView): + page_title = _("Redes") + model = Network + table_class = NetworkTable + + +class NetworkCreateView(SuperuserRequiredMixin, CoreCreateView): + model = Network + page_title = _("Redes") + form_class = NetworkForm + + +class NetworkDetailView(SuperuserRequiredMixin, CoreDetailView): + model = Network + page_title = _("Redes") + + +class NetworkUpdateView(SuperuserRequiredMixin, CoreUpdateView): + model = Network + page_title = _("Redes") + form_class = NetworkForm + + +class NetworkDeleteView(SuperuserRequiredMixin, CoreDeleteView): + model = Network + page_title = _("Redes")