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('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:

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