Merge pull request #2298 from bookwyrm-social/import-review-null-state

Adds celery status view
This commit is contained in:
Mouse Reeve 2022-09-15 11:20:16 -07:00 committed by GitHub
commit d5c095ce6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 247 additions and 0 deletions

View 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 %}

View file

@ -74,6 +74,15 @@
</li>
</ul>
{% 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 %}
<h2 class="menu-label">{% trans "Instance Settings" %}</h2>
<ul class="menu-list">

View 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:])

View 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)

View file

@ -291,6 +291,9 @@ urlpatterns = [
views.Report.as_view(),
name="report-link",
),
re_path(
r"^settings/celery/?$", views.CeleryStatus.as_view(), name="settings-celery"
),
# landing pages
re_path(r"^about/?$", views.about, name="about"),
re_path(r"^privacy/?$", views.privacy, name="privacy"),

View file

@ -4,6 +4,7 @@ from .admin.announcements import Announcements, Announcement
from .admin.announcements import EditAnnouncement, delete_announcement
from .admin.automod import AutoMod, automod_delete, run_automod
from .admin.automod import schedule_automod_task, unschedule_automod_task
from .admin.celery_status import CeleryStatus
from .admin.dashboard import Dashboard
from .admin.federation import Federation, FederatedServer
from .admin.federation import AddFederatedServer, ImportServerBlocklist

View 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)