Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor sync status logging #3529

Merged
merged 17 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions pontoon/administration/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from django.http import Http404, HttpResponse, HttpResponseForbidden, JsonResponse
from django.shortcuts import render
from django.template.defaultfilters import slugify
from django.utils import timezone
from django.utils.datastructures import MultiValueDictKeyError

from pontoon.administration.forms import (
Expand All @@ -31,7 +30,6 @@
)
from pontoon.base.utils import require_AJAX
from pontoon.pretranslation.tasks import pretranslate_task
from pontoon.sync.models import SyncLog
from pontoon.sync.tasks import sync_project_task


Expand Down Expand Up @@ -543,8 +541,7 @@ def manually_sync_project(request, slug):
)

project = Project.objects.get(slug=slug)
sync_log = SyncLog.objects.create(start_time=timezone.now())
sync_project_task.delay(project.pk, sync_log.pk)
sync_project_task.delay(project.pk)

return HttpResponse("ok")

Expand Down
8 changes: 8 additions & 0 deletions pontoon/base/static/js/table.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ var Pontoon = (function (my) {
return parseInt($(el).find('span').text().replace(/,/g, ''));
}

function getSort(el) {
return parseInt($(el).find('[data-sort]').data('sort'), 10) || 0;
}

function getString(el) {
return $(el)
.find('td:eq(' + index + ')')
Expand Down Expand Up @@ -242,6 +246,10 @@ var Pontoon = (function (my) {
} else if (node.is('.population')) {
return (getNumber(a) - getNumber(b)) * dir;

// Sort by the data-sort attribute
} else if (node.attr('data-sort') !== undefined) {
return (getSort(a) - getSort(b)) * dir;

// Sort by alphabetical order
} else {
return getString(a).localeCompare(getString(b)) * dir;
Expand Down
1 change: 1 addition & 0 deletions pontoon/base/templates/django/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@

{% if perms.base.can_manage_project %}
<li><a href="{% url 'pontoon.admin' %}">Admin</a></li>
<li><a href="{% url 'pontoon.sync.log' %}">Sync Log</a></li>
{% endif %}

{% if settings.AUTHENTICATION_METHOD == 'django' %}
Expand Down
1 change: 1 addition & 0 deletions pontoon/base/templates/header.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
{% if project %}
<li><a href="{{ url('pontoon.admin.project', project.slug) }}"><i class="fas fa-wrench fa-fw"></i>Admin &middot; Current Project</a></li>
{% endif %}
<li><a href="{{ url('pontoon.sync.log') }}"><i class="fas fa-sync fa-fw"></i>Sync Log</a></li>
{% endif %}

{% if user.is_authenticated %}
Expand Down
21 changes: 0 additions & 21 deletions pontoon/base/templatetags/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,27 +176,6 @@ def format_datetime(value, format="full", default="---"):
return default


@library.filter
def format_timedelta(value):
if value is not None:
parts = []
if value.days > 0:
parts.append(f"{value.days} days")
minutes = value.seconds // 60
seconds = value.seconds % 60
if minutes > 0:
parts.append(f"{minutes} minutes")
if seconds > 0:
parts.append(f"{seconds} seconds")

if parts:
return ", ".join(parts)
else:
return "0 seconds"
else:
return "---"


@library.filter
def format_for_inbox(date):
# Localize the date to the current timezone
Expand Down
24 changes: 0 additions & 24 deletions pontoon/base/tests/test_helpers.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
from datetime import timedelta
from unittest.mock import patch

import pytest

from pontoon.base.templatetags.helpers import (
format_datetime,
format_timedelta,
full_static,
full_url,
metric_prefix,
Expand Down Expand Up @@ -71,28 +69,6 @@ def test_helper_base_format_dt_builtin(settings):
assert format_datetime(datetime, "time") == "05:07 UTC"


def test_helper_base_format_timedelta_none(settings):
assert format_timedelta(None) == "---"


def test_helper_base_format_timedelta_seconds(settings):
assert format_timedelta(timedelta(seconds=5)) == "5 seconds"


def test_helper_base_format_timedelta_minutes(settings):
duration = timedelta(minutes=1, seconds=7)
assert format_timedelta(duration) == "1 minutes, 7 seconds"


def test_helper_base_format_timedelta_days(settings):
duration = timedelta(days=2, minutes=1, seconds=8)
assert format_timedelta(duration) == "2 days, 1 minutes, 8 seconds"


def test_helper_base_format_timedelta_0(settings):
assert format_timedelta(timedelta(seconds=0)) == "0 seconds"


def test_helper_base_nospam_unicode(settings):
assert str(nospam("<łążźćń>")) == "&lt;łążźćń&gt;"

Expand Down
17 changes: 0 additions & 17 deletions pontoon/insights/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from pontoon.insights.models import (
LocaleInsightsSnapshot,
ProjectInsightsSnapshot,
ProjectLocaleInsightsSnapshot,
)

Expand All @@ -22,21 +21,6 @@ class LocaleInsightsSnapshotAdmin(admin.ModelAdmin):
)


class ProjectInsightsSnapshotAdmin(admin.ModelAdmin):
search_fields = [
"pk",
"project__slug",
"project__name",
]
list_display = (
"pk",
"project",
"created_at",
"completion",
"unreviewed_strings",
)


class ProjectLocaleInsightsSnapshotAdmin(admin.ModelAdmin):
search_fields = [
"pk",
Expand All @@ -56,5 +40,4 @@ class ProjectLocaleInsightsSnapshotAdmin(admin.ModelAdmin):


admin.site.register(LocaleInsightsSnapshot, LocaleInsightsSnapshotAdmin)
admin.site.register(ProjectInsightsSnapshot, ProjectInsightsSnapshotAdmin)
admin.site.register(ProjectLocaleInsightsSnapshot, ProjectLocaleInsightsSnapshotAdmin)
15 changes: 15 additions & 0 deletions pontoon/insights/migrations/0016_delete_projectinsightssnapshot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Generated by Django 4.2.17 on 2025-01-17 14:50

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("insights", "0015_fix_projectlocale_insights_data"),
]

operations = [
migrations.DeleteModel(
name="ProjectInsightsSnapshot",
),
]
4 changes: 0 additions & 4 deletions pontoon/insights/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,5 @@ class LocaleInsightsSnapshot(InsightsSnapshot):
locale = models.ForeignKey("base.Locale", models.CASCADE)


class ProjectInsightsSnapshot(InsightsSnapshot):
project = models.ForeignKey("base.Project", models.CASCADE)


class ProjectLocaleInsightsSnapshot(InsightsSnapshot):
project_locale = models.ForeignKey("base.ProjectLocale", models.CASCADE)
13 changes: 10 additions & 3 deletions pontoon/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,9 +510,12 @@ def _default_from_email():
),
"output_filename": "css/teams.min.css",
},
"sync_logs": {
"source_filenames": ("css/sync_logs.css",),
"output_filename": "css/sync_logs.min.css",
"sync_log": {
"source_filenames": (
"css/table.css",
"css/sync_log.css",
),
"output_filename": "css/sync_log.min.css",
},
"profile": {
"source_filenames": (
Expand Down Expand Up @@ -682,6 +685,10 @@ def _default_from_email():
),
"output_filename": "js/teams.min.js",
},
"sync_log": {
"source_filenames": ("js/table.js",),
"output_filename": "css/sync_log.min.js",
},
"profile": {
"source_filenames": (
"js/lib/chart.umd.min.js",
Expand Down
61 changes: 5 additions & 56 deletions pontoon/sync/admin.py
Original file line number Diff line number Diff line change
@@ -1,62 +1,11 @@
from django.contrib import admin
from django.urls import reverse
from django.utils.html import format_html

from pontoon.sync import models
from pontoon.sync.models import Sync


TIMES = ("start_time", "end_time", "duration")
class SyncAdmin(admin.ModelAdmin):
search_fields = ("project__slug",)
list_display = ("project", "status", "start_time", "end_time", "error")


class ProjectSyncLogInline(admin.TabularInline):
model = models.ProjectSyncLog
extra = 0
verbose_name_plural = "Projects"
readonly_fields = ("edit_link",)

@admin.display
def edit_link(self, obj):
url = reverse(
f"admin:{obj._meta.app_label}_{obj._meta.model_name}_change",
args=[obj.pk],
)
if obj.pk:
return format_html('<a href="{}">edit</a>', url)
else:
return ""


class SyncLogAdmin(admin.ModelAdmin):
search_fields = ("sync_log",)
list_display = TIMES
inlines = (ProjectSyncLogInline,)


class RepositorySyncLogInline(admin.TabularInline):
model = models.RepositorySyncLog
extra = 0
verbose_name_plural = "Repositories"


class ProjectSyncLogAdmin(admin.ModelAdmin):
search_fields = ("project_sync_log",)
list_display = (
"project",
"status",
) + TIMES
inlines = (RepositorySyncLogInline,)
autocomplete_fields = ("sync_log",)


class RepositorySyncLogAdmin(admin.ModelAdmin):
list_display = ("repository_url",) + TIMES
autocomplete_fields = ("project_sync_log",)

@admin.display
def repository_url(self, obj):
return obj.repository.url


admin.site.register(models.SyncLog, SyncLogAdmin)
admin.site.register(models.ProjectSyncLog, ProjectSyncLogAdmin)
admin.site.register(models.RepositorySyncLog, RepositorySyncLogAdmin)
admin.site.register(Sync, SyncAdmin)
38 changes: 15 additions & 23 deletions pontoon/sync/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,31 @@
from pontoon.sync.core.stats import update_stats
from pontoon.sync.core.translations_from_repo import sync_translations_from_repo
from pontoon.sync.core.translations_to_repo import sync_translations_to_repo
from pontoon.sync.models import ProjectSyncLog, RepositorySyncLog, SyncLog


log = logging.getLogger(__name__)


def sync_project(
project: Project,
sync_log: SyncLog,
*,
pull: bool = True,
commit: bool = True,
force: bool = False,
):
) -> tuple[bool, bool]:
"""`(db_changed, repo_changed)`"""
# Mark "now" at the start of sync to avoid messing with
# translations submitted during sync.
now = timezone.now()

log_prefix = f"[{project.slug}]"
log.info(f"{log_prefix} Sync start")
project_sync_log = ProjectSyncLog.objects.create(
sync_log=sync_log, project=project, start_time=now
)

try:
checkouts = checkout_repos(project, force=force, pull=pull)
paths = find_paths(project, checkouts)
except Exception as e:
log.error(f"{log_prefix} {e}")
project_sync_log.skip()
raise e

locale_map: dict[str, Locale] = {
Expand All @@ -52,21 +47,22 @@ def sync_project(
project, locale_map, checkouts.source, paths, now
)

repo_sync_log = RepositorySyncLog.objects.create(
project_sync_log=project_sync_log,
repository=checkouts.target.repo,
start_time=timezone.now(),
)

db_changes = ChangedEntityLocale.objects.filter(
entity__resource__project=project, when__lte=now
).select_related("entity__resource", "locale")
del_trans_count, updated_trans_count = sync_translations_from_repo(
project, locale_map, checkouts, paths, db_changes, now
)
db_changed = bool(
added_entities_count
or changed_paths
or removed_paths
or del_trans_count
or updated_trans_count
)
if added_entities_count > 0:
notify_users(project, added_entities_count)
sync_translations_to_repo(
repo_changed = sync_translations_to_repo(
project,
commit,
locale_map,
Expand All @@ -77,26 +73,22 @@ def sync_project(
removed_paths,
now,
)
if commit:
db_changes.delete()

db_changes.delete()
checkouts.source.repo.last_synced_revision = checkouts.source.commit
if checkouts.target != checkouts.source:
checkouts.target.repo.last_synced_revision = checkouts.target.commit
if (
added_entities_count
or changed_paths
or removed_paths
or del_trans_count
or updated_trans_count
):
if db_changed:
update_stats(project)
repo_sync_log.end()
log.info(f"{log_prefix} Sync done")

if project.pretranslation_enabled and changed_paths:
# Pretranslate changed and added resources for all locales
pretranslate(project, changed_paths)

return db_changed, repo_changed


def notify_users(project: Project, count: int) -> None:
users = User.objects.filter(
Expand Down
Loading
Loading