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('tags', views.TagListView.as_view(), name='tags'),
|
||||||
path('tag', RedirectView.as_view(pattern_name="tags")),
|
path('tag', RedirectView.as_view(pattern_name="tags")),
|
||||||
path('tag/<str:slug>', views.TagAcroView.as_view(), name='tag'),
|
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:
|
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.models import Acronym, Tag, AcroOfTheDay, WikipediaLink, PaperReference, Weblink
|
||||||
from acros.serializers import AcronymSerializer, AcronymListSerializer, TagSerializer
|
from acros.serializers import AcronymSerializer, AcronymListSerializer, TagSerializer
|
||||||
from acros.utils.assets import get_css
|
from acros.utils.assets import get_css
|
||||||
|
from acros.utils.checks import registry
|
||||||
|
|
||||||
handler404 = 'acros.views.PageNotFoundView'
|
handler404 = 'acros.views.PageNotFoundView'
|
||||||
|
|
||||||
|
@ -122,6 +123,16 @@ class TagAcroView(generic.ListView):
|
||||||
return Acronym.objects.filter(tags__slug__exact=self.kwargs['slug'])
|
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 ####
|
#### API Views ####
|
||||||
|
|
||||||
class AcronymViewSet(viewsets.ReadOnlyModelViewSet):
|
class AcronymViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
|
|
Loading…
Reference in a new issue