Device list: Show online status

This commit is contained in:
Raphael Michel
2026-08-05 17:01:13 +02:00
parent e3fa1aed7f
commit fcd8f26cb1
3 changed files with 24 additions and 2 deletions
+7
View File
@@ -20,11 +20,13 @@
# <https://www.gnu.org/licenses/>. # <https://www.gnu.org/licenses/>.
# #
import string import string
from datetime import timedelta
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.db.models import Max from django.db.models import Max
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_scopes import ScopedManager, scopes_disabled from django_scopes import ScopedManager, scopes_disabled
@@ -307,3 +309,8 @@ class DeviceLastSeen(models.Model):
autosummarize=True autosummarize=True
) )
] ]
@property
def is_recent(self):
# pretixSCAN/pretixPOS sync every 5 minutes, so 7 minutes can be considered "offline"
return self.last_seen - now() < timedelta(minutes=7)
@@ -118,6 +118,18 @@
</td> </td>
{% endif %} {% endif %}
<td> <td>
{% if d.last_seen and d.last_seen.is_recent %}
<span class="fa fa-check-circle text-success"
data-toggle="tooltip"
title="{% if d.last_seen %}{% blocktrans with time=d.last_seen.last_seen|date:"SHORT_DATETIME_FORMAT" %}Last seen: {{ time }}{% endblocktrans %}{% else %}{% trans "No recent contact" %}{% endif %}"
></span>
{% else %}
<span class="fa fa-circle text-danger"
data-toggle="tooltip"
title="{% if d.last_seen %}{% blocktrans with time=d.last_seen.last_seen|date:"SHORT_DATETIME_FORMAT" %}Last seen: {{ time }}{% endblocktrans %}{% else %}{% trans "No recent contact" %}{% endif %}"
></span>
{% endif %}
{{ d.device_id }} {{ d.device_id }}
</td> </td>
<td> <td>
@@ -125,6 +137,7 @@
<del>{% endif %} <del>{% endif %}
{{ d.name }} {{ d.name }}
{% if d.revoked %}</del>{% endif %} {% if d.revoked %}</del>{% endif %}
{% if d.gate %} {% if d.gate %}
<br> <br>
<small class="text-muted">{{ d.gate.name }}</small> <small class="text-muted">{{ d.gate.name }}</small>
+4 -2
View File
@@ -108,7 +108,9 @@ from pretix.base.services.export import (
init_organizer_exporters, multiexport, scheduled_organizer_export, init_organizer_exporters, multiexport, scheduled_organizer_export,
) )
from pretix.base.services.mail import mail, prefix_subject from pretix.base.services.mail import mail, prefix_subject
from pretix.base.services.placeholders import prepare_sample_context_for_preview from pretix.base.services.placeholders import (
prepare_sample_context_for_preview,
)
from pretix.base.templatetags.rich_text import markdown_compile_email from pretix.base.templatetags.rich_text import markdown_compile_email
from pretix.base.views.tasks import AsyncAction from pretix.base.views.tasks import AsyncAction
from pretix.control.forms.exports import ScheduledOrganizerExportForm from pretix.control.forms.exports import ScheduledOrganizerExportForm
@@ -1206,7 +1208,7 @@ class DeviceQueryMixin:
def get_queryset(self): def get_queryset(self):
qs = self.request.organizer.devices.prefetch_related( qs = self.request.organizer.devices.prefetch_related(
'limit_events', 'gate', 'limit_events', 'gate',
).order_by('revoked', '-device_id') ).select_related('last_seen').order_by('revoked', '-device_id')
if 'device' in self.request_data and '__ALL' not in self.request_data: if 'device' in self.request_data and '__ALL' not in self.request_data:
qs = qs.filter( qs = qs.filter(