diff --git a/plinth/modules/diagnostics/check.py b/plinth/modules/diagnostics/check.py index aa3babc50..0c257d5f0 100644 --- a/plinth/modules/diagnostics/check.py +++ b/plinth/modules/diagnostics/check.py @@ -4,6 +4,10 @@ from dataclasses import dataclass, field from enum import StrEnum +from django.utils.translation import gettext + +from plinth.utils import SafeFormatter + class Result(StrEnum): """The result of a diagnostic check.""" @@ -24,3 +28,19 @@ class DiagnosticCheck: description: str result: Result = Result.NOT_DONE parameters: dict = field(default_factory=dict) + + +def translate(check: DiagnosticCheck) -> DiagnosticCheck: + """Translate and format description using parameters.""" + description = gettext(check.description) + if check.parameters: + description = SafeFormatter().vformat(description, [], + check.parameters) + + return DiagnosticCheck(check.check_id, description, check.result, + check.parameters) + + +def translate_checks(checks: list[DiagnosticCheck]) -> list[DiagnosticCheck]: + """Translate and format diagnostic checks.""" + return [translate(check) for check in checks] diff --git a/plinth/modules/diagnostics/tests/test_check.py b/plinth/modules/diagnostics/tests/test_check.py index cf1521295..a41148606 100644 --- a/plinth/modules/diagnostics/tests/test_check.py +++ b/plinth/modules/diagnostics/tests/test_check.py @@ -3,7 +3,7 @@ import pytest -from plinth.modules.diagnostics.check import DiagnosticCheck, Result +from plinth.modules.diagnostics.check import DiagnosticCheck, Result, translate def test_result(): @@ -35,3 +35,25 @@ def test_diagnostic_check(): check = DiagnosticCheck('some-check-id', 'sample check', Result.PASSED, {'key': 'value'}) assert check.parameters['key'] == 'value' + + +def test_translate(): + """Test formatting the translated description.""" + check = DiagnosticCheck('some-check-id', 'sample check', Result.PASSED) + translated = translate(check) + assert translated.check_id == 'some-check-id' + assert translated.description == 'sample check' + assert translated.result == Result.PASSED + assert not translated.parameters + + check = DiagnosticCheck('some-check-id', 'sample check {key}', + Result.FAILED, {'key': 'value'}) + translated = translate(check) + assert translated.description == 'sample check value' + assert translated.result == Result.FAILED + assert translated.parameters == {'key': 'value'} + + check = DiagnosticCheck('some-check-id', 'sample check {missing}', + Result.PASSED, {'key': 'value'}) + translated = translate(check) + assert translated.description == 'sample check ?missing?' diff --git a/plinth/notification.py b/plinth/notification.py index fb63ab89c..07aee714c 100644 --- a/plinth/notification.py +++ b/plinth/notification.py @@ -3,7 +3,6 @@ import copy import logging -import string from django.core.exceptions import ValidationError from django.db.models import Q @@ -12,6 +11,7 @@ from django.template.response import SimpleTemplateResponse from django.utils.translation import gettext from plinth import cfg +from plinth.utils import SafeFormatter from . import db, models @@ -370,14 +370,3 @@ class Notification(models.StoredNotification): notes.append(note_context) return {'notifications': notes, 'max_severity': max_severity} - - -class SafeFormatter(string.Formatter): - """A string.format() handler to deal with missing arguments.""" - - def get_value(self, key, args, kwargs): - """Retrieve a given field value.""" - try: - return super().get_value(key, args, kwargs) - except (IndexError, KeyError): - return f'?{key}?' diff --git a/plinth/tests/test_notification.py b/plinth/tests/test_notification.py index 822ec25a3..6af90bc7f 100644 --- a/plinth/tests/test_notification.py +++ b/plinth/tests/test_notification.py @@ -10,7 +10,7 @@ import pytest from django.contrib.auth.models import Group, User from django.core.exceptions import ValidationError -from plinth.notification import Notification, SafeFormatter +from plinth.notification import Notification pytestmark = pytest.mark.django_db @@ -413,15 +413,3 @@ def test_display_context_body_template(note, user, load_cfg, rf): context_note = context['notifications'][0] assert context_note['body'].content == \ b'Test notification body /plinth/help/about/\n' - - -@pytest.mark.parametrize('input_, output', ( - (('', [], {}), ''), - (('{} {}', [10, 20], {}), '10 20'), - (('{1} {0} {key1}', [10, 20], dict(key1='value1')), '20 10 value1'), - (('{2} {1} {key1}', [10, 20], {}), '?2? 20 ?key1?'), -)) -def test_safe_string_formatter(input_, output): - """Test the safe string formatter.""" - formatter = SafeFormatter() - assert output == formatter.vformat(*input_) diff --git a/plinth/tests/test_utils.py b/plinth/tests/test_utils.py index c9e220433..0ad26c3b7 100644 --- a/plinth/tests/test_utils.py +++ b/plinth/tests/test_utils.py @@ -11,7 +11,8 @@ import ruamel.yaml from django.test.client import RequestFactory from ruamel.yaml.compat import StringIO -from plinth.utils import YAMLFile, is_user_admin, is_valid_user_name +from plinth.utils import (SafeFormatter, YAMLFile, is_user_admin, + is_valid_user_name) def test_is_valid_user_name(): @@ -139,3 +140,17 @@ class TestYAMLFileUtil: raise ValueError('Test') assert open(test_file.name, 'r', encoding='utf-8').read() == '' + + +@pytest.mark.parametrize('input_, output', ( + (('', [], {}), ''), + (('{} {}', [10, 20], {}), '10 20'), + (('{1} {0} {key1}', [10, 20], { + 'key1': 'value1' + }), '20 10 value1'), + (('{2} {1} {key1}', [10, 20], {}), '?2? 20 ?key1?'), +)) +def test_safe_string_formatter(input_, output): + """Test the safe string formatter.""" + formatter = SafeFormatter() + assert output == formatter.vformat(*input_) diff --git a/plinth/utils.py b/plinth/utils.py index d7804766e..239da8b37 100644 --- a/plinth/utils.py +++ b/plinth/utils.py @@ -174,3 +174,14 @@ def is_authenticated_user(username, password): import pam # Minimize dependencies for running tests pam_authenticator = pam.pam() return bool(pam_authenticator.authenticate(username, password)) + + +class SafeFormatter(string.Formatter): + """A string.format() handler to deal with missing arguments.""" + + def get_value(self, key, args, kwargs): + """Retrieve a given field value.""" + try: + return super().get_value(key, args, kwargs) + except (IndexError, KeyError): + return f'?{key}?'