mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-11-23 18:11:09 +00:00
Merge pull request #2298 from bookwyrm-social/import-review-null-state
Adds celery status view
This commit is contained in:
commit
d5c095ce6e
7 changed files with 247 additions and 0 deletions
109
bookwyrm/templates/settings/celery.html
Normal file
109
bookwyrm/templates/settings/celery.html
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
{% extends 'settings/layout.html' %}
|
||||||
|
{% load humanize %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load celery_tags %}
|
||||||
|
|
||||||
|
{% block title %}{% trans "Celery Status" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block header %}{% trans "Celery Status" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block panel %}
|
||||||
|
|
||||||
|
{% if queues %}
|
||||||
|
<section class="block content">
|
||||||
|
<h2>{% trans "Queues" %}</h2>
|
||||||
|
<div class="columns has-text-centered">
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="notification">
|
||||||
|
<p class="header">{% trans "Low priority" %}</p>
|
||||||
|
<p class="title is-5">{{ queues.low_priority|intcomma }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="notification">
|
||||||
|
<p class="header">{% trans "Medium priority" %}</p>
|
||||||
|
<p class="title is-5">{{ queues.medium_priority|intcomma }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="notification">
|
||||||
|
<p class="header">{% trans "High priority" %}</p>
|
||||||
|
<p class="title is-5">{{ queues.high_priority|intcomma }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% else %}
|
||||||
|
<div class="notification is-danger is-flex is-align-items-start">
|
||||||
|
<span class="icon icon-warning is-size-4 pr-3" aria-hidden="true"></span>
|
||||||
|
<span>
|
||||||
|
{% trans "Could not connect to Redis broker" %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if stats %}
|
||||||
|
<section class="block content">
|
||||||
|
<h2>{% trans "Active Tasks" %}</h2>
|
||||||
|
{% for worker in active_tasks.values %}
|
||||||
|
<div class="table-container">
|
||||||
|
<table class="table is-striped is-fullwidth">
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "ID" %}</th>
|
||||||
|
<th>{% trans "Task name" %}</th>
|
||||||
|
<th>{% trans "Run time" %}</th>
|
||||||
|
<th>{% trans "Priority" %}</th>
|
||||||
|
</tr>
|
||||||
|
{% if not worker %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="4">
|
||||||
|
<em>{% trans "No active tasks" %}</em>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% for task in worker %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ task.id }}</td>
|
||||||
|
<td>{{ task.name|shortname }}</td>
|
||||||
|
<td>{{ task.time_start|runtime }}</td>
|
||||||
|
<td>{{ task.delivery_info.routing_key }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="block content">
|
||||||
|
<h2>{% trans "Workers" %}</h2>
|
||||||
|
|
||||||
|
{% for worker_name, worker in stats.items %}
|
||||||
|
<div class="notification">
|
||||||
|
<h3>{{ worker_name }}</h3>
|
||||||
|
{% trans "Uptime:" %} {{ worker.uptime|uptime }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
<div class="notification is-danger is-flex is-align-items-start">
|
||||||
|
<span class="icon icon-warning is-size-4 pr-3" aria-hidden="true"></span>
|
||||||
|
<span>
|
||||||
|
{% trans "Could not connect to Celery" %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if errors %}
|
||||||
|
<div class="block content">
|
||||||
|
<h2>{% trans "Errors" %}</h2>
|
||||||
|
{% for error in errors %}
|
||||||
|
<pre>{{ error }}</pre>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -74,6 +74,15 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if perms.edit_instance_settings %}
|
||||||
|
<h2 class="menu-label">{% trans "System" %}</h2>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li>
|
||||||
|
{% url 'settings-celery' as url %}
|
||||||
|
<a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Celery status" %}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
{% if perms.bookwyrm.edit_instance_settings %}
|
{% if perms.bookwyrm.edit_instance_settings %}
|
||||||
<h2 class="menu-label">{% trans "Instance Settings" %}</h2>
|
<h2 class="menu-label">{% trans "Instance Settings" %}</h2>
|
||||||
<ul class="menu-list">
|
<ul class="menu-list">
|
||||||
|
|
24
bookwyrm/templatetags/celery_tags.py
Normal file
24
bookwyrm/templatetags/celery_tags.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
""" template filters for really common utilities """
|
||||||
|
import datetime
|
||||||
|
from django import template
|
||||||
|
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter(name="uptime")
|
||||||
|
def uptime(seconds):
|
||||||
|
"""Seconds uptime to a readable format"""
|
||||||
|
return str(datetime.timedelta(seconds=seconds))
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter(name="runtime")
|
||||||
|
def runtime(timestamp):
|
||||||
|
"""How long has it been?"""
|
||||||
|
return datetime.datetime.now() - datetime.datetime.fromtimestamp(timestamp)
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter(name="shortname")
|
||||||
|
def shortname(name):
|
||||||
|
"""removes bookwyrm.celery..."""
|
||||||
|
return ".".join(name.split(".")[-2:])
|
45
bookwyrm/tests/views/admin/test_celery.py
Normal file
45
bookwyrm/tests/views/admin/test_celery.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
""" test for app action functionality """
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from django.contrib.auth.models import Group
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.test.client import RequestFactory
|
||||||
|
|
||||||
|
from bookwyrm import models, views
|
||||||
|
from bookwyrm.management.commands import initdb
|
||||||
|
from bookwyrm.tests.validate_html import validate_html
|
||||||
|
|
||||||
|
|
||||||
|
class CeleryStatusViews(TestCase):
|
||||||
|
"""every response to a get request, html or json"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""we need basic test data and mocks"""
|
||||||
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
|
||||||
|
"bookwyrm.activitystreams.populate_stream_task.delay"
|
||||||
|
), patch("bookwyrm.lists_stream.populate_lists_task.delay"):
|
||||||
|
self.local_user = models.User.objects.create_user(
|
||||||
|
"mouse@local.com",
|
||||||
|
"mouse@mouse.mouse",
|
||||||
|
"password",
|
||||||
|
local=True,
|
||||||
|
localname="mouse",
|
||||||
|
)
|
||||||
|
initdb.init_groups()
|
||||||
|
initdb.init_permissions()
|
||||||
|
group = Group.objects.get(name="admin")
|
||||||
|
self.local_user.groups.set([group])
|
||||||
|
models.SiteSettings.objects.create()
|
||||||
|
|
||||||
|
def test_celery_status_get(self):
|
||||||
|
"""there are so many views, this just makes sure it LOADS"""
|
||||||
|
view = views.CeleryStatus.as_view()
|
||||||
|
request = self.factory.get("")
|
||||||
|
request.user = self.local_user
|
||||||
|
|
||||||
|
result = view(request)
|
||||||
|
self.assertIsInstance(result, TemplateResponse)
|
||||||
|
validate_html(result.render())
|
||||||
|
self.assertEqual(result.status_code, 200)
|
|
@ -291,6 +291,9 @@ urlpatterns = [
|
||||||
views.Report.as_view(),
|
views.Report.as_view(),
|
||||||
name="report-link",
|
name="report-link",
|
||||||
),
|
),
|
||||||
|
re_path(
|
||||||
|
r"^settings/celery/?$", views.CeleryStatus.as_view(), name="settings-celery"
|
||||||
|
),
|
||||||
# landing pages
|
# landing pages
|
||||||
re_path(r"^about/?$", views.about, name="about"),
|
re_path(r"^about/?$", views.about, name="about"),
|
||||||
re_path(r"^privacy/?$", views.privacy, name="privacy"),
|
re_path(r"^privacy/?$", views.privacy, name="privacy"),
|
||||||
|
|
|
@ -4,6 +4,7 @@ from .admin.announcements import Announcements, Announcement
|
||||||
from .admin.announcements import EditAnnouncement, delete_announcement
|
from .admin.announcements import EditAnnouncement, delete_announcement
|
||||||
from .admin.automod import AutoMod, automod_delete, run_automod
|
from .admin.automod import AutoMod, automod_delete, run_automod
|
||||||
from .admin.automod import schedule_automod_task, unschedule_automod_task
|
from .admin.automod import schedule_automod_task, unschedule_automod_task
|
||||||
|
from .admin.celery_status import CeleryStatus
|
||||||
from .admin.dashboard import Dashboard
|
from .admin.dashboard import Dashboard
|
||||||
from .admin.federation import Federation, FederatedServer
|
from .admin.federation import Federation, FederatedServer
|
||||||
from .admin.federation import AddFederatedServer, ImportServerBlocklist
|
from .admin.federation import AddFederatedServer, ImportServerBlocklist
|
||||||
|
|
56
bookwyrm/views/admin/celery_status.py
Normal file
56
bookwyrm/views/admin/celery_status.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
""" celery status """
|
||||||
|
from django.contrib.auth.decorators import login_required, permission_required
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.views import View
|
||||||
|
import redis
|
||||||
|
|
||||||
|
from celerywyrm import settings
|
||||||
|
from bookwyrm.tasks import app as celery
|
||||||
|
|
||||||
|
r = redis.Redis(
|
||||||
|
host=settings.REDIS_BROKER_HOST,
|
||||||
|
port=settings.REDIS_BROKER_PORT,
|
||||||
|
password=settings.REDIS_BROKER_PASSWORD,
|
||||||
|
db=settings.REDIS_BROKER_DB_INDEX,
|
||||||
|
)
|
||||||
|
|
||||||
|
# pylint: disable= no-self-use
|
||||||
|
@method_decorator(login_required, name="dispatch")
|
||||||
|
@method_decorator(
|
||||||
|
permission_required("bookwyrm.edit_instance_settings", raise_exception=True),
|
||||||
|
name="dispatch",
|
||||||
|
)
|
||||||
|
class CeleryStatus(View):
|
||||||
|
"""Are your tasks running? Well you'd better go catch them"""
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
"""See workers and active tasks"""
|
||||||
|
errors = []
|
||||||
|
try:
|
||||||
|
inspect = celery.control.inspect()
|
||||||
|
stats = inspect.stats()
|
||||||
|
active_tasks = inspect.active()
|
||||||
|
# pylint: disable=broad-except
|
||||||
|
except Exception as err:
|
||||||
|
stats = active_tasks = None
|
||||||
|
errors.append(err)
|
||||||
|
|
||||||
|
try:
|
||||||
|
queues = {
|
||||||
|
"low_priority": r.llen("low_priority"),
|
||||||
|
"medium_priority": r.llen("medium_priority"),
|
||||||
|
"high_priority": r.llen("high_priority"),
|
||||||
|
}
|
||||||
|
# pylint: disable=broad-except
|
||||||
|
except Exception as err:
|
||||||
|
queues = None
|
||||||
|
errors.append(err)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"stats": stats,
|
||||||
|
"active_tasks": active_tasks,
|
||||||
|
"queues": queues,
|
||||||
|
"errors": errors,
|
||||||
|
}
|
||||||
|
return TemplateResponse(request, "settings/celery.html", data)
|
Loading…
Reference in a new issue