1
0
Fork 0
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:
Lukas Winkler 2020-07-22 20:47:13 +02:00
parent 917055668d
commit d5c933608e
Signed by: lukas
GPG key ID: 54DE4D798D244853
10 changed files with 155 additions and 1 deletions

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

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

View file

@ -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:

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

View file

@ -0,0 +1,5 @@
class BaseCheck:
def run(self):
return []

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

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

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

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

View file

@ -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):