Compare commits

..

1 Commits

Author SHA1 Message Date
Erik Kalkoken
94db5d6e87 Merge branch 'seperate-out-celery-once' into 'master'
Remove dependecies from celery_once customizations

See merge request allianceauth/allianceauth!1491
2025-09-30 22:02:45 +02:00
14 changed files with 406 additions and 438 deletions

View File

@ -27,7 +27,7 @@ def dashboard_results(hours: int) -> _TaskCounts:
my_earliest = events.first_event(earliest=earliest) my_earliest = events.first_event(earliest=earliest)
return [my_earliest] if my_earliest else [] return [my_earliest] if my_earliest else []
earliest = dt.datetime.now(dt.timezone.utc) - dt.timedelta(hours=hours) earliest = dt.datetime.utcnow() - dt.timedelta(hours=hours)
earliest_events = [] earliest_events = []
succeeded_count = succeeded_tasks.count(earliest=earliest) succeeded_count = succeeded_tasks.count(earliest=earliest)
earliest_events += earliest_if_exists(succeeded_tasks, earliest) earliest_events += earliest_if_exists(succeeded_tasks, earliest)

View File

@ -42,7 +42,7 @@ class EventSeries:
- event_time: timestamp of event. Will use current time if not specified. - event_time: timestamp of event. Will use current time if not specified.
""" """
if not event_time: if not event_time:
event_time = dt.datetime.now(dt.timezone.utc) event_time = dt.datetime.utcnow()
my_id = self._redis.incr(self._key_counter) my_id = self._redis.incr(self._key_counter)
self._redis.zadd(self._key_sorted_set, {my_id: event_time.timestamp()}) self._redis.zadd(self._key_sorted_set, {my_id: event_time.timestamp()})

View File

@ -65,7 +65,6 @@
{% include 'bundles/timers-js.html' %} {% include 'bundles/timers-js.html' %}
<script> <script>
$(document).ready(() => {
// Data // Data
const timers = [ const timers = [
{% for op in optimer %} {% for op in optimer %}
@ -144,6 +143,5 @@
// Start timed updates // Start timed updates
setInterval(timedUpdate, 1000); setInterval(timedUpdate, 1000);
});
</script> </script>
{% endblock content %} {% endblock content %}

View File

@ -100,13 +100,13 @@ def jabber_broadcast_view(request):
if main_char is not None: if main_char is not None:
message_to_send = form.cleaned_data['message'] + "\n##### SENT BY: " + "[" + main_char.corporation_ticker + "]" + \ message_to_send = form.cleaned_data['message'] + "\n##### SENT BY: " + "[" + main_char.corporation_ticker + "]" + \
main_char.character_name + " TO: " + \ main_char.character_name + " TO: " + \
form.cleaned_data['group'] + " WHEN: " + datetime.datetime.now(datetime.timezone.utc).strftime( form.cleaned_data['group'] + " WHEN: " + datetime.datetime.utcnow().strftime(
"%Y-%m-%d %H:%M:%S") + " #####\n##### Replies are NOT monitored #####\n" "%Y-%m-%d %H:%M:%S") + " #####\n##### Replies are NOT monitored #####\n"
group_to_send = form.cleaned_data['group'] group_to_send = form.cleaned_data['group']
else: else:
message_to_send = form.cleaned_data['message'] + "\n##### SENT BY: " + "No character but can send pings?" + " TO: " + \ message_to_send = form.cleaned_data['message'] + "\n##### SENT BY: " + "No character but can send pings?" + " TO: " + \
form.cleaned_data['group'] + " WHEN: " + datetime.datetime.now(datetime.timezone.utc).strftime( form.cleaned_data['group'] + " WHEN: " + datetime.datetime.utcnow().strftime(
"%Y-%m-%d %H:%M:%S") + " #####\n##### Replies are NOT monitored #####\n" "%Y-%m-%d %H:%M:%S") + " #####\n##### Replies are NOT monitored #####\n"
group_to_send = form.cleaned_data['group'] group_to_send = form.cleaned_data['group']

View File

@ -2,7 +2,7 @@ import random
import string import string
import calendar import calendar
import re import re
import datetime as dt from datetime import datetime
from passlib.apps import phpbb3_context from passlib.apps import phpbb3_context
from django.db import connections from django.db import connections
@ -128,7 +128,7 @@ class Phpbb3Manager:
@staticmethod @staticmethod
def __get_current_utc_date(): def __get_current_utc_date():
d = dt.datetime.now(dt.timezone.utc) d = datetime.utcnow()
unixtime = calendar.timegm(d.utctimetuple()) unixtime = calendar.timegm(d.utctimetuple())
return unixtime return unixtime

View File

@ -1,7 +1,7 @@
import random import random
import string import string
import calendar import calendar
import datetime as dt from datetime import datetime
import hashlib import hashlib
import logging import logging
import re import re
@ -105,7 +105,7 @@ class SmfManager:
@staticmethod @staticmethod
def get_current_utc_date(): def get_current_utc_date():
d = dt.datetime.now(dt.timezone.utc) d = datetime.utcnow()
unixtime = calendar.timegm(d.utctimetuple()) unixtime = calendar.timegm(d.utctimetuple())
return unixtime return unixtime

View File

@ -1,207 +0,0 @@
/* global fetchGet, numberFormatter, taskQueueSettings */
$(document).ready(() => {
'use strict';
const elements = {
total: document.getElementById('total-task-count'),
uptime: document.getElementById('celery-uptime'),
running: document.getElementById('running-task-count'),
queued: document.getElementById('queued-tasks-count'),
succeeded: document.getElementById('succeeded-tasks-count'),
retried: document.getElementById('retried-tasks-count'),
failed: document.getElementById('failed-tasks-count')
};
/**
* Fetches the task queue status and updates the UI elements accordingly.
* It retrieves the total number of tasks, running tasks, queued tasks,
* succeeded tasks, retried tasks, and failed tasks, and updates the
* corresponding HTML elements with the fetched data.
* It also updates the progress bars for succeeded, retried, and failed tasks.
* The function is called immediately and then every 30 seconds to keep the
* task queue status up to date.
*/
const updateTaskCount = () => {
fetchGet({url: taskQueueSettings.url})
.then((data) => {
const elemProgressBar = document.getElementById('celery-tasks-progress-bar');
const progressElements = {
succeeded: {
bar: document.getElementById('celery-progress-bar-succeeded'),
text: document.getElementById('celery-progress-bar-succeeded-progress')
},
retried: {
bar: document.getElementById('celery-progress-bar-retried'),
text: document.getElementById('celery-progress-bar-retried-progress')
},
failed: {
bar: document.getElementById('celery-progress-bar-failed'),
text: document.getElementById('celery-progress-bar-failed-progress')
}
};
// Assign progress data from the fetched data to variables
const {
earliest_task: earliestTask,
tasks_total: tasksTotal,
tasks_running: tasksRunning,
tasks_queued: tasksQueued,
tasks_succeeded: tasksSucceeded,
tasks_retried: tasksRetried,
tasks_failed: tasksFailed
} = data;
/**
* Updates the text content of the specified HTML element with the given value.
* If the value is null, it sets the text to 'N/A'.
* Otherwise, it formats the number using the locale-specific format.
*
* @param {HTMLElement} element The HTML element to update.
* @param {number|null} value The value to set in the element.
*/
const updateTaskCount = (element, value) => {
element.textContent = value === null ? taskQueueSettings.l10n.na : numberFormatter({value: value, locales: taskQueueSettings.l10n.language});
};
/**
* Calculates the time since the given timestamp and returns a formatted string.
* If the timestamp is null or undefined, it returns 'N/A'.
* The returned string is in the format of "X hours, Y minutes" or "X minutes, Y seconds".
*
* @param {string|null} timestamp The timestamp to calculate the time since.
* @returns {string} A formatted string representing the time since the timestamp.
*/
const timeSince = (timestamp) => {
if (!timestamp) {
return taskQueueSettings.l10n.na;
}
const diffSecs = Math.floor((Date.now() - new Date(timestamp)) / 1000);
if (diffSecs >= 3600) {
const hours = Math.floor(diffSecs / 3600);
const minutes = Math.floor((diffSecs % 3600) / 60);
if (minutes > 0) {
const hourText = hours === 1 ? taskQueueSettings.l10n.hour_singular : taskQueueSettings.l10n.hour_plural;
const minuteText = minutes === 1 ? taskQueueSettings.l10n.minute_singular : taskQueueSettings.l10n.minute_plural;
return `${hours} ${hourText}, ${minutes} ${minuteText}`;
}
const hourText = hours === 1 ? taskQueueSettings.l10n.hour_singular : taskQueueSettings.l10n.hour_plural;
return `${hours} ${hourText}`;
}
const units = [
[
60,
taskQueueSettings.l10n.minute_singular,
taskQueueSettings.l10n.minute_plural
],
[
1,
taskQueueSettings.l10n.second_singular,
taskQueueSettings.l10n.second_plural
]
];
for (const [seconds, singular, plural] of units) {
const value = Math.floor(diffSecs / seconds);
if (value > 0) {
return `${value} ${value > 1 ? plural : singular}`;
}
}
return `0 ${taskQueueSettings.l10n.second_plural}`;
};
/**
* Updates the progress bar element and its text content based on the given value and total.
* It calculates the percentage of completion and updates the aria attributes and styles accordingly.
*
* @param {HTMLElement} element The progress bar element to update.
* @param {HTMLElement} textElement The text element to update with the percentage.
* @param {number} value The current value to set in the progress bar.
* @param {number} total The total value for calculating the percentage.
*/
const updateProgressBar = (element, textElement, value, total) => {
const percentage = total ? (value / total) * 100 : 0;
element.setAttribute('aria-valuenow', percentage.toString());
textElement.textContent = `${numberFormatter({value: percentage.toFixed(0), locales: taskQueueSettings.l10n.language})}%`;
element.style.width = `${percentage}%`;
};
// Update task counts
[
[elements.total, tasksTotal],
[elements.running, tasksRunning],
[elements.queued, tasksQueued],
[elements.succeeded, tasksSucceeded],
[elements.retried, tasksRetried],
[elements.failed, tasksFailed]
].forEach(([element, value]) => {
updateTaskCount(element, value);
});
// Update uptime
elements.uptime.textContent = timeSince(earliestTask);
// Update progress bar title
const [
titleTextSucceeded,
titleTextRetried,
titleTextFailed
] = [
[tasksSucceeded, taskQueueSettings.l10n.succeeded],
[tasksRetried, taskQueueSettings.l10n.retried],
[tasksFailed, taskQueueSettings.l10n.failed]
].map(([count, label]) => {
return `${numberFormatter({value: count, locales: taskQueueSettings.l10n.language})} ${label}`;
});
// Set the title attribute for the progress bar
elemProgressBar.setAttribute(
'title',
`${titleTextSucceeded}, ${titleTextRetried}, ${titleTextFailed}`
);
// Update progress bars
[
tasksSucceeded,
tasksRetried,
tasksFailed
].forEach((count, index) => {
const type = ['succeeded', 'retried', 'failed'][index];
updateProgressBar(
progressElements[type].bar,
progressElements[type].text,
count,
tasksTotal
);
});
})
.catch((error) => {
console.error('Error fetching task queue:', error);
// If there is an error fetching the task queue, set all elements to 'ERROR'
[
elements.running,
elements.queued,
elements.succeeded,
elements.retried,
elements.failed
].forEach((elem) => {
elem.textContent = taskQueueSettings.l10n.error;
});
});
};
updateTaskCount();
setInterval(updateTaskCount, 30000);
});

View File

@ -1,30 +0,0 @@
$(document).ready(() => {
'use strict';
const sidebar = document.getElementById('sidebar');
const sidebarKey = `sidebar_${sidebar.id}`;
sidebar.addEventListener('shown.bs.collapse', (event) => {
if (event.target.id === sidebar.id) {
localStorage.removeItem(sidebarKey);
}
});
sidebar.addEventListener('hidden.bs.collapse', (event) => {
if (event.target.id === sidebar.id) {
localStorage.setItem(sidebarKey, 'closed');
}
});
sidebar.classList.toggle('show', localStorage.getItem(sidebarKey) !== 'closed');
const activeChildMenuItem = document.querySelector('ul#sidebar-menu ul.collapse a.active');
if (activeChildMenuItem) {
const activeChildMenuUl = activeChildMenuItem.closest('ul');
activeChildMenuUl.classList.add('show');
document.querySelectorAll(`[data-bs-target^="#${activeChildMenuUl.id}"]`)
.forEach(element => element.setAttribute('aria-expanded', true));
}
});

View File

@ -1,32 +1,29 @@
{% load i18n %} {% load i18n %}
<div id="esi-alert" class="col-12 collapse"> <div id="esi-alert" class="col-12 collapse">
<div class="alert alert-warning"> <div class="alert alert-warning">
<p class="text-center ">{% translate 'Your Server received an ESI error response code of ' %}<b id="esi-code">?</b></p> <p class="text-center ">{% translate 'Your Server received an ESI error response code of ' %}<b id="esi-code">?</b></p>
<hr> <hr>
<pre id="esi-data" class="text-center text-wrap"></pre> <pre id="esi-data" class="text-center text-wrap"></pre>
</div> </div>
</div>
<script> <script>
$(document).ready(() => { const elemCard = document.getElementById('esi-alert');
const elements = { const elemMessage = document.getElementById('esi-data');
card: document.getElementById('esi-alert'), const elemCode = document.getElementById('esi-code');
message: document.getElementById('esi-data'),
code: document.getElementById('esi-code')
};
fetchGet({url: '{% url "authentication:esi_check" %}'}) fetchGet({url: '{% url "authentication:esi_check" %}'})
.then(({status, data}) => { .then((data) => {
console.log('ESI Check:', JSON.stringify({status, data}, null, 2)); console.log('ESI Check: ', JSON.stringify(data, null, 2));
if (status !== 200) { if (data.status !== 200) {
elements.code.textContent = status; elemCode.textContent = data.status;
elements.message.textContent = data.error; elemMessage.textContent = data.data.error;
new bootstrap.Collapse(elements.card, {toggle: true}); new bootstrap.Collapse(elemCard, {toggle: true});
} }
}) })
.catch((error) => console.error('Error fetching ESI check:', error)); .catch((error) => {
console.error('Error fetching ESI check:', error);
}); });
</script> </script>
</div>

View File

@ -161,24 +161,206 @@ the escapejs filter without having to redefine them later.
</div> </div>
<script> <script>
const taskQueueSettings = { const elements = {
url: '{% url "authentication:task_counts" %}', total: document.getElementById('total-task-count'),
l10n: { uptime: document.getElementById('celery-uptime'),
language: '{{ LANGUAGE_CODE }}', running: document.getElementById('running-task-count'),
second_singular: '{{ l10nSecondSingular|escapejs }}', queued: document.getElementById('queued-tasks-count'),
second_plural: '{{ l10nSecondPlural|escapejs }}', succeeded: document.getElementById('succeeded-tasks-count'),
minute_singular: '{{ l10nMinuteSingular|escapejs }}', retried: document.getElementById('retried-tasks-count'),
minute_plural: '{{ l10nMinutePlural|escapejs }}', failed: document.getElementById('failed-tasks-count')
hour_singular: '{{ l10nHourSingular|escapejs }}', };
hour_plural: '{{ l10nHourPlural|escapejs }}',
na: '{{ l10nNA|escapejs }}', /**
error: '{{ l10nError|escapejs }}', * Fetches the task queue status and updates the UI elements accordingly.
running: '{{ l10nRunning|escapejs }}', * It retrieves the total number of tasks, running tasks, queued tasks,
queued: '{{ l10nQueued|escapejs }}', * succeeded tasks, retried tasks, and failed tasks, and updates the
succeeded: '{{ l10nSucceeded|escapejs }}', * corresponding HTML elements with the fetched data.
retried: '{{ l10nRetried|escapejs }}', * It also updates the progress bars for succeeded, retried, and failed tasks.
failed: '{{ l10nFailed|escapejs }}' * The function is called immediately and then every 30 seconds to keep the
* task queue status up to date.
*/
const updateTaskCount = () => {
fetchGet({url: '{% url "authentication:task_counts" %}'})
.then((data) => {
const numberL10nFormat = new Intl.NumberFormat('{{ LANGUAGE_CODE }}');
const elemProgressBar = document.getElementById('celery-tasks-progress-bar');
const progressElements = {
succeeded: {
bar: document.getElementById('celery-progress-bar-succeeded'),
text: document.getElementById('celery-progress-bar-succeeded-progress')
},
retried: {
bar: document.getElementById('celery-progress-bar-retried'),
text: document.getElementById('celery-progress-bar-retried-progress')
},
failed: {
bar: document.getElementById('celery-progress-bar-failed'),
text: document.getElementById('celery-progress-bar-failed-progress')
} }
}; };
// Assign progress data from the fetched data to variables
const {
earliest_task: earliestTask,
tasks_total: tasksTotal,
tasks_running: tasksRunning,
tasks_queued: tasksQueued,
tasks_succeeded: tasksSucceeded,
tasks_retried: tasksRetried,
tasks_failed: tasksFailed
} = data;
/**
* Updates the text content of the specified HTML element with the given value.
* If the value is null, it sets the text to 'N/A'.
* Otherwise, it formats the number using the locale-specific format.
*
* @param {HTMLElement} element The HTML element to update.
* @param {number|null} value The value to set in the element.
*/
const updateTaskCount = (element, value) => {
element.textContent = value == null ? '{{ l10nNA|escapejs }}' : numberL10nFormat.format(value);
};
/**
* Calculates the time since the given timestamp and returns a formatted string.
* If the timestamp is null or undefined, it returns 'N/A'.
* The returned string is in the format of "X hours, Y minutes" or "X minutes, Y seconds".
*
* @param {string|null} timestamp The timestamp to calculate the time since.
* @returns {string} A formatted string representing the time since the timestamp.
*/
const timeSince = (timestamp) => {
if (!timestamp) {
return '{{ l10nNA|escapejs }}';
}
const diffSecs = Math.floor((Date.now() - new Date(timestamp)) / 1000);
if (diffSecs >= 3600) {
const hours = Math.floor(diffSecs / 3600);
const minutes = Math.floor((diffSecs % 3600) / 60);
if (minutes > 0) {
const hourText = hours === 1 ? '{{ l10nHourSingular|escapejs }}' : '{{ l10nHourPlural|escapejs }}';
const minuteText = minutes === 1 ? '{{ l10nMinuteSingular|escapejs }}' : '{{ l10nMinutePlural|escapejs }}';
return `${hours} ${hourText}, ${minutes} ${minuteText}`;
}
const hourText = hours === 1 ? '{{ l10nHourSingular|escapejs }}' : '{{ l10nHourPlural|escapejs }}';
return `${hours} ${hourText}`;
}
const units = [
[
60,
'{{ l10nMinuteSingular|escapejs }}',
'{{ l10nMinutePlural|escapejs }}'
],
[
1,
'{{ l10nSecondSingular|escapejs }}',
'{{ l10nSecondPlural|escapejs }}'
]
];
for (const [seconds, singular, plural] of units) {
const value = Math.floor(diffSecs / seconds);
if (value > 0) {
return `${value} ${value > 1 ? plural : singular}`;
}
}
return '0 {{ l10nSecondPlural|escapejs }}';
};
/**
* Updates the progress bar element and its text content based on the given value and total.
* It calculates the percentage of completion and updates the aria attributes and styles accordingly.
*
* @param {HTMLElement} element The progress bar element to update.
* @param {HTMLElement} textElement The text element to update with the percentage.
* @param {number} value The current value to set in the progress bar.
* @param {number} total The total value for calculating the percentage.
*/
const updateProgressBar = (element, textElement, value, total) => {
const percentage = total ? (value / total) * 100 : 0;
element.setAttribute('aria-valuenow', percentage.toString());
textElement.textContent = `${numberL10nFormat.format(percentage.toFixed(0))}%`;
element.style.width = `${percentage}%`;
};
// Update task counts
[
[elements.total, tasksTotal],
[elements.running, tasksRunning],
[elements.queued, tasksQueued],
[elements.succeeded, tasksSucceeded],
[elements.retried, tasksRetried],
[elements.failed, tasksFailed]
].forEach(([element, value]) => {
updateTaskCount(element, value);
});
// Update uptime
elements.uptime.textContent = timeSince(earliestTask);
// Update progress bar title
const [
titleTextSucceeded,
titleTextRetried,
titleTextFailed
] = [
[tasksSucceeded, '{{ l10nSucceeded|escapejs }}'],
[tasksRetried, '{{ l10nRetried|escapejs }}'],
[tasksFailed, '{{ l10nFailed|escapejs }}']
].map(([count, label]) => {
return `${numberL10nFormat.format(count)} ${label}`;
});
// Set the title attribute for the progress bar
elemProgressBar.setAttribute(
'title',
`${titleTextSucceeded}, ${titleTextRetried}, ${titleTextFailed}`
);
// Update progress bars
[
tasksSucceeded,
tasksRetried,
tasksFailed
].forEach((count, index) => {
const type = ['succeeded', 'retried', 'failed'][index];
updateProgressBar(
progressElements[type].bar,
progressElements[type].text,
count,
tasksTotal
);
});
})
.catch((error) => {
console.error('Error fetching task queue:', error);
// If there is an error fetching the task queue, set all elements to 'ERROR'
[
elements.running,
elements.queued,
elements.succeeded,
elements.retried,
elements.failed
].forEach((elem) => {
elem.textContent = '{{ l10nError|escapejs }}';
});
});
};
updateTaskCount();
setInterval(updateTaskCount, 30000);
</script> </script>
{% include "bundles/auth-dashboard-task-queue-js.html" %}

View File

@ -102,7 +102,44 @@
</main> </main>
<!-- End Body --> <!-- End Body -->
{% include "bundles/auth-sidebar-collapse-js.html" %} <script>
(() => {
// TODO Move to own JS file
const sidebar = document.getElementById('sidebar');
const sidebarKey = `sidebar_${sidebar.id}`;
sidebar.addEventListener('shown.bs.collapse', (event) => {
if (event.target.id === sidebar.id) {
localStorage.removeItem(sidebarKey);
}
});
sidebar.addEventListener('hidden.bs.collapse', (event) => {
if (event.target.id === sidebar.id) {
localStorage.setItem(sidebarKey, 'closed');
}
});
if (localStorage.getItem(sidebarKey) === 'closed') {
sidebar.classList.remove('show');
} else {
sidebar.classList.add('show');
}
const activeChildMenuItem = document.querySelector('#sidebar-menu li ul li a.active');
if (activeChildMenuItem) {
const activeChildMenuUl = activeChildMenuItem.parentElement.parentElement;
const elementsToToggle = document.querySelectorAll(`[data-bs-target^="#${activeChildMenuUl.id}"]`);
activeChildMenuUl.classList.add('show');
elementsToToggle.forEach((element) => {
element.setAttribute('aria-expanded', true);
});
}
})();
</script>
{% theme_js %} {% theme_js %}

View File

@ -1,3 +0,0 @@
{% load sri %}
{% sri_static 'allianceauth/js/dashboard-update-task-queue.js' %}

View File

@ -1,3 +0,0 @@
{% load sri %}
{% sri_static 'allianceauth/js/sidebar-collapse.js' %}

View File

@ -96,7 +96,6 @@
{% get_datatables_language_static LANGUAGE_CODE as DT_LANG_PATH %} {% get_datatables_language_static LANGUAGE_CODE as DT_LANG_PATH %}
<script> <script>
$(document).ready(() => {
const timers = [ const timers = [
{% for timer in timers %} {% for timer in timers %}
{ {
@ -176,6 +175,7 @@
// Start timed updates // Start timed updates
setInterval(timedUpdate, 1000); setInterval(timedUpdate, 1000);
$(document).ready(() => {
const dtOptions = { const dtOptions = {
language: {url: '{{ DT_LANG_PATH }}'}, language: {url: '{{ DT_LANG_PATH }}'},
order: [ order: [
@ -185,10 +185,7 @@
{% if perms.auth.timer_management %} {% if perms.auth.timer_management %}
dtOptions['columnDefs'] = [ dtOptions['columnDefs'] = [
{ { "orderable": false, "targets": 7 }
"orderable": false,
"targets": 7
}
]; ];
{% endif %} {% endif %}