def test_asymmetric(self): """ Call update_stats on asymmetric resources even if they don't exist in the target locale. """ with patch('pontoon.sync.core.update_stats') as update_stats, \ patch.object(Resource, 'is_asymmetric', new_callable=PropertyMock) as is_asymmetric: is_asymmetric.return_value = True update_project_stats(self.db_project, self.vcs_project, self.changeset) update_stats.assert_any_call(self.main_db_resource, self.translated_locale) update_stats.assert_any_call(self.other_db_resource, self.translated_locale) update_stats.assert_any_call(self.missing_db_resource, self.translated_locale)
def test_basic(self): """ Call update_stats on all resources available in the current locale. """ with patch('pontoon.sync.core.update_stats') as update_stats: update_project_stats(self.db_project, self.vcs_project, self.changeset) update_stats.assert_any_call(self.main_db_resource, self.translated_locale) update_stats.assert_any_call(self.other_db_resource, self.translated_locale) assert_not_in( call(self.missing_db_resource, self.translated_locale), update_stats.mock_calls )
def test_asymmetric(self): """ Call update_stats on asymmetric resources even if they don't exist in the target locale. """ with patch('pontoon.sync.core.update_stats') as update_stats, \ patch.object(Resource, 'is_asymmetric', new_callable=PropertyMock) as is_asymmetric: is_asymmetric.return_value = True update_project_stats(self.db_project, self.vcs_project, self.changeset, self.translated_locale) update_stats.assert_any_call(self.main_db_resource, self.translated_locale) update_stats.assert_any_call(self.other_db_resource, self.translated_locale) update_stats.assert_any_call(self.missing_db_resource, self.translated_locale)
def test_basic(self): """ Call update_stats on all resources available in the current locale. """ with patch('pontoon.sync.core.update_stats') as update_stats: update_project_stats(self.db_project, self.vcs_project, self.changeset, self.translated_locale) update_stats.assert_any_call(self.main_db_resource, self.translated_locale) update_stats.assert_any_call(self.other_db_resource, self.translated_locale) assert_not_in( call(self.missing_db_resource, self.translated_locale), update_stats.mock_calls )
def test_extra_locales(self): """ Only update stats for active locales, even if the inactive locale has a resource. """ with patch('pontoon.sync.core.update_stats') as update_stats: update_project_stats(self.db_project, self.vcs_project, self.changeset, self.translated_locale) update_stats.assert_any_call(self.main_db_resource, self.translated_locale) update_stats.assert_any_call(self.other_db_resource, self.translated_locale) assert_not_in(call(self.main_db_resource, self.inactive_locale), update_stats.mock_calls) assert_not_in(call(self.other_db_resource, self.inactive_locale), update_stats.mock_calls)
def test_extra_locales(self): """ Only update stats for active locales, even if the inactive locale has a resource. """ with patch('pontoon.sync.core.update_stats') as update_stats: update_project_stats(self.db_project, self.vcs_project, self.changeset) update_stats.assert_any_call(self.main_db_resource, self.translated_locale) update_stats.assert_any_call(self.other_db_resource, self.translated_locale) assert_not_in( call(self.main_db_resource, self.inactive_locale), update_stats.mock_calls ) assert_not_in( call(self.other_db_resource, self.inactive_locale), update_stats.mock_calls )
def sync_project_repo(self, project_pk, repo_pk, project_sync_log_pk, now, no_pull=False, no_commit=False): db_project = get_or_fail(Project, pk=project_pk, message='Could not sync project with pk={0}, not found.'.format(project_pk)) repo = get_or_fail(Repository, pk=repo_pk, message='Could not sync repo with pk={0}, not found.'.format(project_pk)) project_sync_log = get_or_fail(ProjectSyncLog, pk=project_sync_log_pk, message=('Could not sync project {0}, log with pk={1} not found.' .format(db_project.slug, project_sync_log_pk))) repo_sync_log = RepositorySyncLog.objects.create( project_sync_log=project_sync_log, repository=repo, start_time=timezone.now() ) # Pull VCS changes in case we're on a different worker than the one # sync started on. if not no_pull: pull_changes(db_project) if len(repo.locales) < 1: log.warning('Could not sync repo `{0}`, no locales found within.' .format(repo.url)) return vcs_project = VCSProject(db_project, locales=repo.locales) for locale in repo.locales: try: with transaction.atomic(): changeset = ChangeSet(db_project, vcs_project, now) update_translations(db_project, vcs_project, locale, changeset) changeset.execute() update_project_stats(db_project, vcs_project, changeset, locale) # Clear out the "has_changed" markers now that we've finished # syncing. (ChangedEntityLocale.objects .filter(entity__resource__project=db_project, locale=locale, when__lte=now) .delete()) db_project.has_changed = False db_project.save() # Clean up any duplicate approvals at the end of sync right # before we commit the transaction to avoid race conditions. with connection.cursor() as cursor: cursor.execute(""" UPDATE base_translation AS b SET approved = FALSE, approved_date = NULL WHERE id IN (SELECT trans.id FROM base_translation AS trans LEFT JOIN base_entity AS ent ON ent.id = trans.entity_id LEFT JOIN base_resource AS res ON res.id = ent.resource_id WHERE locale_id = %(locale_id)s AND res.project_id = %(project_id)s) AND approved_date != (SELECT max(approved_date) FROM base_translation WHERE entity_id = b.entity_id AND locale_id = b.locale_id AND (plural_form = b.plural_form OR plural_form IS NULL)); """, { 'locale_id': locale.id, 'project_id': db_project.id }) # Perform the commit last so that, if it succeeds, there is # nothing after it to fail. if not no_commit and locale in changeset.locales_to_commit: commit_changes(db_project, vcs_project, changeset, locale) except CommitToRepositoryException as err: # Transaction aborted, log and move on to the next locale. log.warning( 'Failed to sync locale {locale} for project {project} due to ' 'commit error: {error}'.format( locale=locale.code, project=db_project.slug, error=err, ) ) repo_sync_log.end_time = timezone.now() repo_sync_log.save() log.info('Synced translations for project {0} in locales {1}.'.format( db_project.slug, ','.join(locale.code for locale in repo.locales) ))
def sync_project_repo(self, project_pk, repo_pk, project_sync_log_pk, now, no_pull=False, no_commit=False): db_project = get_or_fail(Project, pk=project_pk, message='Could not sync project with pk={0}, not found.'.format(project_pk)) repo = get_or_fail(Repository, pk=repo_pk, message='Could not sync repo with pk={0}, not found.'.format(project_pk)) project_sync_log = get_or_fail(ProjectSyncLog, pk=project_sync_log_pk, message=('Could not sync project {0}, log with pk={1} not found.' .format(db_project.slug, project_sync_log_pk))) repo_sync_log = RepositorySyncLog.objects.create( project_sync_log=project_sync_log, repository=repo, start_time=timezone.now() ) # Pull VCS changes in case we're on a different worker than the one # sync started on. if not no_pull: pull_changes(db_project) if len(repo.locales) < 1: log.warning('Could not sync repo `{0}`, no locales found within.' .format(repo.url)) repo_sync_log.end_time = timezone.now() repo_sync_log.save(update_fields=['end_time']) return vcs_project = VCSProject(db_project, locales=repo.locales) for locale in repo.locales: try: with transaction.atomic(): changeset = ChangeSet(db_project, vcs_project, now) update_translations(db_project, vcs_project, locale, changeset) changeset.execute() update_project_stats(db_project, vcs_project, changeset, locale) # Clear out the "has_changed" markers now that we've finished # syncing. (ChangedEntityLocale.objects .filter(entity__resource__project=db_project, locale=locale, when__lte=now) .delete()) db_project.has_changed = False db_project.save(update_fields=['has_changed']) # Clean up any duplicate approvals at the end of sync right # before we commit the transaction to avoid race conditions. with connection.cursor() as cursor: cursor.execute(""" UPDATE base_translation AS b SET approved = FALSE, approved_date = NULL WHERE id IN (SELECT trans.id FROM base_translation AS trans LEFT JOIN base_entity AS ent ON ent.id = trans.entity_id LEFT JOIN base_resource AS res ON res.id = ent.resource_id WHERE locale_id = %(locale_id)s AND res.project_id = %(project_id)s) AND approved_date != (SELECT max(approved_date) FROM base_translation WHERE entity_id = b.entity_id AND locale_id = b.locale_id AND (plural_form = b.plural_form OR plural_form IS NULL)); """, { 'locale_id': locale.id, 'project_id': db_project.id }) # Perform the commit last so that, if it succeeds, there is # nothing after it to fail. if not no_commit and locale in changeset.locales_to_commit: commit_changes(db_project, vcs_project, changeset, locale) except CommitToRepositoryException as err: # Transaction aborted, log and move on to the next locale. log.warning( 'Failed to sync locale {locale} for project {project} due to ' 'commit error: {error}'.format( locale=locale.code, project=db_project.slug, error=err, ) ) repo_sync_log.end_time = timezone.now() repo_sync_log.save() log.info('Synced translations for project {0} in locales {1}.'.format( db_project.slug, ','.join(locale.code for locale in repo.locales) ))