Compare commits

..

14 Commits

Author SHA1 Message Date
Ariel Rin
cda5ce739f Merge branch 'mumbleconnectionhistory' into 'master'
Mumble Connection History Page

See merge request allianceauth/allianceauth!1648
2024-10-04 12:12:17 +00:00
Ariel Rin
e5c8426ea3 Merge branch 'fixing-bs3-side-menu' into 'master'
[REMOVE] Duplicate menu item

See merge request allianceauth/allianceauth!1654
2024-10-04 12:07:22 +00:00
Ariel Rin
b2bd489ddc Merge branch 'translations_7f31a07ccd4e4a66b1dd7b6bc2dbddb5' into 'master'
Updates for project Alliance Auth

See merge request allianceauth/allianceauth!1653
2024-10-04 12:06:53 +00:00
Ariel Rin
6397cf358a Updates for project Alliance Auth 2024-10-04 12:06:53 +00:00
Ariel Rin
90fcc4a811 Merge branch 'master' into 'master'
Add an example to the notification documentation

See merge request allianceauth/allianceauth!1652
2024-10-04 12:05:17 +00:00
Ariel Rin
ec7472fe22 Merge branch 'bootstrap-dark-fixes' into 'master'
[FIX] Re-add the dark bootstrap html tag

See merge request allianceauth/allianceauth!1651
2024-10-04 12:03:25 +00:00
Peter Pfeufer
5148b1914d
[REMOVE] Duplicate menu item 2024-10-04 13:56:57 +02:00
Ariel Rin
2e0716f5ae Merge branch 'mumbleconnectionhistory-improvements' into 'mumbleconnectionhistory'
[CHANGE] Some improvements

See merge request soratidus999/allianceauth!12
2024-09-23 23:33:01 +00:00
Peter Pfeufer
1fb091acb2
[CHANGE] Some improvements
- Fixed Bootstraps cards, rows and cols
- Replaced style argument with Bootstrap class
- Removed unused Django templatetag
- JS modernized and moved to its own script tag instead of concatenating it with other more or less "global" scripts
- Fixed Bootstrap classes
2024-09-23 15:09:35 +02:00
r0kym
07c62ed32a Add an example to the notification documentation 2024-09-23 13:15:19 +02:00
Aaron Kable
660fe79d08 re-add the dark bootstrap html tag 2024-09-17 21:24:01 +08:00
Joel Falknau
1dea92ed76
add Connection History 2024-09-12 15:54:14 +10:00
Joel Falknau
3f54d49d8b
update help text from mumble definition 2024-09-12 15:54:06 +10:00
Joel Falknau
77da6928b2
add Chart.js 4.4.1 to Bundles 2024-09-12 15:53:53 +10:00
11 changed files with 300 additions and 14 deletions

View File

@ -49,7 +49,7 @@ class GroupsMenuItem(MenuItemHook):
MenuItemHook.__init__(
self,
text=_("Groups"),
classes="fa-solid fa-user",
classes="fa-solid fa-users",
url_name="groupmanagement:groups",
order=25,
navactive=[

View File

@ -68,7 +68,7 @@ msgstr "Englisch"
#: allianceauth/authentication/models.py:72
msgid "Czech"
msgstr ""
msgstr "Tschechisch"
#: allianceauth/authentication/models.py:73
#: allianceauth/project_template/project_name/settings/base.py:101
@ -108,7 +108,7 @@ msgstr "Russisch"
#: allianceauth/authentication/models.py:80
#: allianceauth/project_template/project_name/settings/base.py:107
msgid "Dutch"
msgstr ""
msgstr "Niederländisch"
#: allianceauth/authentication/models.py:81
#: allianceauth/project_template/project_name/settings/base.py:108
@ -123,7 +123,7 @@ msgstr "Ukrainisch"
#: allianceauth/authentication/models.py:83
#: allianceauth/project_template/project_name/settings/base.py:111
msgid "Simplified Chinese"
msgstr ""
msgstr "Vereinfachtes Chinesisch"
#: allianceauth/authentication/models.py:99
#: allianceauth/menu/templates/menu/menu-user.html:42
@ -1664,7 +1664,7 @@ msgstr "Anstehende Flotten"
#: allianceauth/optimer/templates/optimer/management.html:44
#: allianceauth/timerboard/templates/timerboard/view.html:62
msgid "No upcoming timers."
msgstr "Keine bevorstehenden Timer."
msgstr "Keine anstehenden Timer."
#: allianceauth/optimer/templates/optimer/management.html:52
msgid "Past Fleet Operations"
@ -2804,12 +2804,12 @@ msgstr "Entankernd"
#: allianceauth/timerboard/models.py:58
msgid "Abandoned"
msgstr ""
msgstr "Aufgegeben"
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:7
#: allianceauth/timerboard/templates/timerboard/view.html:53
msgid "Upcoming Timers"
msgstr "Bevorstehende Timefr"
msgstr "Anstehende Timer"
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:15
msgid "Timer"

View File

@ -1 +1,21 @@
"""
Example
=======
.. code-block:: python
from allianceauth.notifications.models import Notification
def notify_user_view(request):
'''Simple view sending a notification to the user'''
Notification.objects.notify_user(
user=request.user,
title="Some title",
message="Some message",
level=Notification.Level.INFO,
)
"""
from .core import notify # noqa: F401

View File

@ -90,7 +90,7 @@ class MumbleUser(AbstractServiceModel):
blank=True,
null=True,
editable=False,
help_text="The Mumble Release the user last authenticated with"
help_text="Client release. For official releases, this equals the version. For snapshots and git compiles, this will be something else."
)
version = models.IntegerField(
verbose_name="Mumble Version",
@ -159,4 +159,5 @@ class MumbleUser(AbstractServiceModel):
class Meta:
permissions = (
("access_mumble", "Can access the Mumble service"),
("view_connection_history", "Can access the connection history of the Mumble service"),
)

View File

@ -0,0 +1,205 @@
{% extends "allianceauth/base-bs5.html" %}
{% load i18n %}
{% block page_title %}
{% translate "Mumble" %}
{% endblock page_title %}
{% block header_nav_brand %}
<a class="navbar-brand">{% trans "Mumble History" %} - {{ mumble_url }}</a>
{% endblock header_nav_brand %}
{% block header_nav_collapse_left %}
{% endblock header_nav_collapse_left %}
{% block header_nav_collapse_right %}
{% endblock header_nav_collapse_right %}
{% block content %}
<div class="card col-lg-12 mb-3">
<div class="card-header">
<span class="card-title">{% translate "Server Connection History" %}</span>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table w-100" id="table-mumble-connection-history">
<thead>
<tr>
<th class="text-start">{% translate "User" %}</th>
<th class="text-start">{% translate "Displayed Name" %}</th>
<th class="text-start">{% translate "Release" %}</th>
<th class="text-start">{% translate "Version" %}</th>
<th class="text-end">{% translate "Last Connect" %}</th>
<th class="text-end">{% translate "Last Disconnect" %}</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6">
<div class="card">
<div class="card-header">
<span class="card-title">{% translate "Server Connection Breakdown" %}</span>
</div>
<div class="card-body">
<canvas id="pieChart"></canvas> <!-- Canvas element for the pie chart -->
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card">
<div class="card-header">
<span class="card-title">{% translate "Server Connection Breakdown" %}</span>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table w-100" id="table-mumble-connection-stats">
<thead>
<tr>
<th class="text-start">{% translate "Version" %}</th>
<th class="text-end">{% translate "Number" %}</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}
{% block extra_javascript %}
{% include "bundles/datatables-js-bs5.html" %}
{% include "bundles/filterdropdown-js.html" %}
{% include "bundles/chart-js.html" %}
{% include "bundles/moment-js.html" with locale=True %}
<script>
$(document).ready(function () {
const MUMBLESTATS_DATETIME_FORMAT = 'YYYY-MM-DD, HH:mm';
'use strict';
$("#table-mumble-connection-history").DataTable({
ajax: {
url: '{% url "mumble:connection_history_data" %}',
dataSrc: 'connection_history_data',
},
columns: [
{ data: 'user' },
{ data: 'display_name' },
{ data: 'release' },
{ data: 'version' },
{
data: 'last_connect',
render: (data) => {
return moment(data).utc().format(MUMBLESTATS_DATETIME_FORMAT);
},
className: 'text-end',
},
{
data: 'last_disconnect',
render: (data) => {
return moment(data).utc().format(MUMBLESTATS_DATETIME_FORMAT);
},
className: 'text-end',
},
],
order: [[4, 'desc']],
processing: true,
stateSave: true,
stateDuration: 0,
filterDropDown: {
columns: [
{
idx: 2,
},
{
idx: 3,
},
],
bootstrap: true,
bootstrap_version: 5,
},
});
$("#table-mumble-connection-stats").DataTable({
ajax: {
url: '{% url "mumble:release_counts_data" %}',
dataSrc: 'release_counts_data',
},
columns: [
{ data: 'release' },
{ data: 'user_count', className: 'text-end' },
],
order: [[1, 'desc']],
processing: true,
stateSave: true,
stateDuration: 0,
});
// Initialize empty Pie chart
const ctx = document.getElementById('pieChart').getContext('2d');
const pieChart = new Chart(ctx, {
type: 'pie',
data: {
labels: [], // Initially empty
datasets: [
{
label: 'Server Connection Breakdown',
data: [], // Initially empty
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)'
],
borderWidth: 1
}
]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'top'
}
}
}
});
// AJAX call to dynamically update the chart
$.ajax({
url: '{% url "mumble:release_pie_chart_data" %}', // Your Django view URL that returns chart data
method: "GET",
success: (data) => {
// Replace chart data with the data from the AJAX response
pieChart.data.labels = data.labels; // Set the new labels
pieChart.data.datasets[0].data = data.values; // Set the new values
// Update the chart to reflect the new data
pieChart.update();
},
error: (xhr, status, error) => {
console.error('Error fetching pie chart data:', status, error);
},
});
});
</script>
{% endblock extra_javascript %}
{% block extra_css %}
{% include "bundles/datatables-css-bs5.html" %}
{% endblock extra_css %}

View File

@ -48,4 +48,9 @@
</a>
{% endif %}
{% endif %}
{% if request.user.is_superuser %}
<a class="btn btn-primary" type="button" id="btnMumbleConnectionHistory" href="{% url 'mumble:connection_history' %}" title="{% translate 'Mumble Connection History' %}">
<i class="fa-solid fa-clock-rotate-left"></i> History
</a>
{% endif %}
{% endblock %}

View File

@ -10,6 +10,10 @@ module_urls = [
path('deactivate/', views.DeleteMumbleView.as_view(), name='deactivate'),
path('reset_password/', views.ResetPasswordMumbleView.as_view(), name='reset_password'),
path('set_password/', views.SetPasswordMumbleView.as_view(), name='set_password'),
path('connection_history/', views.connection_history, name="connection_history"),
path('ajax/connection_history_data', views.connection_history_data, name="connection_history_data"),
path('ajax/release_counts_data', views.release_counts_data, name="release_counts_data"),
path('ajax/release_pie_chart_data', views.release_pie_chart_data, name="release_pie_chart_data"),
]
urlpatterns = [

View File

@ -3,6 +3,11 @@ import logging
from allianceauth.services.forms import ServicePasswordModelForm
from allianceauth.services.abstract import BaseCreatePasswordServiceAccountView, BaseDeactivateServiceAccountView, \
BaseResetPasswordServiceAccountView, BaseSetPasswordServiceAccountView
from django.conf import settings
from django.contrib.auth.decorators import login_required, permission_required
from django.db.models import Count
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render
from .models import MumbleUser
@ -35,3 +40,51 @@ class ResetPasswordMumbleView(MumbleViewMixin, BaseResetPasswordServiceAccountVi
class SetPasswordMumbleView(MumbleViewMixin, BaseSetPasswordServiceAccountView):
form_class = MumblePasswordForm
@login_required
@permission_required('mumble.view_connection_history')
def connection_history(request) -> HttpResponse:
context = {
"mumble_url": settings.MUMBLE_URL,
}
return render(request, 'services/mumble/mumble_connection_history.html', context)
@login_required
@permission_required("mumble.view_connection_history")
def connection_history_data(request) -> JsonResponse:
connection_history_data = MumbleUser.objects.all(
).values(
'user',
'display_name',
'release',
'version',
'last_connect',
'last_disconnect',
)
return JsonResponse({"connection_history_data": list(connection_history_data)})
@login_required
@permission_required("mumble.view_connection_history")
def release_counts_data(request) -> JsonResponse:
release_counts_data = MumbleUser.objects.values('release').annotate(user_count=Count('user_id')).order_by('release')
return JsonResponse({
"release_counts_data": list(release_counts_data),
})
@login_required
@permission_required("mumble.view_connection_history")
def release_pie_chart_data(request) -> JsonResponse:
release_counts = MumbleUser.objects.values('release').annotate(user_count=Count('user_id')).order_by('release')
return JsonResponse({
"labels": list(release_counts.values_list("release", flat=True)),
"values": list(release_counts.values_list("user_count", flat=True)),
})

View File

@ -10,11 +10,6 @@
<i class="fa-solid fa-gauge-high fa-fw"></i> {% translate "Dashboard" %}
</a>
</li>
<li>
<a class="{% navactive request 'groupmanagement:groups' %}" href="{% url 'groupmanagement:groups' %}">
<i class="fa-solid fa-users fa-fw"></i> {% translate "Groups" %}
</a>
</li>
{% menu_items %}
</ul>

View File

@ -0,0 +1,3 @@
<!-- Start Chart.js js from cdnjs -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js" integrity="sha512-CQBWl4fJHWbryGE+Pc7UAxWMUMNMWzWxF4SQo9CgkJIN1kx6djDQZjh3Y8SZ1d+6I+1zze6Z7kHXO7q3UyZAWw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- End Chart.js js from cdnjs -->

View File

@ -45,7 +45,7 @@ class BootstrapDarkThemeHook(ThemeHook):
self,
"Bootstrap Dark",
"Powerful, extensible, and feature-packed frontend toolkit.",
html_tags={"data-theme": "bootstrap-dark"},
html_tags={"data-theme": "bootstrap-dark", "data-bs-theme":"dark"},
css=CSS_STATICS,
js=JS_STATICS,
header_padding="3.5em"