Skip to content

Commit

Permalink
Job report using viewsets with Wagtail 6.1 plus (#14)
Browse files Browse the repository at this point in the history
* Require Wagtail 6.1+
* Add a job viewset
   * Add an inspect view
* Remove the old report view
  • Loading branch information
zerolab authored Jul 31, 2024
1 parent 4e5df47 commit 46c788c
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 53 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ integrates with the Smartling translation platform.

- Python 3.8+
- Django 4.2+
- Wagtail 5.2+
- Wagtail 6.1+

## Installation

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ dynamic = [
]
dependencies = [
"Django>=4.2",
"Wagtail>=5.2",
"Wagtail>=6.1",
"djangorestframework>=3",
"requests",
"wagtail-localize>=1.0.0",
Expand Down
1 change: 0 additions & 1 deletion src/wagtail_localize_smartling/admin_urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@
app_name = "wagtail_localize_smartling"
urlpatterns = [
path("status/", views.SmartlingStatusView.as_view(), name="status"),
path("jobs/", views.JobReportView.as_view(), name="jobs_report"),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<td {% if column.classname %}class="{{ column.classname }}"{% endif %}>
{% if value %}
<ul>
{% for item in value %}
<li>{{ item.target_locale.get_display_name }}</li>
{% endfor %}
</ul>
{% endif %}
</td>
39 changes: 1 addition & 38 deletions src/wagtail_localize_smartling/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,13 @@

from typing import Any, Dict

import django_filters

from django.contrib.auth import get_user_model
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views.generic import TemplateView
from wagtail.admin.filters import WagtailFilterSet
from wagtail.admin.views.generic import WagtailAdminTemplateMixin
from wagtail.admin.views.reports import ReportView
from wagtail.models import Locale

from .api.types import JobStatus
from .models import Job, Project
from .models import Project
from .utils import (
format_smartling_project_url,
get_wagtail_source_locale,
Expand Down Expand Up @@ -80,34 +74,3 @@ def get_context_data(self, **kwargs):
context["suggested_source_locale_exists"] = False

return super().get_context_data(**context)


def get_users_for_filter(user):
User = get_user_model()
return User.objects.filter(
pk__in=Job.objects.values_list("user", flat=True)
).order_by(User.USERNAME_FIELD) # pyright: ignore[reportAttributeAccessIssue]


class JobReportFilterSet(WagtailFilterSet):
status = django_filters.ChoiceFilter(choices=JobStatus.choices)
user = django_filters.ModelChoiceFilter(
label=_("User"),
field_name="user",
queryset=lambda request: get_users_for_filter(request.user),
)

class Meta:
model = Job
fields = ["status"]


class JobReportView(ReportView):
title = _("Smartling jobs")
template_name = "wagtail_localize_smartling/admin/job_report.html"
filterset_class = JobReportFilterSet

def get_queryset(self):
return Job.objects.select_related("translation_source").prefetch_related(
"translations__target_locale"
)
206 changes: 206 additions & 0 deletions src/wagtail_localize_smartling/viewsets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import django_filters

from django.contrib.auth import get_user_model
from django.urls import reverse
from django.utils.functional import cached_property
from django.utils.html import format_html, format_html_join
from django.utils.translation import gettext_lazy as _
from wagtail.admin.filters import WagtailFilterSet
from wagtail.admin.ui.tables import Column, DateColumn, TitleColumn, UserColumn
from wagtail.admin.views import generic
from wagtail.admin.viewsets.model import ModelViewSet
from wagtail.permission_policies import ModelPermissionPolicy

from .api.types import JobStatus
from .models import Job
from .templatetags.wagtail_localize_smartling_admin_tags import smartling_job_url


def get_users_for_filter(user):
User = get_user_model()
return User.objects.filter(
pk__in=Job.objects.values_list("user", flat=True)
).order_by(User.USERNAME_FIELD) # pyright: ignore[reportAttributeAccessIssue]


class JobReportFilterSet(WagtailFilterSet):
status = django_filters.ChoiceFilter(choices=JobStatus.choices)
user = django_filters.ModelChoiceFilter(
label=_("User"),
field_name="user",
queryset=lambda request: get_users_for_filter(request.user),
)

class Meta:
model = Job
fields = ["status"]


class JobPermissionPolicy(ModelPermissionPolicy):
def user_has_permission(self, user, action):
if action in ["add", "change"]:
return False

return super().user_has_permission(user, action)


class SourceInstanceColumn(TitleColumn):
def get_value(self, instance):
return instance.translation_source.get_source_instance()

def get_link_attrs(self, instance, parent_context):
return {"title": _(f"Job {instance.name}")}


class TargetLocalesColumn(Column):
"""Outputs a list of job target locales"""

cell_template_name = (
"wagtail_localize_smartling/admin/tables/target_locales_cell.html"
)

def get_value(self, instance):
return getattr(instance, self.accessor).all()


class JobIndexView(generic.IndexView):
page_title = _("Smartling jobs")
breadcrumbs = []

def get_breadcrumbs_items(self):
return self.breadcrumbs_items + [
{"url": "", "label": self.page_title},
]

@cached_property
def columns(self):
columns = [
SourceInstanceColumn(
"source_instance",
label=_("Source"),
get_url=self.get_inspect_url,
),
TargetLocalesColumn(
"translations",
label=_("Target locales"),
),
DateColumn(
"due_date",
label=_("Due date"),
width="12%",
),
Column("status", label=_("Status"), accessor="get_status_display"),
DateColumn(
"first_synced_at",
label=_("First synced at"),
width="12%",
),
DateColumn(
"last_synced_at",
label=_("Last synced at"),
width="12%",
),
UserColumn("user"),
]
return columns

def get_queryset(self):
queryset = super().get_queryset()
return queryset.select_related("translation_source").prefetch_related(
"translations__target_locale"
)


class JobInspectView(generic.InspectView):
def get_fields(self):
return [
"translation_source",
"translations",
"user",
"reference_number",
"status",
"due_date",
"first_synced_at",
"last_synced_at",
"description",
"translations_imported_at",
]

def get_breadcrumbs_items(self):
items = []
if self.index_url_name:
items.append(
{
"url": reverse(self.index_url_name),
"label": _("Smartling jobs"),
}
)
items.append(
{
"url": "",
"label": _("Inspect"),
"sublabel": self.object.reference_number,
}
)
return self.breadcrumbs_items + items

def get_field_display_value(self, field_name, field):
# allow customising field display in the inspect class
value_func = getattr(self, f"get_{field_name}_display_value", None)
if value_func is not None:
return value_func()

return super().get_field_display_value(field_name, field)

def get_reference_number_display_value(self):
if smartling_url := smartling_job_url(self.object):
return format_html(
'<a href="{}" title="Reference {}" target="_blank">{}</a>',
smartling_url,
self.object.reference_number,
_("View job in Smartling"),
)
return self.object.reference_number

def get_translation_source_display_value(self):
return format_html(
'<a href="{}">{}</a>',
self.object.translation_source.get_source_instance_edit_url(),
self.object.translation_source.get_source_instance(),
)

def get_translations_display_value(self):
content_html = format_html_join(
"\n",
'<li><a href="{}">{}</a> - {}</li>',
(
(
translation.get_target_instance_edit_url(),
str(translation.get_target_instance()),
translation.target_locale.get_display_name(),
)
for translation in self.object.translations.all()
),
)

return format_html("<ul>{}</ul>", content_html)


class JobViewSet(ModelViewSet):
model = Job
index_view_class = JobIndexView
inspect_view_class = JobInspectView
filterset_class = JobReportFilterSet # pyright: ignore[reportAssignmentType]
form_fields = ["status"]
icon = "wagtail-localize-language"
add_to_admin_menu = True
menu_label = _("Smartling jobs") # pyright: ignore[reportAssignmentType]
copy_view_enabled = False
inspect_view_enabled = True

@property
def permission_policy(self):
return JobPermissionPolicy(self.model)


smartling_job_viewset = JobViewSet("smartling-jobs")
11 changes: 4 additions & 7 deletions src/wagtail_localize_smartling/wagtail_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from wagtail.admin.menu import MenuItem

from . import admin_urls
from .viewsets import smartling_job_viewset


@hooks.register("register_admin_urls") # pyright: ignore[reportOptionalCall]
Expand All @@ -20,10 +21,6 @@ def register_smartling_settings_menu_item():
)


@hooks.register("register_reports_menu_item") # pyright: ignore[reportOptionalCall]
def register_report_menu_item():
return MenuItem(
_("Smartling jobs"),
reverse("wagtail_localize_smartling:jobs_report"),
icon_name="wagtail-localize-language",
)
@hooks.register("register_admin_viewset") # pyright: ignore[reportOptionalCall]
def register_viewset():
return smartling_job_viewset
9 changes: 4 additions & 5 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ min_version = 4.0
requires =
tox-gh-actions>=3
envlist =
python{3.8,3.9,3.10,3.11,3.12}-django4.2-wagtail{5.2,6.0,6.1}-{sqlite,postgres}
python{3.10,3.11,3.12}-django5.0-wagtail{5.2,6.0,6.1}-{sqlite,postgres}
python{3.8,3.9,3.10,3.11,3.12}-django4.2-wagtail{6.1}-{postgres}
python{3.10,3.11,3.12}-django5.0-wagtail{6.1}-{sqlite,postgres}
pyright
skip_missing_interpreters = true

Expand Down Expand Up @@ -37,10 +37,9 @@ deps =
django4.2: Django>=4.2,<4.3
django5.0: Django>=5.0,<5.1

wagtail5.2: wagtail>=5.2,<5.3
wagtail6.0: wagtail>=5.2,<5.3
wagtail6.1: wagtail>=6.1,<6.2

postgres: psycopg2>=2.6
postgres: psycopg2>=2.9

setenv =
postgres: DATABASE_URL={env:DATABASE_URL:postgres:///wagtail_localize_smartling}
Expand Down

0 comments on commit 46c788c

Please sign in to comment.