def flags(self): """Return flags from unit. We currently extract maxwidth attribute. """ flags = Flags() if hasattr(self.unit, "xmlelement"): flags.merge(self.unit.xmlelement) if hasattr(self.template, "xmlelement"): flags.merge(self.template.xmlelement) return flags.format()
def bulk_perform( user, unit_set, query, target_state, add_flags, remove_flags, add_labels, remove_labels, ): matching = unit_set.search(query) components = Component.objects.filter( id__in=matching.values_list("translation__component_id", flat=True)) target_state = int(target_state) add_flags = Flags(add_flags) remove_flags = Flags(remove_flags) updated = 0 for component in components: component.preload_sources() with transaction.atomic(), component.lock(): for unit in matching.filter( translation__component=component).select_for_update(): if user is not None and not user.has_perm("unit.edit", unit): continue updated += 1 if (target_state != -1 and unit.state > STATE_EMPTY and unit.state < STATE_READONLY): unit.translate( user, unit.target, target_state, change_action=Change.ACTION_BULK_EDIT, propagate=False, ) if add_flags or remove_flags: flags = Flags(unit.source_info.extra_flags) flags.merge(add_flags) flags.remove(remove_flags) unit.source_info.is_bulk_edit = True unit.source_info.extra_flags = flags.format() unit.source_info.save(update_fields=["extra_flags"]) if add_labels: unit.source_info.is_bulk_edit = True unit.source_info.labels.add(*add_labels) if remove_labels: unit.source_info.is_bulk_edit = True unit.source_info.labels.remove(*remove_labels) component.invalidate_stats_deep() return updated
def bulk_edit(request, project, component=None, lang=None): obj, unit_set, context = parse_url(request, project, component, lang) if not request.user.has_perm('translation.auto', obj): raise PermissionDenied() form = BulkEditForm(request.user, obj, request.POST) if not form.is_valid(): messages.error(request, _('Failed to process form!')) show_form_errors(request, form) return redirect(obj) target_state = int(form.cleaned_data['state']) add_flags = Flags(form.cleaned_data['add_flags']) remove_flags = Flags(form.cleaned_data['remove_flags']) matching = unit_set.search(form.cleaned_data['q']) updated = 0 with transaction.atomic(): for unit in matching.select_for_update(): if not request.user.has_perm('unit.edit', unit): continue if target_state != -1 and unit.state: unit.translate( request.user, unit.target, target_state, change_action=Change.ACTION_MASS_STATE, ) updated += 1 if add_flags or remove_flags: flags = Flags(unit.source_info.extra_flags) flags.merge(add_flags) flags.remove(remove_flags) unit.source_info.extra_flags = flags.format() unit.source_info.save(update_fields=['extra_flags']) updated += 1 import_message( request, updated, _('Bulk edit completed, no strings were updated.'), ungettext( 'Bulk edit completed, %d string was updated.', 'Bulk edit completed, %d strings were updated.', updated, ), ) return redirect(obj)
def ignore_check_source(request, check_id): obj = get_object_or_404(Check, pk=int(check_id)) unit = obj.unit.source_unit if not request.user.has_perm("unit.check", obj) or not request.user.has_perm( "source.edit", unit.translation.component ): raise PermissionDenied() # Mark check for ignoring ignore = obj.check_obj.ignore_string flags = Flags(unit.extra_flags) if ignore not in flags: flags.merge(ignore) unit.extra_flags = flags.format() unit.save(same_content=True) # response for AJAX return HttpResponse("ok")
def bulk_perform( user, unit_set, query, target_state, add_flags, remove_flags, add_labels, remove_labels, ): matching = unit_set.search(query) target_state = int(target_state) add_flags = Flags(add_flags) remove_flags = Flags(remove_flags) updated = 0 with transaction.atomic(): for unit in matching.select_for_update(): if user is not None and not user.has_perm("unit.edit", unit): continue if target_state != -1 and unit.state: unit.translate( user, unit.target, target_state, change_action=Change.ACTION_MASS_STATE, ) updated += 1 if add_flags or remove_flags: flags = Flags(unit.source_info.extra_flags) flags.merge(add_flags) flags.remove(remove_flags) unit.source_info.extra_flags = flags.format() unit.source_info.save(update_fields=["extra_flags"]) updated += 1 if add_labels: unit.source_info.labels.add(*add_labels) updated += 1 if remove_labels: unit.source_info.labels.remove(*remove_labels) updated += 1 return updated
def ignore_check_source(request, check_id): obj = get_object_or_404(Check, pk=int(check_id)) unit = obj.unit.source_info project = unit.translation.component.project request.user.check_access(project) if (not request.user.has_perm('unit.check', project) or not request.user.has_perm('source.edit', unit.translation.component)): raise PermissionDenied() # Mark check for ignoring ignore = obj.check_obj.ignore_string flags = Flags(unit.extra_flags) if ignore not in flags: flags.merge(ignore) unit.extra_flags = flags.format() unit.save() # response for AJAX return HttpResponse('ok')
def ignore_check_source(request, check_id, pk): obj = get_object_or_404(Check, pk=int(check_id)) request.user.check_access(obj.project) source = get_object_or_404(Unit, pk=int(pk)) if (obj.project != source.translation.component.project or not request.user.has_perm('unit.check', obj.project) or not request.user.has_perm('source.edit', source.translation.component)): raise PermissionDenied() # Mark check for ignoring ignore = obj.check_obj.ignore_string flags = Flags(source.extra_flags) if ignore not in flags: flags.merge(ignore) source.extra_flags = flags.format() source.save() # response for AJAX return HttpResponse('ok')
def edit_context(request, pk): unit = get_object_or_404(Unit, pk=pk) if not unit.is_source and not unit.translation.component.is_glossary: raise Http404("Non source unit!") do_add = "addflag" in request.POST if do_add or "removeflag" in request.POST: if not request.user.has_perm("unit.flag", unit.translation): raise PermissionDenied() flag = request.POST.get("addflag", request.POST.get("removeflag")) flags = Flags(unit.extra_flags) if ( flag in ("terminology", "forbidden") and not unit.is_source and flag not in flags ): unit = unit.source_unit flags = Flags(unit.extra_flags) if do_add: flags.merge(flag) else: flags.remove(flag) new_flags = flags.format() if new_flags != unit.extra_flags: unit.extra_flags = new_flags unit.save(same_content=True, update_fields=["extra_flags"]) else: if not request.user.has_perm("source.edit", unit.translation): raise PermissionDenied() form = ContextForm(request.POST, instance=unit, user=request.user) if form.is_valid(): form.save() else: messages.error(request, _("Failed to change additional string info!")) show_form_errors(request, form) return redirect_next(request.POST.get("next"), unit.get_absolute_url())
def new_unit(request, project, component, lang): translation = get_translation(request, project, component, lang) if not request.user.has_perm("unit.add", translation): raise PermissionDenied() form = get_new_unit_form(translation, request.user, request.POST) if not form.is_valid(): show_form_errors(request, form) else: new_unit = translation.add_unit(request, *form.as_tuple()) messages.success(request, _("New string has been added.")) if form.cleaned_data["variant"]: flags = Flags(new_unit.extra_flags) flags.set_value("variant", form.cleaned_data["variant"]) new_unit.extra_flags = flags.format() new_unit.save( update_fields=["extra_flags"], same_content=True, run_checks=False, ) return redirect(new_unit) return redirect(translation)
def add_unit( # noqa: C901 self, request, context: str, source: Union[str, List[str]], target: Optional[Union[str, List[str]]] = None, extra_flags: str = "", auto_context: bool = False, is_batch_update: bool = False, ): user = request.user if request else None component = self.component if self.is_source: translations = [self] translations.extend(component.translation_set.exclude(id=self.id)) else: translations = [component.source_translation, self] has_template = component.has_template() source_unit = None result = None # Automatic context suffix = 0 base = context while self.unit_set.filter(context=context, source=source).exists(): suffix += 1 context = f"{base}{suffix}" for translation in translations: is_source = translation.is_source kwargs = {} if has_template: kwargs["pending"] = is_source else: kwargs["pending"] = not is_source if kwargs["pending"]: kwargs["details"] = {"add_unit": True} if is_source: current_target = source kwargs["extra_flags"] = extra_flags else: current_target = target if current_target is None: current_target = "" if isinstance(current_target, list): current_target = join_plural(current_target) if isinstance(source, list): source = join_plural(source) if has_template: id_hash = calculate_hash(context) else: id_hash = calculate_hash(source, context) # When adding to a target the source string can already exist unit = None if not self.is_source and is_source: try: unit = translation.unit_set.get(id_hash=id_hash) flags = Flags(unit.extra_flags) flags.merge(extra_flags) new_flags = flags.format() if unit.extra_flags != new_flags: unit.save(update_fields=["extra_flags"], same_content=True) except Unit.DoesNotExist: pass if unit is None: unit = Unit( translation=translation, context=context, source=source, target=current_target, state=STATE_TRANSLATED if bool(current_target) else STATE_EMPTY, source_unit=source_unit, id_hash=id_hash, position=0, **kwargs, ) unit.is_batch_update = is_batch_update unit.save(force_insert=True) Change.objects.create( unit=unit, action=Change.ACTION_NEW_UNIT, target=source, user=user, author=user, ) # The source language is always first in the translations array if source_unit is None: source_unit = unit if translation == self: result = unit if not is_batch_update: component.update_variants() component.sync_terminology() return result
def flags(self): """Return flags or typecomments from units.""" flags = Flags(*self.mainunit.typecomments) flags.remove({"fuzzy"}) return flags.format()
def bulk_perform( user, unit_set, query, target_state, add_flags, remove_flags, add_labels, remove_labels, ): matching = unit_set.search(query).prefetch() components = Component.objects.filter( id__in=matching.values_list("translation__component_id", flat=True)) target_state = int(target_state) add_flags = Flags(add_flags) remove_flags = Flags(remove_flags) updated = 0 for component in components: with transaction.atomic(), component.lock(): component.preload_sources() component.commit_pending("bulk edit", user) component_units = matching.filter( translation__component=component).select_for_update() can_edit_source = user is None or user.has_perm( "source.edit", component) update_unit_ids = [] source_units = [] for unit in component_units: changed = False source_unit = unit.source_unit if (target_state != -1 and (user is None or user.has_perm("unit.edit", unit)) and target_state != unit.state and unit.state in EDITABLE_STATES): # Create change object for edit, update is done outside the looop unit.generate_change(user, user, Change.ACTION_BULK_EDIT, check_new=False) changed = True update_unit_ids.append(unit.pk) if unit.is_source: source_units.append(unit) if can_edit_source: if add_flags or remove_flags: flags = Flags(source_unit.extra_flags) flags.merge(add_flags) flags.remove(remove_flags) new_flags = flags.format() if source_unit.extra_flags != new_flags: source_unit.is_bulk_edit = True source_unit.extra_flags = new_flags source_unit.save(update_fields=["extra_flags"]) changed = True if add_labels: source_unit.is_bulk_edit = True source_unit.labels.add(*add_labels) changed = True if remove_labels: source_unit.is_bulk_edit = True source_unit.labels.remove(*remove_labels) changed = True if changed: updated += 1 if target_state != -1: # Bulk update state Unit.objects.filter(pk__in=update_unit_ids).update( pending=True, state=target_state) # Fire source_change event in bulk for source units for unit in source_units: # The change is already done in the database, we # need it here to recalculate state of translation # units unit.is_bulk_edit = True unit.pending = True unit.state = target_state unit.source_unit_save() component.invalidate_stats_deep() return updated
def bulk_perform( user, unit_set, query, target_state, add_flags, remove_flags, add_labels, remove_labels, project, components=None, ): matching = unit_set.search(query, project=project).prefetch() if components is None: components = Component.objects.filter( id__in=matching.values_list("translation__component_id", flat=True) ) target_state = int(target_state) add_flags = Flags(add_flags) remove_flags = Flags(remove_flags) update_source = add_flags or remove_flags or add_labels or remove_labels updated = 0 for component in components: component.batch_checks = True with transaction.atomic(), component.lock(): component.commit_pending("bulk edit", user) component_units = matching.filter(translation__component=component) source_unit_ids = set() if target_state == -1: # Only fetch source unit ids here source_unit_ids = set( component_units.values_list("source_unit_id", flat=True) ) else: update_unit_ids = [] source_units = [] # Generate changes for state change for unit in component_units.select_for_update(): source_unit_ids.add(unit.source_unit_id) if ( (user is None or user.has_perm("unit.edit", unit)) and target_state != unit.state and unit.state in EDITABLE_STATES ): # Create change object for edit, update is done outside the loop unit.generate_change( user, user, Change.ACTION_BULK_EDIT, check_new=False ) updated += 1 update_unit_ids.append(unit.pk) if unit.is_source: source_units.append(unit) # Bulk update state Unit.objects.filter(pk__in=update_unit_ids).update( pending=True, state=target_state ) # Fire source_change event in bulk for source units for unit in source_units: # The change is already done in the database, we # need it here to recalculate state of translation # units unit.is_batch_update = True unit.pending = True unit.state = target_state unit.source_unit_save() if update_source and ( user is None or user.has_perm("source.edit", component) ): # Perform changes on the source units source_units = ( Unit.objects.filter(pk__in=source_unit_ids) .prefetch() .prefetch_bulk() ) if add_labels or remove_labels: source_units = source_units.prefetch_related("labels") for source_unit in source_units.select_for_update(): changed = False if add_flags or remove_flags: flags = Flags(source_unit.extra_flags) flags.merge(add_flags) flags.remove(remove_flags) new_flags = flags.format() if source_unit.extra_flags != new_flags: source_unit.is_batch_update = True source_unit.extra_flags = new_flags source_unit.save(update_fields=["extra_flags"]) changed = True if add_labels: source_unit.is_batch_update = True source_unit.labels.add(*add_labels) changed = True if remove_labels: source_unit.is_batch_update = True source_unit.labels.remove(*remove_labels) changed = True if changed: updated += 1 component.invalidate_cache() component.update_source_checks() component.run_batched_checks() return updated
def bulk_perform( user, unit_set, query, target_state, add_flags, remove_flags, add_labels, remove_labels, ): matching = unit_set.search(query) components = Component.objects.filter( id__in=matching.values_list("translation__component_id", flat=True) ) target_state = int(target_state) add_flags = Flags(add_flags) remove_flags = Flags(remove_flags) updated = 0 for component in components: with transaction.atomic(), component.lock(): component.preload_sources() component.commit_pending("bulk edit", user) component_units = matching.filter( translation__component=component ).select_for_update() can_edit_source = user is None or user.has_perm("source.edit", component) for unit in component_units: changed = False if ( target_state != -1 and (user is None or user.has_perm("unit.edit", unit)) and target_state != unit.state and unit.state in EDITABLE_STATES ): # Create change object for edit, update is done outside the looop unit.generate_change( user, user, Change.ACTION_BULK_EDIT, check_new=False ) changed = True if can_edit_source: if add_flags or remove_flags: flags = Flags(unit.source_info.extra_flags) flags.merge(add_flags) flags.remove(remove_flags) unit.source_info.is_bulk_edit = True unit.source_info.extra_flags = flags.format() unit.source_info.save(update_fields=["extra_flags"]) changed = True if add_labels: unit.source_info.is_bulk_edit = True unit.source_info.labels.add(*add_labels) changed = True if remove_labels: unit.source_info.is_bulk_edit = True unit.source_info.labels.remove(*remove_labels) changed = True if changed: updated += 1 if target_state != -1: component_units.filter(state__in=EDITABLE_STATES).exclude( state=target_state ).update(pending=True, state=target_state) for unit in component_units: if unit.translation.is_source: unit.is_bulk_edit = True update_source(Unit, unit) component.invalidate_stats_deep() return updated
def test_quoted_values(self): flags = Flags(r"""placeholders:"bar: \"value\"":'baz \'value\''""") self.assertEqual(flags.get_value("placeholders"), ['bar: "value"', "baz 'value'"]) self.assertEqual(flags.format(), r'''placeholders:"bar: \"value\"":"baz 'value'"''')
def test_set(self): flags = Flags() flags.set_value("variant", "Long string with \"quotes\" and 'quotes'.") self.assertEqual( flags.format(), r'''variant:"Long string with \"quotes\" and 'quotes'."''')
def flags(self): flags = Flags(super().flags) flags.remove("xml-text") return flags.format()