Skip to content

Commit

Permalink
Refactor --clean
Browse files Browse the repository at this point in the history
  • Loading branch information
Josef-Friedrich committed Jan 26, 2024
1 parent a456e0a commit 290c844
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 114 deletions.
7 changes: 1 addition & 6 deletions mscxyz/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -815,13 +815,8 @@ def list_styles(version: int) -> None:
for a in args.meta_set:
score.meta.set_field(destination_field=a[0], format_string=a[1])

# elif args.subcommand == "meta":
# score = Score(file)
# if __no_error(lxml.etree.XMLSyntaxError, score.errors):
# pre: dict[str, str] = score.meta.interface.export_to_dict()

if args.meta_clean:
score.meta.clean_metadata(fields_spec=args.meta_clean)
score.fields.clean(args.meta_clean)

if args.meta_json:
score.meta.export_json()
Expand Down
28 changes: 27 additions & 1 deletion mscxyz/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import typing
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Mapping, Union
from typing import Any, Iterator, Mapping, Sequence, Union

from mscxyz.meta import FormatStringNoFieldError, UnmatchedFormatStringError
from mscxyz.settings import DefaultArguments
Expand Down Expand Up @@ -42,6 +42,8 @@ class Field:
color: Color = "white"
"""The color of the field in the debug output."""

readonly: bool = False


class FieldsManager:
score: "Score"
Expand Down Expand Up @@ -202,57 +204,66 @@ class FieldsManager:
"for example ``2.03``, ``3.01`` or ``4.20``.",
attr_path="version",
verbosity=2,
readonly=True,
),
Field(
name="version_major",
description="The major MuseScore version, for example ``2``, ``3`` or ``4``.",
attr_path="version_major",
verbosity=2,
readonly=True,
),
Field(
name="path",
description="The absolute path of the MuseScore file, for example ``/home/xyz/score.mscz``.",
attr_path="path",
verbosity=2,
readonly=True,
),
Field(
name="backup_file",
description="The absolute path of the backup file. "
"The string ``_bak`` is appended to the file name before the extension.",
attr_path="backup_file",
verbosity=3,
readonly=True,
),
Field(
name="json_file",
description="The absolute path of the JSON file in which the metadata can be exported.",
attr_path="json_file",
verbosity=3,
readonly=True,
),
Field(
name="dirname",
description="The name of the containing directory of the MuseScore file, for "
"example: ``/home/xyz/score_files``.",
attr_path="dirname",
verbosity=2,
readonly=True,
),
Field(
name="filename",
description="The filename of the MuseScore file, for example:"
"``score.mscz``.",
attr_path="filename",
verbosity=2,
readonly=True,
),
Field(
name="basename",
description="The basename of the score file, for example: ``score``.",
attr_path="basename",
verbosity=2,
readonly=True,
),
Field(
name="extension",
description="The extension (``mscx`` or ``mscz``) of the score file.",
attr_path="extension",
verbosity=2,
readonly=True,
),
)

Expand All @@ -270,6 +281,9 @@ def __init__(self, score: "Score") -> None:
self.__fields_by_name[field.name] = field
self.pre = self.export_to_dict()

def __iter__(self) -> Iterator[Field]:
return iter(self.fields)

@property
def names(self) -> tuple[str, ...]:
return tuple(self.__fields_by_name.keys())
Expand Down Expand Up @@ -361,6 +375,18 @@ def distribute(self, source_fields: str, format_string: str) -> None:
self.set(field, value)
return

def clean(self, fields_spec: str) -> None:
fields: Sequence[str]
if fields_spec == "all":
fields = self.names
else:
fields = fields_spec.split(",")

for name in fields:
field = self.get_field(name)
if not field.readonly:
self.set(name, None)

def export_json(self) -> Path:
"""
Export the data as a JSON file.
Expand Down
38 changes: 10 additions & 28 deletions mscxyz/meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,6 @@ def __get_text(self, field: str) -> str | None:
return self.score.xml.get_text(element)

def __set_text(self, field: str, value: str | None) -> None:
if value is None:
return None
element: _Element = self.__get_element(field)
element.text = value

Expand Down Expand Up @@ -414,19 +412,8 @@ def work_title(self, value: str | None) -> None:
self.__set_text("workTitle", value)

def clean(self) -> None:
fields = (
"arranger",
"copyright",
"creationDate",
"movementNumber",
"platform",
"poet",
"source",
"translator",
"workNumber",
)
for field in fields:
setattr(self, field, "")
for field in self.fields:
setattr(self, field, None)


class Vbox:
Expand Down Expand Up @@ -642,6 +629,10 @@ def title(self) -> str | None:
def title(self, value: str | None) -> None:
self.__set_text("Title", value)

def clean(self) -> None:
for field in self.fields:
setattr(self, field, None)


class Combined:
"""Combines the metadata fields of the embedded ``metaTag`` and ``VBox`` elements."""
Expand Down Expand Up @@ -878,21 +869,12 @@ def set_field(self, destination_field: str, format_string: str) -> None:
field_value = tmep.parse(format_string, self.interface.export_to_dict())
setattr(self.interface, destination_field, field_value)

def clean_metadata(self, fields_spec: typing.Literal["all"] | str) -> None:
def clean(self) -> None:
"""
Clean the metadata fields of the object.
:param fields_spec: Specification of the fields to clean. It can be either
``all`` to clean all fields, or a comma-separated string specifying
individual fields.
Clean all metadata fields of the object.
"""
fields: list[str]
if fields_spec == "all":
fields = self.interface_read_write.fields
else:
fields = fields_spec.split(",")
for field in fields:
setattr(self.interface_read_write, field, "")
self.metatag.clean()
self.vbox.clean()

def delete_duplicates(self) -> None:
"""
Expand Down
6 changes: 6 additions & 0 deletions tests/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,12 @@ def execute(self) -> Cli:
self.__execute()
return self

def fields(self) -> FieldsManager:
self.__execute()
if self.__score is None:
raise Exception("No score object")
return self.__score.fields

def score(self) -> Score:
self.__execute()
if self.__score is None:
Expand Down
120 changes: 41 additions & 79 deletions tests/test_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@

import pytest

import mscxyz
import mscxyz.meta
from mscxyz import meta, supported_versions, utils
from mscxyz.meta import (
Interface,
Expand Down Expand Up @@ -256,64 +254,6 @@ def test_field_readonly_relpath_backup(self) -> None:
)


class TestClassInterface:
def setup_method(self) -> None:
self.fields: list[str] = [
"combined_composer",
"combined_lyricist",
"combined_subtitle",
"combined_title",
"metatag_arranger",
"metatag_audio_com_url",
"metatag_composer",
"metatag_copyright",
"metatag_creation_date",
"metatag_lyricist",
"metatag_movement_number",
"metatag_movement_title",
"metatag_msc_version",
"metatag_platform",
"metatag_poet",
"metatag_source",
"metatag_source_revision_id",
"metatag_subtitle",
"metatag_translator",
"metatag_work_number",
"metatag_work_title",
"readonly_abspath",
"readonly_basename",
"readonly_dirname",
"readonly_extension",
"readonly_filename",
"readonly_relpath",
"readonly_relpath_backup",
"vbox_composer",
"vbox_lyricist",
"vbox_subtitle",
"vbox_title",
]

self.tmp: str = helper.get_file("meta-all-values.mscx")
self.xml_tree = Score(self.tmp)
self.interface = Interface(self.xml_tree)

def test_static_method_get_all_fields(self) -> None:
assert Interface.get_all_fields() == self.fields

@pytest.mark.skip(reason="Test needs to be rewritten")
def test_get(self) -> None:
for field in self.fields:
assert getattr(self.interface, field), field

def test_set(self) -> None:
self.interface.vbox_title = "lol"
assert self.interface.vbox_title == "lol"

def test_exception(self) -> None:
with pytest.raises(mscxyz.meta.ReadOnlyFieldError):
self.interface.readonly_extension = "lol"


def get_meta_tag(filename: str, version: int) -> Metatag:
score = helper.get_score(filename, version)
return score.meta.metatag
Expand Down Expand Up @@ -351,7 +291,7 @@ def test_clean(self, version: int) -> None:
m.arranger = "A"
assert m.arranger == "A"
m.clean()
assert m.arranger == ""
assert m.arranger is None


def get_vbox(filename: str, version: int) -> Vbox:
Expand Down Expand Up @@ -457,31 +397,29 @@ def test_distribute_field_exception_unmatched(self) -> None:

class TestOptionClean:
def test_clean_all(self) -> None:
c = Cli("--clean-meta", "all").append_score("meta-all-values.mscz").execute()
i = c.post.meta.interface_read_write
for field in i.fields:
assert getattr(i, field) is None, field
f = Cli("--clean-meta", "all").append_score("meta-all-values.mscz").fields()
for field in f:
if not field.readonly:
assert f.get(field.name) is None, field

def test_clean_single_field(self) -> None:
c = (
f = (
Cli("--clean-meta", "vbox_title")
.append_score("meta-all-values.mscz")
.execute()
)
i = c.post.meta.interface_read_write
assert i.vbox_title is None, "vbox_title"
assert i.vbox_composer == "vbox_composer", "vbox_composer"
).fields()
assert f.get("vbox_title") is None, "vbox_title"
assert f.get("vbox_composer") == "vbox_composer", "vbox_composer"

def test_clean_some_fields(self) -> None:
c = (
f = (
Cli("--clean-meta", "vbox_title,vbox_composer")
.append_score("meta-all-values.mscz")
.execute()
)
i = c.post.meta.interface_read_write
assert i.vbox_title is None, "vbox_title"
assert i.vbox_composer is None, "vbox_composer"
assert i.vbox_subtitle == "vbox_subtitle", "vbox_subtitle"
).fields()
assert f.get("vbox_title") is None, "vbox_title"
assert f.get("vbox_composer") is None, "vbox_composer"
assert f.get("vbox_subtitle") == "vbox_subtitle", "vbox_subtitle"


class TestStdout:
Expand Down Expand Up @@ -817,9 +755,33 @@ def setup_method(self) -> None:
self.meta: Meta = helper.get_meta("meta-all-values.mscx")

def test_method_clean_metadata(self) -> None:
self.meta.interface.combined_lyricist = "test"
self.meta.clean_metadata("all")
assert self.meta.interface.combined_lyricist is None
self.meta.vbox.lyricist = "test"
self.meta.clean()
assert self.meta.vbox.title is None
assert self.meta.vbox.subtitle is None
assert self.meta.vbox.composer is None
assert self.meta.vbox.lyricist is None
assert self.meta.metatag.arranger is None
assert self.meta.metatag.audio_com_url is None
assert self.meta.metatag.composer is None
assert self.meta.metatag.copyright is None
assert self.meta.metatag.creation_date is None
assert self.meta.metatag.lyricist is None
assert self.meta.metatag.movement_number is None
assert self.meta.metatag.movement_title is None
assert self.meta.metatag.msc_version is None
assert self.meta.metatag.platform is None
assert self.meta.metatag.poet is None
assert self.meta.metatag.source is None
assert self.meta.metatag.source_revision_id is None
assert self.meta.metatag.subtitle is None
assert self.meta.metatag.translator is None
assert self.meta.metatag.work_number is None
assert self.meta.metatag.work_title is None
assert self.meta.title is None
assert self.meta.subtitle is None
assert self.meta.composer is None
assert self.meta.lyricist is None

def test_method_delete_duplicates(self) -> None:
self.meta.interface.combined_lyricist = "test"
Expand Down

0 comments on commit 290c844

Please sign in to comment.