mirror of
https://github.com/Findus23/acronomy.git
synced 2024-09-19 15:33:45 +02:00
add simple framework for runing data consistency checks
This commit is contained in:
parent
917055668d
commit
d5c933608e
10 changed files with 155 additions and 1 deletions
12
acros/management/commands/datacheck.py
Normal file
12
acros/management/commands/datacheck.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from django.core.management.base import BaseCommand
|
||||
|
||||
from acros.utils.checks import registry
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'check integrity of data'
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
errors = registry.run_checks()
|
||||
for error in errors:
|
||||
print(error)
|
19
acros/templates/acros/datacheck.html
Normal file
19
acros/templates/acros/datacheck.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
{% block heading %}
|
||||
<h1 class="acronym">Data Checks</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% for error in errors %}
|
||||
<div class="alert alert-{{ error.level }}" role="alert">
|
||||
<strong>{{ error.obj }}</strong>:
|
||||
{{ error.msg }}
|
||||
<br>
|
||||
<a href="{{ error.edit_url }}" class="alert-link">Edit</a>
|
||||
<a href="{{ error.admin_edit_url }}" class="alert-link">Admin-Edit</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% endblock %}
|
|
@ -34,7 +34,8 @@ urlpatterns = [
|
|||
path('tags', views.TagListView.as_view(), name='tags'),
|
||||
path('tag', RedirectView.as_view(pattern_name="tags")),
|
||||
path('tag/<str:slug>', views.TagAcroView.as_view(), name='tag'),
|
||||
path('integrations', views.IntegrationsView.as_view(), name="integrations")
|
||||
path('integrations', views.IntegrationsView.as_view(), name="integrations"),
|
||||
path('datachecks', views.DataCheckView.as_view(), name="datachecks")
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
|
|
5
acros/utils/checks/__init__.py
Normal file
5
acros/utils/checks/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from .basecheck import BaseCheck
|
||||
from .check_registry import CheckRegistry, registry
|
||||
from .messages import DEBUG, INFO, WARNING, ERROR, CRITICAL, CheckMessage, CheckWarning, CheckInfo
|
||||
from .checks.acronym import *
|
||||
from .checks.image import *
|
5
acros/utils/checks/basecheck.py
Normal file
5
acros/utils/checks/basecheck.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
class BaseCheck:
|
||||
def run(self):
|
||||
return []
|
||||
|
||||
|
22
acros/utils/checks/check_registry.py
Normal file
22
acros/utils/checks/check_registry.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
from typing import Set
|
||||
|
||||
from . import BaseCheck
|
||||
|
||||
|
||||
class CheckRegistry:
|
||||
def __init__(self):
|
||||
self.checks: Set[BaseCheck] = set()
|
||||
|
||||
def register(self, check: BaseCheck):
|
||||
self.checks.add(check)
|
||||
|
||||
def run_checks(self):
|
||||
errors = []
|
||||
for Check in self.checks:
|
||||
inst = Check()
|
||||
new_errors = list(inst.run())
|
||||
errors.extend(new_errors)
|
||||
return errors
|
||||
|
||||
|
||||
registry = CheckRegistry()
|
23
acros/utils/checks/checks/acronym.py
Normal file
23
acros/utils/checks/checks/acronym.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
from acros.models import Acronym
|
||||
from acros.utils.checks import BaseCheck, CheckWarning, registry, CheckInfo
|
||||
|
||||
|
||||
class LetterCheck(BaseCheck):
|
||||
def run(self):
|
||||
for acronym in Acronym.objects.all():
|
||||
if acronym.acro_letters is None:
|
||||
yield CheckInfo(
|
||||
"missing acronym letters",
|
||||
obj=acronym
|
||||
)
|
||||
continue
|
||||
let_len = len(acronym.acro_letters)
|
||||
acr_len = len(acronym.name)
|
||||
if let_len != acr_len:
|
||||
yield CheckWarning(
|
||||
f"number of letters selected ({let_len}) not equal to letters in acronym ({acr_len})",
|
||||
obj=acronym
|
||||
)
|
||||
|
||||
|
||||
registry.register(LetterCheck)
|
13
acros/utils/checks/checks/image.py
Normal file
13
acros/utils/checks/checks/image.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
from acros.models import WikipediaImage
|
||||
from acros.utils.checks import BaseCheck, CheckWarning, registry
|
||||
|
||||
|
||||
class AuthorAttributionCheck(BaseCheck):
|
||||
def run(self):
|
||||
for image in WikipediaImage.objects.all():
|
||||
if image.attribution_required and not image.artist:
|
||||
yield CheckWarning("Image needs attribution, but is missing an artist", obj=image)
|
||||
|
||||
|
||||
registry.register(AuthorAttributionCheck)
|
||||
|
43
acros/utils/checks/messages.py
Normal file
43
acros/utils/checks/messages.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
from django.urls import reverse
|
||||
|
||||
from acros.models import Acronym
|
||||
|
||||
DEBUG = 10
|
||||
INFO = "info"
|
||||
WARNING = "warning"
|
||||
ERROR = 40
|
||||
CRITICAL = 50
|
||||
|
||||
|
||||
class CheckMessage:
|
||||
def __init__(self, level: str, msg: str, obj=None):
|
||||
self.level = level
|
||||
self.msg = msg
|
||||
self.obj = obj
|
||||
|
||||
def __str__(self):
|
||||
obj = str(self.obj)
|
||||
print(self.obj._meta.label)
|
||||
return f"{obj}: {self.msg}"
|
||||
|
||||
@property
|
||||
def edit_url(self):
|
||||
if isinstance(self.obj, Acronym):
|
||||
return reverse("edit", args=[self.obj.slug])
|
||||
return None
|
||||
|
||||
@property
|
||||
def admin_edit_url(self):
|
||||
if isinstance(self.obj, Acronym):
|
||||
return reverse("admin:acros_acronym_change", args=[self.obj.id])
|
||||
return None
|
||||
|
||||
|
||||
class CheckWarning(CheckMessage):
|
||||
def __init__(self, msg: str, obj=None):
|
||||
super(CheckWarning, self).__init__(WARNING, msg, obj)
|
||||
|
||||
|
||||
class CheckInfo(CheckMessage):
|
||||
def __init__(self, msg: str, obj=None):
|
||||
super(CheckInfo, self).__init__(INFO, msg, obj)
|
|
@ -11,6 +11,7 @@ from acros.forms import EditForm, AddForm, WikipediaForm, PaperForm, WeblinkForm
|
|||
from acros.models import Acronym, Tag, AcroOfTheDay, WikipediaLink, PaperReference, Weblink
|
||||
from acros.serializers import AcronymSerializer, AcronymListSerializer, TagSerializer
|
||||
from acros.utils.assets import get_css
|
||||
from acros.utils.checks import registry
|
||||
|
||||
handler404 = 'acros.views.PageNotFoundView'
|
||||
|
||||
|
@ -122,6 +123,16 @@ class TagAcroView(generic.ListView):
|
|||
return Acronym.objects.filter(tags__slug__exact=self.kwargs['slug'])
|
||||
|
||||
|
||||
class DataCheckView(generic.TemplateView, LoginRequiredMixin):
|
||||
template_name = "acros/datacheck.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
data = super().get_context_data(**kwargs)
|
||||
errors = registry.run_checks()
|
||||
data['errors'] = errors
|
||||
return data
|
||||
|
||||
|
||||
#### API Views ####
|
||||
|
||||
class AcronymViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
|
|
Loading…
Reference in a new issue