Skip to content
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
Empty file.
8 changes: 4 additions & 4 deletions openedx_tagging/core/tagging/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from django.db import transaction
from django.db.models import F, QuerySet
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext as _

from .models import ObjectTag, Tag, Taxonomy

Expand Down Expand Up @@ -196,11 +196,11 @@ def _check_new_tag_count(new_tag_count: int) -> None:

if current_count + new_tag_count > 100:
raise ValueError(
_(f"Cannot add more than 100 tags to ({object_id}).")
_("Cannot add more than 100 tags to ({object_id}).").format(object_id=object_id)
)

if not isinstance(tags, list):
raise ValueError(_(f"Tags must be a list, not {type(tags).__name__}."))
raise ValueError(_("Tags must be a list, not {type}.").format(type=type(tags).__name__))

ObjectTagClass = object_tag_class
taxonomy = taxonomy.cast() # Make sure we're using the right subclass. This is a no-op if we are already.
Expand All @@ -209,7 +209,7 @@ def _check_new_tag_count(new_tag_count: int) -> None:
_check_new_tag_count(len(tags))

if not taxonomy.allow_multiple and len(tags) > 1:
raise ValueError(_(f"Taxonomy ({taxonomy.name}) only allows one tag per object."))
raise ValueError(_("Taxonomy ({name}) only allows one tag per object.").format(name=taxonomy.name))

current_tags = list(
ObjectTagClass.objects.filter(taxonomy=taxonomy, object_id=object_id)
Expand Down
3 changes: 3 additions & 0 deletions openedx_tagging/core/tagging/import_export/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
"""
Externally-facing import/export classes.
"""
from .parsers import ParserFormat
38 changes: 17 additions & 21 deletions openedx_tagging/core/tagging/import_export/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def __init__(self, taxonomy: Taxonomy, tag, index: int):
self.index = index

def __repr__(self) -> str:
return str(_(f"Action {self.name} (index={self.index},id={self.tag.id})"))
return str(_("Action {name} (index={index},id={id})").format(name=self.name, index=self.index, id=self.tag.id))

def __str__(self) -> str:
return self.__repr__()
Expand Down Expand Up @@ -104,11 +104,10 @@ def _validate_parent(self, indexed_actions) -> ImportActionError | None:
):
return ImportActionError(
action=self,
tag_id=self.tag.id,
message=_(
f"Unknown parent tag ({self.tag.parent_id}). "
"Unknown parent tag ({parent_id}). "
"You need to add parent before the child in your file."
),
).format(parent_id=self.tag.parent_id),
)
return None

Expand All @@ -122,10 +121,9 @@ def _validate_value(self, indexed_actions) -> ImportActionError | None:
taxonomy_tag = self.taxonomy.tag_set.get(value=self.tag.value)
return ImportActionError(
action=self,
tag_id=self.tag.id,
message=_(
f"Duplicated tag value with tag in database (external_id={taxonomy_tag.external_id})."
),
"Duplicated tag value with tag in database (external_id={external_id})."
).format(external_id=taxonomy_tag.external_id)
)
except Tag.DoesNotExist:
# Validates value duplication on create actions
Expand All @@ -148,7 +146,6 @@ def _validate_value(self, indexed_actions) -> ImportActionError | None:
if action:
return ImportActionConflict(
action=self,
tag_id=self.tag.id,
conflict_action_index=action.index,
message=_("Duplicated tag value."),
)
Expand All @@ -175,9 +172,9 @@ def __str__(self) -> str:
return str(
_(
"Create a new tag with values "
f"(external_id={self.tag.id}, value={self.tag.value}, "
f"parent_id={self.tag.parent_id})."
)
"(external_id={external_id}, value={value}, "
"parent_id={parent_id})."
).format(external_id=self.tag.id, value=self.tag.value, parent_id=self.tag.parent_id)
)

@classmethod
Expand All @@ -199,7 +196,6 @@ def _validate_id(self, indexed_actions) -> ImportActionError | None:
if action:
return ImportActionConflict(
action=self,
tag_id=self.tag.id,
conflict_action_index=action.index,
message=_("Duplicated external_id tag."),
)
Expand Down Expand Up @@ -263,13 +259,13 @@ def __str__(self) -> str:
if not taxonomy_tag.parent:
from_str = _("from empty parent")
else:
from_str = _(f"from parent (external_id={taxonomy_tag.parent.external_id})")
from_str = _("from parent (external_id={external_id})").format(external_id=taxonomy_tag.parent.external_id)

return str(
_(
f"Update the parent of tag (external_id={taxonomy_tag.external_id}) "
f"{from_str} to parent (external_id={self.tag.parent_id})."
)
"Update the parent of tag (external_id={external_id}) "
"{from_str} to parent (external_id={parent_id})."
).format(external_id=taxonomy_tag.external_id, from_str=from_str, parent_id=self.tag.parent_id)
)

@classmethod
Expand Down Expand Up @@ -329,9 +325,9 @@ def __str__(self) -> str:
taxonomy_tag = self._get_tag()
return str(
_(
f"Rename tag value of tag (external_id={taxonomy_tag.external_id}) "
f"from '{taxonomy_tag.value}' to '{self.tag.value}'"
)
"Rename tag value of tag (external_id={external_id}) "
"from '{from_value}' to '{to_value}'"
).format(external_id=taxonomy_tag.external_id, from_value=taxonomy_tag.value, to_value=self.tag.value)
)

@classmethod
Expand Down Expand Up @@ -378,7 +374,7 @@ class DeleteTag(ImportAction):

def __str__(self) -> str:
taxonomy_tag = self._get_tag()
return str(_(f"Delete tag (external_id={taxonomy_tag.external_id})"))
return str(_("Delete tag (external_id={external_id})").format(external_id=taxonomy_tag.external_id))

name = "delete"

Expand Down Expand Up @@ -415,7 +411,7 @@ class WithoutChanges(ImportAction):
name = "without_changes"

def __str__(self) -> str:
return str(_(f"No changes needed for tag (external_id={self.tag.id})"))
return str(_("No changes needed for tag (external_id={external_id})").format(external_id=self.tag.id))

@classmethod
def applies_for(cls, taxonomy: Taxonomy, tag) -> bool:
Expand Down
15 changes: 9 additions & 6 deletions openedx_tagging/core/tagging/import_export/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@

from io import BytesIO

from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext as _

from ..models import TagImportTask, TagImportTaskState, Taxonomy
from .exceptions import TagImportError
from .import_plan import TagImportPlan, TagImportTask
from .parsers import ParserFormat, get_parser

Expand Down Expand Up @@ -115,7 +116,7 @@ def import_tags(
tag_import_plan.execute(task)
task.end_success()
return True
except Exception as exception:
except (TagImportError, ValueError) as exception:
# Log any exception
task.log_exception(exception)
return False
Expand Down Expand Up @@ -159,8 +160,10 @@ def _check_unique_import_task(taxonomy: Taxonomy) -> bool:
if not last_task:
return True
return (
last_task.status == TagImportTaskState.SUCCESS.value
or last_task.status == TagImportTaskState.ERROR.value
last_task.status in {
TagImportTaskState.SUCCESS.value,
TagImportTaskState.ERROR.value
}
)


Expand Down Expand Up @@ -189,6 +192,6 @@ def _import_export_validations(taxonomy: Taxonomy):
if taxonomy.system_defined:
raise ValueError(
_(
f"Invalid taxonomy ({taxonomy.id}): You cannot import/export a system-defined taxonomy."
)
"Invalid taxonomy ({id}): You cannot import/export a system-defined taxonomy."
).format(id=taxonomy.id)
)
51 changes: 28 additions & 23 deletions openedx_tagging/core/tagging/import_export/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ class TagImportError(Exception):
Base exception for import
"""

def __init__(self, message: str = "", **kargs):
def __init__(self, message: str = ""):
super().__init__()
self.message = message
super().__init__(message, **kargs)

def __str__(self):
return str(self.message)
Expand All @@ -32,21 +32,21 @@ class TagParserError(TagImportError):
Base exception for parsers
"""

def __init__(self, tag: dict | None, **kargs):
def __init__(self, tag: dict | None, **kargs): # pylint: disable=unused-argument
super().__init__()
self.tag = tag
self.message = _(f"Import parser error on {tag}")
self.message = _("Import parser error on {tag}").format(tag=tag)


class ImportActionError(TagImportError):
"""
Base exception for actions
"""

def __init__(self, action: ImportAction, tag_id: str, message: str, **kargs):
def __init__(self, action: ImportAction, message: str, **kargs):
super().__init__(**kargs)
self.message = _(
f"Action error in '{action.name}' (#{action.index}): {message}"
)
"Action error in '{name}' (#{index}): {message}"
).format(name=action.name, index=action.index, message=message)


class ImportActionConflict(ImportActionError):
Expand All @@ -57,14 +57,19 @@ class ImportActionConflict(ImportActionError):
def __init__(
self,
action: ImportAction,
tag_id: str,
conflict_action_index: int,
message: str,
**kargs,
):
super().__init__(action, message, **kargs)
self.message = _(
f"Conflict with '{action.name}' (#{action.index}) "
f"and action #{conflict_action_index}: {message}"
"Conflict with '{action_name}' (#{action_index}) "
"and action #{conflict_action_index}: {message}"
).format(
action_name=action.name,
action_index=action.index,
conflict_action_index=conflict_action_index,
message=message,
)


Expand All @@ -73,36 +78,36 @@ class InvalidFormat(TagParserError):
Exception used when there is an error with the format
"""

def __init__(self, tag: dict | None, format: str, message: str, **kargs):
super().__init__(tag)
self.message = _(f"Invalid '{format}' format: {message}")
def __init__(self, tag: dict | None, input_format: str, message: str, **kargs):
super().__init__(tag, **kargs)
self.message = _("Invalid '{format}' format: {message}").format(format=input_format, message=message)


class FieldJSONError(TagParserError):
"""
Exception used when missing a required field on the .json
"""

def __init__(self, tag: dict | None, field, **kargs):
super().__init__(tag)
self.message = _(f"Missing '{field}' field on {tag}")
def __init__(self, tag: dict | None, field: str, **kargs):
super().__init__(tag, **kargs)
self.message = _("Missing '{field}' field on {tag}").format(field=field, tag=tag)


class EmptyJSONField(TagParserError):
"""
Exception used when a required field is empty on the .json
"""

def __init__(self, tag, field, **kargs):
self.tag = tag
self.message = _(f"Empty '{field}' field on {tag}")
def __init__(self, tag: dict | None, field: str, **kargs):
super().__init__(tag, **kargs)
self.message = _("Empty '{field}' field on {tag}").format(field=field, tag=tag)


class EmptyCSVField(TagParserError):
"""
Exception used when a required field is empty on the .csv
"""

def __init__(self, tag, field, row, **kargs):
self.tag = tag
self.message = _(f"Empty '{field}' field on the row {row}")
def __init__(self, tag: dict | None, field: str, row: int, **kargs):
super().__init__(tag, **kargs)
self.message = _("Empty '{field}' field on the row {row}").format(field=field, row=row)
10 changes: 5 additions & 5 deletions openedx_tagging/core/tagging/import_export/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,13 +212,13 @@ def _load_data(cls, file: BytesIO) -> tuple[list[dict], list[TagParserError]]:
tags_data = json.load(file)
except json.JSONDecodeError as error:
return [], [
InvalidFormat(tag=None, format=cls.format.value, message=str(error))
InvalidFormat(tag=None, input_format=cls.format.value, message=str(error))
]
if "tags" not in tags_data:
return [], [
InvalidFormat(
tag=None,
format=cls.format.value,
input_format=cls.format.value,
message=_("Missing 'tags' field on the .json file"),
)
]
Expand Down Expand Up @@ -298,8 +298,8 @@ def _verify_header(cls, header_fields: list[str]) -> list[TagParserError]:
errors.append(
InvalidFormat(
tag=None,
format=cls.format.value,
message=_(f"Missing '{req_field}' field on CSV headers"),
input_format=cls.format.value,
message=_("Missing '{req_field}' field on CSV headers").format(req_field=req_field),
)
)
return errors
Expand All @@ -319,4 +319,4 @@ def get_parser(parser_format: ParserFormat) -> type[Parser]:
if parser_format == parser.format:
return parser

raise ValueError(_(f"Parser not found for format {parser_format}"))
raise ValueError(_("Parser not found for format {parser_format}").format(parser_format=parser_format))
Loading