Add more base functions
This commit is contained in:
parent
565cba18e2
commit
2f06ecabd7
46
shynet/a17t/templates/a17t/includes/pagination.html
Normal file
46
shynet/a17t/templates/a17t/includes/pagination.html
Normal file
@ -0,0 +1,46 @@
|
||||
<nav class="flex w-full flex-wrap items-center justify-between" role="navigation" aria-label="pagination">
|
||||
<div class="w-full md:w-auto mb-2">
|
||||
{% if page.has_previous %}
|
||||
<a href="?page={{ page.previous_page_number }}{{url_parameters}}" class="button ~neutral !low">Previous</a>
|
||||
{% else %}
|
||||
<a class="button ~neutral !normal" disabled>Previous</a>
|
||||
{% endif %}
|
||||
{% if page.has_next %}
|
||||
<a href="?page={{ page.next_page_number }}{{url_parameters}}" class="button ~neutral !low">Next</a>
|
||||
{% else %}
|
||||
<a class="button ~neutral !normal" disabled>Next</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<ul class="pagination-list w-full md:w-auto mb-2">
|
||||
{% for pnum in begin %}
|
||||
{% ifequal page.number pnum %}
|
||||
<li><a class="button ~neutral !high">{{ pnum }}</a></li>
|
||||
{% else %}
|
||||
<li><a class="button ~neutral !normal" href="?page={{ pnum }}{{url_parameters}}">{{ pnum }}</a></li>
|
||||
{% endifequal %}
|
||||
{% endfor %}
|
||||
|
||||
{% if middle %}
|
||||
<li><span class="pagination-ellipsis">…</span></li>
|
||||
{% for pnum in middle %}
|
||||
{% ifequal page.number pnum %}
|
||||
<li><a class="button ~neutral !high">{{ pnum }}</a></li>
|
||||
{% else %}
|
||||
<li><a class="button ~neutral !normal" href="?page={{ pnum }}{{url_parameters}}">{{ pnum }}</a></li>
|
||||
{% endifequal %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if end %}
|
||||
<li><span class="pagination-ellipsis">…</span></li>
|
||||
{% for pnum in end %}
|
||||
{% ifequal page.number pnum %}
|
||||
<li><a class="button ~neutral !high">{{ pnum }}</a></li>
|
||||
{% else %}
|
||||
<li><a class="button ~neutral !normal" href="?page={{ pnum }}{{url_parameters}}">{{ pnum }}</a></li>
|
||||
{% endifequal %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
55
shynet/a17t/templatetags/pagination.py
Normal file
55
shynet/a17t/templatetags/pagination.py
Normal file
@ -0,0 +1,55 @@
|
||||
# From https://djangosnippets.org/snippets/1441/
|
||||
|
||||
from django import template
|
||||
from django.utils.http import urlencode
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.inclusion_tag("a17t/includes/pagination.html")
|
||||
def pagination(
|
||||
page,
|
||||
request,
|
||||
begin_pages=2,
|
||||
end_pages=2,
|
||||
before_current_pages=4,
|
||||
after_current_pages=4,
|
||||
):
|
||||
url_parameters = "".join(
|
||||
[
|
||||
f"&{urlencode(key)}={urlencode(value)}"
|
||||
for key, value in request.GET.items()
|
||||
if key != "page"
|
||||
]
|
||||
)
|
||||
|
||||
before = max(page.number - before_current_pages - 1, 0)
|
||||
after = page.number + after_current_pages
|
||||
|
||||
begin = page.paginator.page_range[:begin_pages]
|
||||
middle = page.paginator.page_range[before:after]
|
||||
end = page.paginator.page_range[-end_pages:]
|
||||
last_page_number = end[-1]
|
||||
|
||||
def collides(firstlist, secondlist):
|
||||
return any(item in secondlist for item in firstlist)
|
||||
|
||||
if collides(middle, end):
|
||||
end = range(max(page.number - before_current_pages, 1), last_page_number + 1)
|
||||
middle = []
|
||||
|
||||
if collides(begin, middle):
|
||||
begin = range(1, min(page.number + after_current_pages, last_page_number) + 1)
|
||||
middle = []
|
||||
|
||||
if collides(begin, end):
|
||||
begin = range(1, last_page_number + 1)
|
||||
end = []
|
||||
|
||||
return {
|
||||
"page": page,
|
||||
"begin": begin,
|
||||
"middle": middle,
|
||||
"end": end,
|
||||
"url_parameters": url_parameters,
|
||||
}
|
@ -70,3 +70,7 @@ class Hit(models.Model):
|
||||
location = models.TextField(blank=True)
|
||||
referrer = models.TextField(blank=True)
|
||||
load_time = models.FloatField(null=True)
|
||||
|
||||
@property
|
||||
def duration(self):
|
||||
return self.last_seen - self.start_time
|
||||
|
@ -4,27 +4,31 @@ from django.utils import timezone
|
||||
|
||||
|
||||
class DateRangeMixin:
|
||||
def get_context_data(self, **kwargs):
|
||||
data = super().get_context_data(**kwargs)
|
||||
|
||||
def get_start_date(self):
|
||||
if self.request.GET.get("startDate") != None:
|
||||
found_time = timezone.datetime.strptime(
|
||||
self.request.GET.get("startDate"), "%Y-%m-%d"
|
||||
)
|
||||
found_time.replace(hour=0, minute=0)
|
||||
data["start_date"] = found_time
|
||||
return found_time
|
||||
else:
|
||||
data["start_date"] = timezone.now() - timezone.timedelta(days=30)
|
||||
return timezone.now() - timezone.timedelta(days=30)
|
||||
|
||||
def get_end_date(self):
|
||||
if self.request.GET.get("endDate") != None:
|
||||
found_time = timezone.datetime.strptime(
|
||||
self.request.GET.get("endDate"), "%Y-%m-%d"
|
||||
)
|
||||
found_time.replace(hour=23, minute=59)
|
||||
data["end_date"] = found_time
|
||||
return found_time
|
||||
else:
|
||||
data["end_date"] = timezone.now()
|
||||
return timezone.now()
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
data = super().get_context_data(**kwargs)
|
||||
data["start_date"] = self.get_start_date()
|
||||
data["end_date"] = self.get_end_date()
|
||||
return data
|
||||
|
||||
|
||||
|
@ -64,7 +64,6 @@ class Service(models.Model):
|
||||
Session.objects.filter(
|
||||
service=self, start_time__gt=start_time, start_time__lt=end_time
|
||||
)
|
||||
.prefetch_related("hit_set")
|
||||
.order_by("-start_time")
|
||||
)
|
||||
session_count = sessions.count()
|
||||
@ -151,6 +150,5 @@ class Service(models.Model):
|
||||
"browsers": browsers,
|
||||
"devices": devices,
|
||||
"device_types": device_types,
|
||||
"sessions": sessions,
|
||||
"online": True,
|
||||
}
|
||||
|
@ -8,6 +8,8 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
{% include 'a17t/head.html' %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/litepicker/dist/js/main.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"
|
||||
integrity="sha256-R4pqcOYV8lt7snxMQO/HSbVCFRPMdrhAFMH+vr9giYI=" crossorigin="anonymous"></script>
|
||||
{% block extra_head %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
@ -1,11 +1,11 @@
|
||||
{% load humanize helpers %}
|
||||
|
||||
<a class="card ~neutral !low service mb-6" href="{% url 'core:service' service.uuid %}">
|
||||
{% with stats=service.stats %}
|
||||
<a class="card ~neutral !low service mb-6" href="{% url 'core:service' object.uuid %}">
|
||||
{% with stats=object.stats %}
|
||||
<div class="md:flex justify-between items-center">
|
||||
<div class="mr-4 md:w-4/12">
|
||||
<h3 class="heading text-xl mr-2 mb-1">
|
||||
{{service.name}}
|
||||
{{object.name}}
|
||||
</h3>
|
||||
{% include 'core/includes/stats_status_chip.html' %}
|
||||
</div>
|
||||
|
@ -1,2 +1,4 @@
|
||||
<a class="portal {% if url == request.get_full_path %}~urge active bg-gray-100{% endif %}"
|
||||
{% load helpers %}
|
||||
|
||||
<a class="portal {% if request.get_full_path|startswith:url %}~urge active bg-gray-100{% endif %}"
|
||||
href="{{url}}">{{label}}</a><br>
|
@ -1,5 +1,6 @@
|
||||
{% load humanize %}
|
||||
|
||||
{% with stats=object.get_daily_stats %}
|
||||
{% if stats.currently_online > 0 %}
|
||||
<span class="chip ~positive">
|
||||
{{stats.currently_online|intcomma}} online
|
||||
@ -12,4 +13,5 @@
|
||||
<span class="chip ~critical">
|
||||
Offline
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
@ -9,7 +9,7 @@
|
||||
</div>
|
||||
<hr class="sep">
|
||||
|
||||
{% for service in services %}
|
||||
{% for object in services %}
|
||||
{% include 'core/includes/service_overview.html' %}
|
||||
{% empty %}
|
||||
<p>You don't have any services.</p>
|
||||
|
@ -1,31 +1,13 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "core/service_base.html" %}
|
||||
|
||||
{% load humanize helpers %}
|
||||
|
||||
{% block head_title %}{{service.name}}{% endblock %}
|
||||
{% block service_actions %}
|
||||
<div class="mr-2">{% include 'core/includes/date_range.html' %}</div>
|
||||
<a href="{% url 'core:service_update' service.uuid %}" class="button field ~neutral !low w-auto">Manage →</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
.limited-height {
|
||||
max-height: 400px !important;
|
||||
overflow: scroll !important;
|
||||
}
|
||||
</style>
|
||||
<div class="md:flex justify-between items-center" id="heading">
|
||||
<div class="flex items-center">
|
||||
<h3 class="heading leading-none mr-4">
|
||||
{{service.name}}
|
||||
</h3>
|
||||
<div class='text-3xl'>
|
||||
{% include 'core/includes/stats_status_chip.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="mr-2">{% include 'core/includes/date_range.html' %}</div>
|
||||
<a href="{% url 'core:service_update' service.uuid %}" class="button field ~neutral !low w-auto">Manage →</a>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="sep h-8">
|
||||
{% block service_content %}
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6" id="stats">
|
||||
<article class="card ~neutral !low">
|
||||
<p class="label">Sessions</p>
|
||||
@ -70,7 +52,7 @@
|
||||
<tbody>
|
||||
{% for location in stats.locations %}
|
||||
<tr>
|
||||
<td>{{location.location}}</td>
|
||||
<td>{{location.location|urlize}}</td>
|
||||
<td>{{location.count|intcomma}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@ -88,7 +70,7 @@
|
||||
<tbody>
|
||||
{% for referrer in stats.referrers %}
|
||||
<tr>
|
||||
<td>{{referrer.referrer|default:"Direct"}}</td>
|
||||
<td>{{referrer.referrer|default:"Direct"|urlize}}</td>
|
||||
<td>{{referrer.count|intcomma}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@ -168,63 +150,5 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card ~neutral !low limited-height">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th>Time</th>
|
||||
<th>Identity</th>
|
||||
<th>Duration</th>
|
||||
<th>Network</th>
|
||||
<th>IP</th>
|
||||
<th>Hits</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for session in stats.sessions %}
|
||||
<tr>
|
||||
<td>
|
||||
{{session.start_time|date:"M j Y, g:i a"|capfirst}}
|
||||
{% if session.is_currently_active %}
|
||||
<span class="badge ~positive">Online</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{session.identifier|default:"Anonymous"}}</td>
|
||||
<td>{{session.duration|naturaldelta}}</td>
|
||||
<td>{{session.country|flag_emoji}} {{session.asn|default:"Unknown"}}</td>
|
||||
<td>{{session.ip}}</td>
|
||||
<td>{{session.hit_set.count|intcomma}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% for session in stats.sessions %}
|
||||
<tr>
|
||||
<td>
|
||||
{{session.start_time|date:"M j Y, g:i a"|capfirst}}
|
||||
{% if session.is_currently_active %}
|
||||
<span class="badge ~positive">Online</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{session.identifier|default:"Anonymous"}}</td>
|
||||
<td>{{session.duration|naturaldelta}}</td>
|
||||
<td>{{session.country|flag_emoji}} {{session.asn|default:"Unknown"}}</td>
|
||||
<td>{{session.ip}}</td>
|
||||
<td>{{session.hit_set.count|intcomma}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% for session in stats.sessions %}
|
||||
<tr>
|
||||
<td>
|
||||
{{session.start_time|date:"M j Y, g:i a"|capfirst}}
|
||||
{% if session.is_currently_active %}
|
||||
<span class="badge ~positive">Online</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{session.identifier|default:"Anonymous"}}</td>
|
||||
<td>{{session.duration|naturaldelta}}</td>
|
||||
<td>{{session.country|flag_emoji}} {{session.asn|default:"Unknown"}}</td>
|
||||
<td>{{session.ip}}</td>
|
||||
<td>{{session.hit_set.count|intcomma}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<a href="{% url 'core:service_session_list' service.uuid %}" class="button">View individual sessions →</a>
|
||||
{% endblock %}
|
20
shynet/core/templates/core/pages/service_delete.html
Normal file
20
shynet/core/templates/core/pages/service_delete.html
Normal file
@ -0,0 +1,20 @@
|
||||
{% extends "core/service_base.html" %}
|
||||
|
||||
{% load a17t_tags %}
|
||||
|
||||
{% block head_title %}Delete {{object.name}}{% endblock %}
|
||||
|
||||
{% block service_content %}
|
||||
<form class="card ~neutral !low p-0 max-w-xl" method="POST">
|
||||
{% csrf_token %}
|
||||
<div class="p-4">
|
||||
<p>Are you sure you want to delete this service? All of its
|
||||
analytics and associated data will be permanently deleted.</p>
|
||||
{{form|a17t}}
|
||||
</div>
|
||||
<div class="section ~critical !normal p-4">
|
||||
<button type="submit" class="button ~critical !high">Delete</button>
|
||||
<a href="{% url 'core:service' object.uuid %}" class="button ~critical !low">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
100
shynet/core/templates/core/pages/service_session.html
Normal file
100
shynet/core/templates/core/pages/service_session.html
Normal file
@ -0,0 +1,100 @@
|
||||
{% extends "core/service_base.html" %}
|
||||
|
||||
{% load a17t_tags pagination humanize helpers %}
|
||||
|
||||
{% block head_title %}{{object.name}} Session{% endblock %}
|
||||
|
||||
{% block service_actions %}
|
||||
<a href="{% url 'core:service' object.uuid %}" class="button field ~neutral !low w-auto">Analytics →</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block service_content %}
|
||||
<article class="card ~neutral !low">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="heading text-xl text-purple-600 mr-4">
|
||||
{{session.identifier|default:"Anonymous"}} @ {{session.duration|naturaldelta}} <span
|
||||
class="text-gray-600">({{session.ip}})</span>
|
||||
</h3>
|
||||
<p>{{session.start_time|date:"M j Y, g:i a"}} until
|
||||
{{session.last_seen|date:"g:i a"}}</p>
|
||||
</div>
|
||||
<div>
|
||||
{% if session.is_currently_active %}<span class="chip ~positive text-base">Online</span>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<hr class="sep h-8">
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div>
|
||||
<p>Browser</p>
|
||||
<p class="label">{{session.browser|default:"Unknown"}}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>Device</p>
|
||||
<p class="label">{{session.device|default:"Unknown"}}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>Device Type</p>
|
||||
<p class="label">{{session.device_type|title}}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>OS</p>
|
||||
<p class="label">{{session.os|default:"Unknown"}}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>Network</p>
|
||||
<p class="label">{{session.asn|default:"Unknown"}}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>Country</p>
|
||||
<p class="label">{{session.country|flag_emoji}} {{session.country|default:"Unknown"}}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>Location</p>
|
||||
<p class="label">
|
||||
{% if session.latitude %}
|
||||
<a href="https://www.google.com/maps/search/?api=1&query={{session.latitude}},{{session.longitude}}">Open
|
||||
in Maps ↗</a>
|
||||
{% else %}
|
||||
Unknown
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>Time Zone</p>
|
||||
<p class="label">{{session.time_zone|default:"Unknown"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<div class="">
|
||||
{% for hit in session.hit_set.all %}
|
||||
<article class="my-12 md:flex">
|
||||
<div class="md:w-2/12">
|
||||
<div class="text-lg">{{hit.start_time|date:"g:i a"}}</div>
|
||||
</div>
|
||||
<div class="md:flex card ~neutral !low flex-grow justify-between">
|
||||
<div class="mb-4 md:mb-0 md:w-1/2">
|
||||
<p class="label font-medium text-lg">{{hit.location|urlize}}</p>
|
||||
{% if hit.referrer %}
|
||||
<p>via {{hit.referrer|urlize}}<p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-3 md:pl-8 md:w-1/2">
|
||||
<div>
|
||||
<p>Duration</p>
|
||||
<p class="label">{{hit.duration|naturaldelta}}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>Load</p>
|
||||
<p class="label">{{hit.load_time|floatformat:"0"}}ms</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>Tracker</p>
|
||||
<p class="label">{{hit.tracker}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
48
shynet/core/templates/core/pages/service_session_list.html
Normal file
48
shynet/core/templates/core/pages/service_session_list.html
Normal file
@ -0,0 +1,48 @@
|
||||
{% extends "core/service_base.html" %}
|
||||
|
||||
{% load a17t_tags pagination humanize helpers %}
|
||||
|
||||
{% block head_title %}{{object.name}} Sessions{% endblock %}
|
||||
|
||||
{% block service_actions %}
|
||||
<a href="{% url 'core:service' object.uuid %}" class="button field ~neutral !low w-auto">Analytics →</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block service_content %}
|
||||
<div class="card ~neutral !low mb-8 pt-2 max-w-full overflow-x-scroll">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th>Time</th>
|
||||
<th>Identity</th>
|
||||
<th>Network</th>
|
||||
<th>Duration</th>
|
||||
<th>Hits</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for session in object_list %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url 'core:service_session' object.pk session.pk %}" class="font-medium text-purple-700">
|
||||
{{session.start_time|date:"M j Y, g:i a"|capfirst}}
|
||||
{% if session.is_currently_active %}
|
||||
<span class="badge ~positive">Online</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{% if session.identifier %}
|
||||
<span class="chip ~neutral">{{session.identifier}}</span>
|
||||
{% else %}
|
||||
<span class="text-gray-600">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{session.country|flag_emoji}} {{session.asn|default:"Unknown"}}</td>
|
||||
<td>{{session.duration|naturaldelta}}</td>
|
||||
<td>{{session.hit_set.count|intcomma}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% pagination page_obj request %}
|
||||
{% endblock %}
|
@ -1,17 +1,14 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "core/service_base.html" %}
|
||||
|
||||
{% load a17t_tags %}
|
||||
|
||||
{% block head_title %}{{object.name}} Management{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="md:flex justify-between items-center" id="heading">
|
||||
<h3 class="heading leading-none mr-4">
|
||||
{{object.name}}
|
||||
</h3>
|
||||
<a href="{% url 'core:service' object.uuid %}" class="button field ~neutral !low w-auto">View →</a>
|
||||
</div>
|
||||
<hr class="sep">
|
||||
{% block service_actions %}
|
||||
<a href="{% url 'core:service' object.uuid %}" class="button field ~neutral !low w-auto">View →</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block service_content %}
|
||||
<div class="max-w-xl content">
|
||||
<h5>Analytics Installation</h5>
|
||||
<p>(At the end of <code><body></code>)
|
||||
@ -25,9 +22,14 @@
|
||||
<div class="p-4">
|
||||
{{form|a17t}}
|
||||
</div>
|
||||
<div class="section ~neutral !normal p-4">
|
||||
<button type="submit" class="button ~neutral !high">Save</button>
|
||||
<a href="{% url 'core:service' object.uuid %}" class="button ~neutral !low">Cancel</a>
|
||||
<div class="section ~neutral !normal p-4 flex justify-between">
|
||||
<div>
|
||||
<button type="submit" class="button ~neutral !high">Save</button>
|
||||
<a href="{% url 'core:service' object.uuid %}" class="button ~neutral !low">Cancel</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{% url 'core:service_delete' object.uuid %}" class="button ~critical !high">Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
25
shynet/core/templates/core/service_base.html
Normal file
25
shynet/core/templates/core/service_base.html
Normal file
@ -0,0 +1,25 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load humanize helpers %}
|
||||
|
||||
{% block head_title %}{{service.name}}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="md:flex justify-between items-center" id="heading">
|
||||
<a class="flex items-center" href="{% url 'core:service' object.uuid %}">
|
||||
<h3 class="heading leading-none mr-4">
|
||||
{{object.name}}
|
||||
</h3>
|
||||
<div class='text-3xl'>
|
||||
{% include 'core/includes/stats_status_chip.html' %}
|
||||
</div>
|
||||
</a>
|
||||
<div class="flex items-center">
|
||||
{% block service_actions %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<hr class="sep h-8">
|
||||
{% block service_content %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
@ -25,3 +25,9 @@ def flag_emoji(isocode):
|
||||
return flag.flag(isocode)
|
||||
except:
|
||||
return ""
|
||||
|
||||
@register.filter('startswith')
|
||||
def startswith(text, starts):
|
||||
if isinstance(text, str):
|
||||
return text.startswith(starts)
|
||||
return False
|
||||
|
@ -1,3 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
@ -13,4 +13,19 @@ urlpatterns = [
|
||||
views.ServiceUpdateView.as_view(),
|
||||
name="service_update",
|
||||
),
|
||||
path(
|
||||
"dash/service/<pk>/delete/",
|
||||
views.ServiceDeleteView.as_view(),
|
||||
name="service_delete",
|
||||
),
|
||||
path(
|
||||
"dash/service/<pk>/sessions/",
|
||||
views.ServiceSessionsListView.as_view(),
|
||||
name="service_session_list",
|
||||
),
|
||||
path(
|
||||
"dash/service/<pk>/sessions/<session_pk>/",
|
||||
views.ServiceSessionView.as_view(),
|
||||
name="service_session",
|
||||
),
|
||||
]
|
||||
|
@ -1,13 +1,21 @@
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.shortcuts import reverse
|
||||
from django.shortcuts import reverse, get_object_or_404
|
||||
from django.utils import timezone
|
||||
from django.views.generic import (CreateView, DetailView, TemplateView,
|
||||
UpdateView)
|
||||
from django.views.generic import (
|
||||
CreateView,
|
||||
DetailView,
|
||||
TemplateView,
|
||||
UpdateView,
|
||||
DeleteView,
|
||||
ListView,
|
||||
)
|
||||
|
||||
from .forms import ServiceForm
|
||||
from .mixins import BaseUrlMixin, DateRangeMixin
|
||||
from .models import Service
|
||||
|
||||
from analytics.models import Session
|
||||
|
||||
|
||||
class IndexView(TemplateView):
|
||||
template_name = "core/pages/index.html"
|
||||
@ -51,3 +59,45 @@ class ServiceUpdateView(LoginRequiredMixin, BaseUrlMixin, UpdateView):
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse("core:service", kwargs={"uuid": self.object.uuid})
|
||||
|
||||
|
||||
class ServiceDeleteView(LoginRequiredMixin, DeleteView):
|
||||
model = Service
|
||||
form_class = ServiceForm
|
||||
template_name = "core/pages/service_delete.html"
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse("core:dashboard")
|
||||
|
||||
|
||||
class ServiceSessionsListView(LoginRequiredMixin, DateRangeMixin, ListView):
|
||||
model = Session
|
||||
template_name = "core/pages/service_session_list.html"
|
||||
paginate_by = 20
|
||||
|
||||
def get_object(self):
|
||||
return get_object_or_404(Service, pk=self.kwargs.get("pk"))
|
||||
|
||||
def get_queryset(self):
|
||||
return Session.objects.filter(
|
||||
service=self.get_object(),
|
||||
start_time__lt=self.get_end_date(),
|
||||
start_time__gt=self.get_start_date(),
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
data = super().get_context_data(**kwargs)
|
||||
data["object"] = self.get_object()
|
||||
return data
|
||||
|
||||
|
||||
class ServiceSessionView(LoginRequiredMixin, DetailView):
|
||||
model = Session
|
||||
template_name = "core/pages/service_session.html"
|
||||
pk_url_kwarg = "session_pk"
|
||||
context_object_name = "session"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
data = super().get_context_data(**kwargs)
|
||||
data["object"] = get_object_or_404(Service, pk=self.kwargs.get("pk"))
|
||||
return data
|
||||
|
Loading…
Reference in New Issue
Block a user