diagnostics: Translate descriptions only in view

Tests:

- Enable all apps, and run diagnostics. Diagnostic descriptions are formatted
  as expected.

- Change the language to Spanish, and view the diagnostic results. Diagnostic
  descriptions are translated as expected.

Signed-off-by: James Valleroy <jvalleroy@mailbox.org>
Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
James Valleroy 2023-11-17 16:45:11 -05:00 committed by Sunil Mohan Adapa
parent a5820bc36d
commit 3fcd6b9e58
No known key found for this signature in database
GPG Key ID: 43EA1CFF0AA7C5F2
18 changed files with 101 additions and 89 deletions

View File

@ -3,8 +3,7 @@
import logging
import pathlib
from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext_noop
from plinth.privileged import config as privileged
@ -117,8 +116,8 @@ class DropinConfigs(app_module.FollowerComponent):
check_id = f'dropin-config-{etc_path}'
result_string = Result.PASSED if result else Result.FAILED
template = _('Static configuration {etc_path} is setup properly')
description = format_lazy(template, etc_path=str(etc_path))
description = gettext_noop(
'Static configuration {etc_path} is setup properly')
parameters = {'etc_path': str(etc_path)}
results.append(
DiagnosticCheck(check_id, description, result_string,

View File

@ -5,9 +5,7 @@ import socket
import subprocess
import psutil
from django.utils.text import format_lazy
from django.utils.translation import gettext as _
from django.utils.translation import gettext_lazy
from django.utils.translation import gettext_noop
from plinth import action_utils, app
@ -104,8 +102,7 @@ class Daemon(app.LeaderComponent):
check_id = f'daemon-running-{self.unit}'
result = Result.PASSED if self.is_running() else Result.FAILED
template = gettext_lazy('Service {service_name} is running')
description = format_lazy(template, service_name=self.unit)
description = gettext_noop('Service {service_name} is running')
parameters = {'service_name': self.unit}
return DiagnosticCheck(check_id, description, result, parameters)
@ -163,14 +160,11 @@ def diagnose_port_listening(port, kind='tcp', listen_address=None):
if listen_address:
parameters['listen_address'] = listen_address
check_id = f'daemon-listening-address-{kind}-{port}-{listen_address}'
template = gettext_lazy(
description = gettext_noop(
'Listening on {kind} port {listen_address}:{port}')
description = format_lazy(template, kind=kind,
listen_address=listen_address, port=port)
else:
check_id = f'daemon-listening-{kind}-{port}'
template = gettext_lazy('Listening on {kind} port {port}')
description = format_lazy(template, kind=kind, port=port)
description = gettext_noop('Listening on {kind} port {port}')
return DiagnosticCheck(check_id, description,
Result.PASSED if result else Result.FAILED,
@ -239,11 +233,11 @@ def diagnose_netcat(host, port, input='', negate=False):
result = Result.FAILED
check_id = f'daemon-netcat-{host}-{port}'
description = _('Connect to {host}:{port}')
description = gettext_noop('Connect to {host}:{port}')
parameters = {'host': host, 'port': port, 'negate': negate}
if negate:
check_id = f'daemon-netcat-negate-{host}-{port}'
description = _('Cannot connect to {host}:{port}')
description = gettext_noop('Cannot connect to {host}:{port}')
return DiagnosticCheck(check_id, description.format(host=host, port=port),
result, parameters)

View File

@ -4,8 +4,7 @@
import re
import subprocess
from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy
from django.utils.translation import gettext_noop
from plinth import action_utils, app
from plinth.modules.diagnostics.check import DiagnosticCheck, Result
@ -149,16 +148,13 @@ def diagnose_url(url, kind=None, env=None, check_certificate=True,
except FileNotFoundError:
result = Result.ERROR
parameters = {'url': url}
parameters = {'url': url, 'kind': kind}
if kind:
parameters['kind'] = kind
check_id = f'apache-url-kind-{url}-{kind}'
template = gettext_lazy('Access URL {url} on tcp{kind}')
description = format_lazy(template, url=url, kind=kind)
description = gettext_noop('Access URL {url} on tcp{kind}')
else:
check_id = f'apache-url-{url}'
template = gettext_lazy('Access URL {url}')
description = format_lazy(template, url=url)
description = gettext_noop('Access URL {url}')
return DiagnosticCheck(check_id, description, result, parameters)

View File

@ -12,7 +12,8 @@ from plinth import app
from plinth.modules.apache.components import (Uwsgi, Webserver, check_url,
diagnose_url,
diagnose_url_on_all)
from plinth.modules.diagnostics.check import DiagnosticCheck, Result
from plinth.modules.diagnostics.check import (DiagnosticCheck, Result,
translate, translate_checks)
def test_webserver_init():
@ -248,13 +249,13 @@ def test_diagnose_url(get_addresses, check):
}
parameters = {key: args[key] for key in ['url', 'kind']}
check.return_value = True
result = diagnose_url(**args)
result = translate(diagnose_url(**args))
assert result == DiagnosticCheck(
'apache-url-kind-https://localhost/test-4',
'Access URL https://localhost/test on tcp4', Result.PASSED, parameters)
check.return_value = False
result = diagnose_url(**args)
result = translate(diagnose_url(**args))
assert result == DiagnosticCheck(
'apache-url-kind-https://localhost/test-4',
'Access URL https://localhost/test on tcp4', Result.FAILED, parameters)
@ -283,7 +284,7 @@ def test_diagnose_url(get_addresses, check):
'kind': '6'
},
]
results = diagnose_url_on_all(**args)
results = translate_checks(diagnose_url_on_all(**args))
assert results == [
DiagnosticCheck('apache-url-kind-https://test-host-1/test-4',
'Access URL https://test-host-1/test on tcp4',

View File

@ -6,6 +6,7 @@ FreedomBox app to configure system date and time.
import subprocess
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext_noop
from plinth import app as app_module
from plinth import menu
@ -118,4 +119,5 @@ def _diagnose_time_synchronized():
pass
return DiagnosticCheck('datetime-ntp-sync',
_('Time synchronized to NTP server'), result)
gettext_noop('Time synchronized to NTP server'),
result)

View File

@ -3,6 +3,7 @@
FreedomBox app for running diagnostics.
"""
from copy import deepcopy
import logging
from django.http import Http404, HttpResponseRedirect
@ -17,7 +18,7 @@ from plinth.app import App
from plinth.modules import diagnostics
from plinth.views import AppView
from .check import Result
from .check import Result, translate_checks
logger = logging.getLogger(__name__)
@ -62,7 +63,14 @@ class DiagnosticsFullView(TemplateView):
is_task_running = False
with diagnostics.results_lock:
results = diagnostics.current_results
results = deepcopy(diagnostics.current_results)
# Translate and format diagnostic check descriptions for each app
for app_id in results['results']:
if 'diagnosis' in results['results'][app_id]:
diagnosis = results['results'][app_id]['diagnosis']
results['results'][app_id]['diagnosis'] = translate_checks(
diagnosis)
context = super().get_context_data(**kwargs)
context['is_task_running'] = is_task_running
@ -89,6 +97,8 @@ def diagnose_app(request, app_id):
exception)
diagnosis_exception = str(exception)
# Translate and format diagnostic check descriptions
diagnosis = translate_checks(diagnosis)
show_rerun_setup = False
for check in diagnosis:
if check.result in [Result.FAILED, Result.WARNING]:

View File

@ -4,8 +4,8 @@
import contextlib
import logging
from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext_noop
from plinth import app as app_module
from plinth import cfg, menu
@ -268,7 +268,7 @@ def remove_passthrough(ipv, *args):
def _diagnose_default_zone(config):
"""Diagnose whether the default zone is external."""
check_id = 'firewall-default-zone'
description = gettext('Default zone is external')
description = gettext_noop('Default zone is external')
result = Result.PASSED if config[
'default_zone'] == 'external' else Result.FAILED
return DiagnosticCheck(check_id, description, result)
@ -277,7 +277,7 @@ def _diagnose_default_zone(config):
def _diagnose_firewall_backend(config):
"""Diagnose whether the firewall backend is nftables."""
check_id = 'firewall-backend'
description = gettext('Firewall backend is nftables')
description = gettext_noop('Firewall backend is nftables')
result = Result.PASSED if config['backend'] == 'nftables' \
else Result.FAILED
return DiagnosticCheck(check_id, description, result)
@ -290,7 +290,7 @@ def _diagnose_direct_passthroughs(config):
which are the number that are added by firewall's setup.
"""
check_id = 'firewall-direct-passthroughs'
description = gettext('Direct passthrough rules exist')
description = gettext_noop('Direct passthrough rules exist')
result = Result.PASSED if len(
config['passthroughs']) >= 12 else Result.FAILED
return DiagnosticCheck(check_id, description, result)

View File

@ -7,8 +7,7 @@ import logging
import re
from typing import ClassVar
from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext_noop
from plinth import app
from plinth.modules import firewall
@ -133,9 +132,8 @@ class Firewall(app.FollowerComponent):
# Internal zone
check_id = f'firewall-port-internal-{port}'
result = Result.PASSED if port in internal_ports else Result.FAILED
template = _(
description = gettext_noop(
'Port {name} ({details}) available for internal networks')
description = format_lazy(template, name=port, details=details)
parameters = {'name': port, 'details': details}
results.append(
DiagnosticCheck(check_id, description, result, parameters))
@ -145,17 +143,15 @@ class Firewall(app.FollowerComponent):
check_id = f'firewall-port-external-available-{port}'
result = Result.PASSED \
if port in external_ports else Result.FAILED
template = _(
description = gettext_noop(
'Port {name} ({details}) available for external networks')
description = format_lazy(template, name=port, details=details)
else:
check_id = f'firewall-port-external-unavailable-{port}'
result = Result.PASSED \
if port not in external_ports else Result.FAILED
template = _(
description = gettext_noop(
'Port {name} ({details}) unavailable for external networks'
)
description = format_lazy(template, name=port, details=details)
parameters = {'name': port, 'details': details}
results.append(

View File

@ -8,7 +8,8 @@ from unittest.mock import call, patch
import pytest
from plinth.app import App
from plinth.modules.diagnostics.check import DiagnosticCheck, Result
from plinth.modules.diagnostics.check import (DiagnosticCheck, Result,
translate_checks)
from plinth.modules.firewall.components import (Firewall,
FirewallLocalProtection)
@ -153,7 +154,7 @@ def test_diagnose(get_enabled_services, get_port_details):
get_port_details.side_effect = get_port_details_side_effect
firewall = Firewall('test-firewall-1', ports=['test-port1', 'test-port2'],
is_external=False)
results = firewall.diagnose()
results = translate_checks(firewall.diagnose())
assert results == [
DiagnosticCheck(
'firewall-port-internal-test-port1',
@ -187,7 +188,7 @@ def test_diagnose(get_enabled_services, get_port_details):
firewall = Firewall('test-firewall-1', ports=['test-port3', 'test-port4'],
is_external=True)
results = firewall.diagnose()
results = translate_checks(firewall.diagnose())
assert results == [
DiagnosticCheck(
'firewall-port-internal-test-port3',

View File

@ -6,6 +6,7 @@ import logging
import pathlib
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext_noop
from plinth import app as app_module
from plinth import cfg, menu
@ -13,6 +14,7 @@ from plinth.config import DropinConfigs
from plinth.modules import names
from plinth.modules.apache.components import diagnose_url
from plinth.modules.backups.components import BackupRestore
from plinth.modules.diagnostics.check import DiagnosticCheck, Result
from plinth.modules.names.components import DomainType
from plinth.package import Packages
from plinth.signals import domain_added, domain_removed, post_app_loading
@ -97,7 +99,10 @@ class LetsEncryptApp(app_module.App):
if not results:
results.append(
(_('Cannot test: No domains are configured.'), 'warning'))
DiagnosticCheck(
'letsencrypt-cannot-test',
gettext_noop('Cannot test: No domains are configured.'),
Result.WARNING))
return results

View File

@ -5,6 +5,7 @@ FreedomBox app to configure Privoxy.
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext_noop
from plinth import action_utils
from plinth import app as app_module
@ -112,9 +113,9 @@ def diagnose_url_with_proxy():
result = diagnose_url(url, kind=address['kind'], env=env)
result.check_id = f'privoxy-url-proxy-kind-{url}-{address["kind"]}'
result.description = _(
'Access {url} with proxy {proxy} on tcp{kind}') \
.format(url=url, proxy=proxy, kind=address['kind'])
result.description = gettext_noop(
'Access {url} with proxy {proxy} on tcp{kind}')
result.parameters['proxy'] = proxy
results.append(result)
return results

View File

@ -5,6 +5,7 @@ import json
import logging
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext_noop
from plinth import action_utils
from plinth import app as app_module
@ -134,7 +135,7 @@ class TorApp(app_module.App):
if status['relay_enabled']:
results.append(
DiagnosticCheck(
'tor-port-relay', _('Tor relay port available'),
'tor-port-relay', gettext_noop('Tor relay port available'),
Result.PASSED if 'orport' in ports else Result.FAILED))
if 'orport' in ports:
results.append(
@ -145,7 +146,8 @@ class TorApp(app_module.App):
if status['bridge_relay_enabled']:
results.append(
DiagnosticCheck(
'tor-port-obfs3', _('Obfs3 transport registered'),
'tor-port-obfs3',
gettext_noop('Obfs3 transport registered'),
Result.PASSED if 'obfs3' in ports else Result.FAILED))
if 'obfs3' in ports:
results.append(
@ -155,7 +157,8 @@ class TorApp(app_module.App):
results.append(
DiagnosticCheck(
'tor-port-obfs4', _('Obfs4 transport registered'),
'tor-port-obfs4',
gettext_noop('Obfs4 transport registered'),
Result.PASSED if 'obfs4' in ports else Result.FAILED))
if 'obfs4' in ports:
results.append(
@ -167,8 +170,8 @@ class TorApp(app_module.App):
hs_hostname = status['hs_hostname'].split('.onion')[0]
results.append(
DiagnosticCheck(
'tor-onion-version', _('Onion service is version 3'),
Result.PASSED
'tor-onion-version',
gettext_noop('Onion service is version 3'), Result.PASSED
if len(hs_hostname) == 56 else Result.FAILED))
return results

View File

@ -6,6 +6,7 @@ import logging
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext_noop
from plinth import app as app_module
from plinth import cfg, frontpage, kvstore, menu
@ -136,8 +137,7 @@ def _diagnose_url_via_tor(url, kind=None):
"""Diagnose whether a URL is reachable via Tor."""
result = diagnose_url(url, kind=kind, wrapper='torsocks')
result.check_id = 'torproxy-url'
result.description = _('Access URL {url} on tcp{kind} via Tor') \
.format(url=url, kind=kind)
result.description = gettext_noop('Access URL {url} on tcp{kind} via Tor')
return result
@ -148,7 +148,7 @@ def _diagnose_tor_use(url, kind=None):
result = diagnose_url(url, kind=kind, wrapper='torsocks',
expected_output=expected_output)
result.check_id = 'torproxy-using-tor'
result.description = _('Confirm Tor usage at {url} on tcp{kind}') \
.format(url=url, kind=kind)
result.description = gettext_noop(
'Confirm Tor usage at {url} on tcp{kind}')
return result

View File

@ -7,6 +7,7 @@ import subprocess
import augeas
from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext_noop
from plinth import app as app_module
from plinth import cfg, menu
@ -123,8 +124,7 @@ def _diagnose_ldap_entry(search_item):
except subprocess.CalledProcessError:
pass
template = _('Check LDAP entry "{search_item}"')
description = format_lazy(template, search_item=search_item)
description = gettext_noop('Check LDAP entry "{search_item}"')
parameters = {'search_item': search_item}
return DiagnosticCheck(check_id, description, result, parameters)
@ -138,8 +138,7 @@ def _diagnose_nslcd_config(config, key, value):
except KeyError:
result = Result.FAILED
template = _('Check nslcd config "{key} {value}"')
description = format_lazy(template, key=key, value=value)
description = gettext_noop('Check nslcd config "{key} {value}"')
parameters = {'key': key, 'value': value}
return DiagnosticCheck(check_id, description, result, parameters)
@ -169,8 +168,7 @@ def _diagnose_nsswitch_config():
break
template = _('Check nsswitch config "{database}"')
description = format_lazy(template, database=database)
description = gettext_noop('Check nsswitch config "{database}"')
parameters = {'database': database}
results.append(

View File

@ -8,7 +8,7 @@ import time
import apt.cache
from django.utils.translation import gettext as _
from django.utils.translation import gettext_lazy
from django.utils.translation import gettext_lazy, gettext_noop
import plinth.privileged.packages as privileged
from plinth import app as app_module
@ -211,9 +211,8 @@ class Packages(app_module.FollowerComponent):
package_name = package_expression.actual()
except MissingPackageError:
check_id = f'package-available-{package_expression}'
description = _(
'Package {expression} is not available for '
'install').format(expression=package_expression)
description = gettext_noop('Package {package_expression} is '
'not available for install')
parameters = {'package_expression': str(package_expression)}
results.append(
DiagnosticCheck(check_id, description, Result.FAILED,
@ -229,10 +228,8 @@ class Packages(app_module.FollowerComponent):
result = Result.PASSED
check_id = f'package-latest-{package_name}'
description = _('Package {package_name} is the latest version '
'({latest_version})').format(
package_name=package_name,
latest_version=latest_version)
description = gettext_noop('Package {package_name} is the latest '
'version ({latest_version})')
parameters = {
'package_name': package_name,
'latest_version': latest_version,

View File

@ -9,7 +9,8 @@ import pytest
from plinth.app import App
from plinth.config import DropinConfigs
from plinth.modules.diagnostics.check import DiagnosticCheck, Result
from plinth.modules.diagnostics.check import (DiagnosticCheck, Result,
translate_checks)
pytestmark = pytest.mark.usefixtures('mock_privileged')
privileged_modules_to_mock = ['plinth.privileged.config']
@ -160,7 +161,7 @@ def test_dropin_config_diagnose_symlinks(dropin_configs, tmp_path):
"""Test diagnosing dropin configs for symlinks."""
with patch('plinth.config.DropinConfigs.ROOT', new=tmp_path):
# Nothing exists
results = dropin_configs.diagnose()
results = translate_checks(dropin_configs.diagnose())
assert results == [
DiagnosticCheck(
f'dropin-config-{tmp_path}/etc/test/path1',

View File

@ -12,7 +12,8 @@ import pytest
from plinth.app import App, FollowerComponent, Info
from plinth.daemon import (Daemon, RelatedDaemon, app_is_running,
diagnose_netcat, diagnose_port_listening)
from plinth.modules.diagnostics.check import DiagnosticCheck, Result
from plinth.modules.diagnostics.check import (DiagnosticCheck, Result,
translate, translate_checks)
privileged_modules_to_mock = ['plinth.privileged.service']
@ -148,7 +149,7 @@ def test_diagnose(port_listening, service_is_running, daemon):
(345, 'udp')])
port_listening.side_effect = side_effect
service_is_running.return_value = True
results = daemon.diagnose()
results = translate_checks(daemon.diagnose())
assert results == [
DiagnosticCheck('daemon-running-test-unit',
'Service test-unit is running', Result.PASSED,
@ -214,14 +215,14 @@ def test_diagnose_port_listening(connections):
]
# Check that message is correct
results = diagnose_port_listening(1234)
results = translate(diagnose_port_listening(1234))
assert results == DiagnosticCheck('daemon-listening-tcp-1234',
'Listening on tcp port 1234',
Result.PASSED, {
'kind': 'tcp',
'port': 1234
})
results = diagnose_port_listening(1234, 'tcp', '0.0.0.0')
results = translate(diagnose_port_listening(1234, 'tcp', '0.0.0.0'))
assert results == DiagnosticCheck(
'daemon-listening-address-tcp-1234-0.0.0.0',
'Listening on tcp port 0.0.0.0:1234', Result.PASSED, {
@ -231,14 +232,14 @@ def test_diagnose_port_listening(connections):
})
# Failed results
results = diagnose_port_listening(4321)
results = translate(diagnose_port_listening(4321))
assert results == DiagnosticCheck('daemon-listening-tcp-4321',
'Listening on tcp port 4321',
Result.FAILED, {
'kind': 'tcp',
'port': 4321
})
results = diagnose_port_listening(4321, 'tcp', '0.0.0.0')
results = translate(diagnose_port_listening(4321, 'tcp', '0.0.0.0'))
assert results == DiagnosticCheck(
'daemon-listening-address-tcp-4321-0.0.0.0',
'Listening on tcp port 0.0.0.0:4321', Result.FAILED, {

View File

@ -10,7 +10,8 @@ import pytest
from plinth.app import App
from plinth.errors import MissingPackageError
from plinth.modules.diagnostics.check import DiagnosticCheck, Result
from plinth.modules.diagnostics.check import (DiagnosticCheck, Result,
translate_checks)
from plinth.package import Package, Packages, packages_installed
@ -238,27 +239,33 @@ def test_diagnose(cache):
Package('package4') | Package('package5'),
Package('package6') | Package('package7')
])
results = component.diagnose()
results = translate_checks(component.diagnose())
assert results == [
DiagnosticCheck('package-available-package1',
'Package package1 is not available for install',
Result.FAILED, {'package_expression': 'package1'}),
DiagnosticCheck('package-latest-package2',
'Package package2 is the latest version (2.0)',
Result.PASSED,
{'package_name': 'package2', 'latest_version': '2.0'}),
Result.PASSED, {
'package_name': 'package2',
'latest_version': '2.0'
}),
DiagnosticCheck('package-latest-package3',
'Package package3 is the latest version (3.0)',
Result.WARNING,
{'package_name': 'package3', 'latest_version': '3.0'}),
Result.WARNING, {
'package_name': 'package3',
'latest_version': '3.0'
}),
DiagnosticCheck(
'package-available-package4 | package5',
'Package package4 | package5 is not available for install',
Result.FAILED, {'package_expression': 'package4 | package5'}),
DiagnosticCheck('package-latest-package7',
'Package package7 is the latest version (4.0)',
Result.PASSED,
{'package_name': 'package7', 'latest_version': '4.0'}),
Result.PASSED, {
'package_name': 'package7',
'latest_version': '4.0'
}),
]