diff --git a/picard/const/__init__.py b/picard/const/__init__.py index 3690b67e7..16b684951 100644 --- a/picard/const/__init__.py +++ b/picard/const/__init__.py @@ -185,3 +185,5 @@ DEFAULT_FILE_NAMING_FORMAT = "$if2(%albumartist%,%artist%)/\n" \ DEFAULT_NUMBERED_SCRIPT_NAME = N_("My script %d") DEFAULT_SCRIPT_NAME = N_("My script") DEFAULT_COVER_IMAGE_FILENAME = "cover" + +SCRIPT_VERSION = '1.1' diff --git a/picard/script/__init__.py b/picard/script/__init__.py index d8b8b8adf..c5285381e 100644 --- a/picard/script/__init__.py +++ b/picard/script/__init__.py @@ -48,6 +48,7 @@ from picard.config import get_config from picard.const import ( DEFAULT_FILE_NAMING_FORMAT, DEFAULT_SCRIPT_NAME, + SCRIPT_VERSION, ) from picard.script.functions import ( # noqa: F401 # pylint: disable=unused-import register_script_function, @@ -131,12 +132,12 @@ class PicardScript(): # Base class developed to support future tagging script class as possible replacement for currently used tuples in config.setting["list_of_scripts"]. TYPE = PicardScriptType.BASE - JSON_OUTPUT = {'title', 'script'} + JSON_OUTPUT = {'title', 'script', 'script_version'} # Don't automatically trigger changing the `script_last_updated` property when updating these properties. _last_updated_ignore_list = {'last_updated', 'readonly', 'deletable', 'id'} - def __init__(self, script='', title='', id=None, last_updated=None): + def __init__(self, script='', title='', id=None, last_updated=None, script_version=None): """Base class for Picard script objects Args: @@ -155,13 +156,17 @@ class PicardScript(): self.update_last_updated() else: self.last_updated = last_updated + if script_version is None or not script_version: + self.script_version = SCRIPT_VERSION + else: + self.script_version = script_version def _set_new_id(self): """Sets the ID of the script to a new system generated uuid. """ self.id = str(uuid.uuid4()) - def get_value(self, setting): + def __getitem__(self, setting): """A safe way of getting the value of the specified property setting, because it handles missing properties by returning None rather than raising an exception. @@ -223,7 +228,12 @@ class PicardScript(): """Create a copy of the current script object with updated title and last updated attributes. """ new_object = deepcopy(self) - new_object.update_script_setting(title=_("%s (Copy)") % self.title) + new_object.update_script_setting( + title=_("%s (Copy)") % self.title, + script_version=SCRIPT_VERSION, + readonly=False, + deletable=True + ) new_object._set_new_id() return new_object @@ -269,7 +279,7 @@ class FileNamingScript(PicardScript): """Picard file naming script class """ TYPE = PicardScriptType.FILENAMING - JSON_OUTPUT = {'title', 'script', 'author', 'description', 'license', 'version', 'last_updated'} + JSON_OUTPUT = {'title', 'script', 'author', 'description', 'license', 'version', 'last_updated', 'script_version'} def __init__( self, @@ -282,7 +292,8 @@ class FileNamingScript(PicardScript): description='', license='', version='', - last_updated=None + last_updated=None, + script_version=None ): """Creates a Picard file naming script object. @@ -298,7 +309,7 @@ class FileNamingScript(PicardScript): version (str): Identifies the version of the script. Defaults to ''. last_updated (str): The UTC date and time when the script was last updated. Defaults to current date/time. """ - super().__init__(script=script, title=title, id=id, last_updated=last_updated) + super().__init__(script=script, title=title, id=id, last_updated=last_updated, script_version=script_version) self.readonly = readonly # for presets self.deletable = deletable # Allow removal from list of scripts self.author = author @@ -306,12 +317,6 @@ class FileNamingScript(PicardScript): self.license = license self.version = version - def copy(self): - new_object = super().copy() - new_object.readonly = False - new_object.deletable = True - return new_object - def get_file_naming_script_presets(): """Generator of preset example file naming script objects. @@ -336,6 +341,7 @@ def get_file_naming_script_presets(): version="1.0", license=LICENSE, last_updated="2019-08-05 13:40:00 UTC", + script_version="1.0", ) yield FileNamingScript( @@ -350,6 +356,7 @@ def get_file_naming_script_presets(): version="1.0", license=LICENSE, last_updated="2021-04-12 21:30:00 UTC", + script_version="1.0", ) yield FileNamingScript( @@ -367,4 +374,5 @@ def get_file_naming_script_presets(): version="1.0", license=LICENSE, last_updated="2021-04-12 21:30:00 UTC", + script_version="1.0", ) diff --git a/picard/ui/scripteditor.py b/picard/ui/scripteditor.py index 1cbdec28e..b441665a8 100644 --- a/picard/ui/scripteditor.py +++ b/picard/ui/scripteditor.py @@ -375,7 +375,7 @@ class ScriptEditorPage(PicardDialog): for script_json in self.naming_scripts: script_item = FileNamingScript().create_from_json(script_json) - if script_item.get_value('title'): + if script_item['title']: self.ui.preset_naming_scripts.addItem(self.SCRIPT_TITLE_USER % script_item.title, script_item) for script_item in get_file_naming_script_presets(): @@ -404,7 +404,7 @@ class ScriptEditorPage(PicardDialog): """ selected_item = self.get_selected_item() self.update_combo_box_item(self.ui.preset_naming_scripts.currentIndex(), selected_item) - self.ui.script_title.setText(selected_item.get_value('title')) + self.ui.script_title.setText(selected_item['title']) def _insert_item(self, script_item): """Insert a new item into the script selection combo box and update the script list in the settings. @@ -471,7 +471,7 @@ class ScriptEditorPage(PicardDialog): script_item (FileNamingScript): Updated script information """ self.ui.preset_naming_scripts.setItemData(idx, script_item) - self.ui.preset_naming_scripts.setItemText(idx, self.SCRIPT_TITLE_USER % script_item.get_value('title')) + self.ui.preset_naming_scripts.setItemText(idx, self.SCRIPT_TITLE_USER % script_item['title']) def set_button_states(self, save_enabled=True): """Set the button states based on the readonly and deletable attributes of the currently selected @@ -660,7 +660,7 @@ class ScriptEditorPage(PicardDialog): except JSONDecodeError as error: self.output_file_error(FILE_ERROR_DECODE, filename, error.msg) return - if not (script_item.get_value('title') and script_item.get_value('script')): + if not (script_item['title'] and script_item['script']): self.output_file_error(FILE_ERROR_DECODE, filename, _('Invalid script package')) return else: @@ -811,12 +811,12 @@ class ScriptDetailsEditor(PicardDialog): self.ui.buttonBox.rejected.connect(self.close_window) self.ui.last_updated_now.clicked.connect(self.set_last_updated) - self.ui.script_title.setText(self.script_item.get_value('title')) - self.ui.script_author.setText(self.script_item.get_value('author')) - self.ui.script_version.setText(self.script_item.get_value('version')) - self.ui.script_last_updated.setText(self.script_item.get_value('last_updated')) - self.ui.script_license.setText(self.script_item.get_value('license')) - self.ui.script_description.setPlainText(self.script_item.get_value('description')) + self.ui.script_title.setText(self.script_item['title']) + self.ui.script_author.setText(self.script_item['author']) + self.ui.script_version.setText(self.script_item['version']) + self.ui.script_last_updated.setText(self.script_item['last_updated']) + self.ui.script_license.setText(self.script_item['license']) + self.ui.script_description.setPlainText(self.script_item['description']) self.ui.script_last_updated.setReadOnly(self.readonly) self.ui.script_author.setReadOnly(self.readonly) diff --git a/test/test_scriptclasses.py b/test/test_scriptclasses.py index 446882446..a9b1aa00a 100644 --- a/test/test_scriptclasses.py +++ b/test/test_scriptclasses.py @@ -52,97 +52,99 @@ class ScriptClassesTest(PicardTestCase): def test_script_object_1(self): # Check initial loaded values. - test_script = PicardScript(title='Script 1', script='Script text', id='12345', last_updated='2021-04-26') + test_script = PicardScript(title='Script 1', script='Script text', id='12345', last_updated='2021-04-26', script_version='1.0') self.assertEqual(test_script.id, '12345') - self.assertEqual(test_script.get_value('id'), '12345') + self.assertEqual(test_script['id'], '12345') self.assertEqual(test_script.last_updated, '2021-04-26') - self.assertEqual(test_script.get_value('last_updated'), '2021-04-26') - self.assertEqual(test_script.to_json(), '{"script": "Script text", "title": "Script 1"}') + self.assertEqual(test_script['last_updated'], '2021-04-26') + self.assertEqual(test_script.to_json(), '{"script": "Script text", "script_version": "1.0", "title": "Script 1"}') def test_script_object_2(self): # Check updating values directly so as not to modify `last_updated`. test_script = PicardScript(title='Script 1', script='Script text', id='12345', last_updated='2021-04-26') test_script.id = '54321' self.assertEqual(test_script.id, '54321') - self.assertEqual(test_script.get_value('id'), '54321') + self.assertEqual(test_script['id'], '54321') self.assertEqual(test_script.last_updated, '2021-04-26') - self.assertEqual(test_script.get_value('last_updated'), '2021-04-26') + self.assertEqual(test_script['last_updated'], '2021-04-26') test_script.title = 'Updated Script 1' self.assertEqual(test_script.title, 'Updated Script 1') - self.assertEqual(test_script.title, 'Updated Script 1') - self.assertEqual(test_script.get_value('title'), 'Updated Script 1') - self.assertEqual(test_script.get_value('last_updated'), '2021-04-26') + self.assertEqual(test_script['title'], 'Updated Script 1') + self.assertEqual(test_script['last_updated'], '2021-04-26') def test_script_object_3(self): # Check updating values that are ignored from modifying `last_updated`. test_script = PicardScript(title='Script 1', script='Script text', id='12345', last_updated='2021-04-26') test_script.update_script_setting(id='54321') self.assertEqual(test_script.id, '54321') - self.assertEqual(test_script.get_value('id'), '54321') + self.assertEqual(test_script['id'], '54321') self.assertEqual(test_script.last_updated, '2021-04-26') - self.assertEqual(test_script.get_value('last_updated'), '2021-04-26') + self.assertEqual(test_script['last_updated'], '2021-04-26') def test_script_object_4(self): # Check updating values that modify `last_updated`. test_script = PicardScript(title='Script 1', script='Script text', id='12345', last_updated='2021-04-26') test_script.update_script_setting(title='Updated Script 1') self.assertEqual(test_script.title, 'Updated Script 1') - self.assertEqual(test_script.get_value('title'), 'Updated Script 1') + self.assertEqual(test_script['title'], 'Updated Script 1') self.assertEqual(test_script.last_updated, '2020-01-02 12:34:56 UTC') - self.assertEqual(test_script.get_value('last_updated'), '2020-01-02 12:34:56 UTC') + self.assertEqual(test_script['last_updated'], '2020-01-02 12:34:56 UTC') def test_script_object_5(self): # Check updating values from JSON that modify `last_updated`. test_script = PicardScript(title='Script 1', script='Script text', id='12345', last_updated='2021-04-26') test_script.update_from_json('{"script": "Updated script"}') self.assertEqual(test_script.script, 'Updated script') - self.assertEqual(test_script.get_value('script'), 'Updated script') + self.assertEqual(test_script['script'], 'Updated script') self.assertEqual(test_script.last_updated, '2020-01-02 12:34:56 UTC') - self.assertEqual(test_script.get_value('last_updated'), '2020-01-02 12:34:56 UTC') + self.assertEqual(test_script['last_updated'], '2020-01-02 12:34:56 UTC') def test_script_object_6(self): # Test that extra (unknown) settings are ignored during updating - test_script = PicardScript(title='Script 1', script='Script text', id='12345', last_updated='2021-04-26') + test_script = PicardScript(title='Script 1', script='Script text', id='12345', last_updated='2021-04-26', script_version='1.0') test_script.update_script_setting(description='Updated description') - self.assertEqual(test_script.get_value('last_updated'), '2021-04-26') - self.assertEqual(test_script.to_json(), '{"script": "Script text", "title": "Script 1"}') + self.assertEqual(test_script['last_updated'], '2021-04-26') + self.assertEqual(test_script.to_json(), '{"script": "Script text", "script_version": "1.0", "title": "Script 1"}') with self.assertRaises(AttributeError): print(test_script.description) def test_script_object_7(self): # Test that extra (unknown) settings are ignored during updating from JSON string - test_script = PicardScript(title='Script 1', script='Script text', id='12345', last_updated='2021-04-26') + test_script = PicardScript(title='Script 1', script='Script text', id='12345', last_updated='2021-04-26', script_version='1.0') test_script.update_from_json('{"description": "Updated description"}') - self.assertEqual(test_script.get_value('last_updated'), '2021-04-26') - self.assertEqual(test_script.to_json(), '{"script": "Script text", "title": "Script 1"}') + self.assertEqual(test_script['last_updated'], '2021-04-26') + self.assertEqual(test_script.to_json(), '{"script": "Script text", "script_version": "1.0", "title": "Script 1"}') with self.assertRaises(AttributeError): print(test_script.description) def test_script_object_8(self): # Test that requested unknown settings return None test_script = PicardScript(title='Script 1', script='Script text', id='12345', last_updated='2021-04-26') - self.assertEqual(test_script.get_value('unknown_setting'), None) + self.assertEqual(test_script['unknown_setting'], None) def test_script_object_9(self): # Test that an exception is raised when creating or updating using an invalid JSON string with self.assertRaises(JSONDecodeError): test_script = PicardScript().create_from_json('Not a JSON string') - test_script = PicardScript(title='Script 1', script='Script text', id='12345', last_updated='2021-04-26') + test_script = PicardScript(title='Script 1', script='Script text', id='12345', last_updated='2021-04-26', script_version='1.0') with self.assertRaises(JSONDecodeError): test_script.update_from_json('Not a JSON string') def test_naming_script_object_1(self): # Check initial loaded values. - test_script = FileNamingScript(title='Script 1', script='Script text', id='12345', last_updated='2021-04-26', description='Script description', author='Script author') + test_script = FileNamingScript( + title='Script 1', script='Script text', id='12345', last_updated='2021-04-26', + description='Script description', author='Script author', script_version='1.0' + ) self.assertEqual(test_script.id, '12345') - self.assertEqual(test_script.get_value('id'), '12345') + self.assertEqual(test_script['id'], '12345') self.assertEqual(test_script.last_updated, '2021-04-26') - self.assertEqual(test_script.get_value('last_updated'), '2021-04-26') + self.assertEqual(test_script['last_updated'], '2021-04-26') self.assertEqual(test_script.script, 'Script text') - self.assertEqual(test_script.get_value('script'), 'Script text') + self.assertEqual(test_script['script'], 'Script text') self.assertEqual(test_script.author, 'Script author') - self.assertEqual(test_script.get_value('author'), 'Script author') + self.assertEqual(test_script['author'], 'Script author') self.assertEqual(test_script.to_json(), '{' '"author": "Script author", ' @@ -150,6 +152,7 @@ class ScriptClassesTest(PicardTestCase): '"last_updated": "2021-04-26", ' '"license": "", ' '"script": "Script text", ' + '"script_version": "1.0", ' '"title": "Script 1", ' '"version": ""' '}' @@ -161,6 +164,7 @@ class ScriptClassesTest(PicardTestCase): ' "last_updated": "2021-04-26",\n' ' "license": "",\n' ' "script": "Script text",\n' + ' "script_version": "1.0",\n' ' "title": "Script 1",\n' ' "version": ""\n' '}'