Commit 8adc47b8 authored by Samuel GAIST's avatar Samuel GAIST
Browse files

[editor] Refactored application settings management

Until now, only the prefix path was passed around. However.
there are cases where more information is needed like for
example the current user when creating new assets.

This commit also fixes the parameter handling of the editor
command which was duplicating what was already done by
beat.cmdline.
parent 061ce41e
......@@ -40,7 +40,6 @@ from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import QCoreApplication
from beat.cmdline.click_helper import AliasedGroup
from beat.cmdline.config import Configuration
from beat.cmdline.decorators import raise_on_error
from beat.cmdline.decorators import verbosity_option
......@@ -63,27 +62,17 @@ Example:
@with_plugins(pkg_resources.iter_entry_points("beat.editor.cli"))
@click.group(cls=AliasedGroup)
@click.option(
"--prefix",
"-p",
help="Overrides the prefix of your local data. If not set use the value from your RC file [default: %(prefix)s]",
type=click.STRING,
)
@click.option(
"--cache",
"-c",
help="Overrides the cache prefix. If not set, use the value from your RC file, otherwise defaults to `<prefix>/%(cache)s'",
type=click.STRING,
)
@verbosity_option()
@click.pass_context
def editor(ctx, prefix, cache):
def editor(ctx):
"""beat.editor commands."""
ctx.meta["prefix"] = prefix
ctx.meta["cache"] = cache
config = ctx.meta["config"]
config.set("prefix", os.path.abspath(config.path))
global logger
logger = setup_logger("beat.editor", ctx.meta["verbosity"])
logger.info("BEAT prefix set to `%s'", ctx.meta["config"].path)
QCoreApplication.setApplicationName("beat.editor")
QCoreApplication.setOrganizationName("Idiap")
......@@ -95,18 +84,12 @@ def editor(ctx, prefix, cache):
@click.pass_context
@raise_on_error
def start(ctx):
"""Start the beat editor
"""
config = Configuration(ctx.meta)
global logger
logger.info("BEAT prefix set to `%s'", config.path)
logger.info("BEAT cache set to `%s'", config.cache)
"""Start the beat editor"""
app = QApplication(sys.argv)
mainwindow = MainWindow()
mainwindow.set_prefix_root(os.path.abspath(config.path))
mainwindow.set_context(ctx)
mainwindow.show()
return app.exec_()
......@@ -125,22 +108,17 @@ Example:
@click.pass_context
@raise_on_error
def edit(ctx, asset_type, asset_name):
"""Edit one specific asset
"""
config = Configuration(ctx.meta)
global logger
logger.info("BEAT prefix set to `%s'", config.path)
logger.info("BEAT cache set to `%s'", config.cache)
prefix_root = os.path.abspath(config.path)
"""Edit one specific asset"""
app = QApplication(sys.argv)
asset_widget = AssetWidget()
asset_widget.set_prefix_root(prefix_root)
asset_widget.set_context(ctx)
asset_path = os.path.join(
prefix_root, AssetType.path(AssetType[asset_type.upper()]), asset_name + ".json"
ctx.meta["config"].path,
AssetType.path(AssetType[asset_type.upper()]),
asset_name + ".json",
)
asset_widget.load_json(asset_path)
......
......@@ -32,6 +32,10 @@ import subprocess
import shutil
import importlib
from collections import namedtuple
from beat.cmdline.config import Configuration
from ..backend.assetmodel import AssetType
if sys.platform == "darwin":
......@@ -75,3 +79,13 @@ def test_prefix():
# Only needed once because that prefix is generated on call of the
# resource_filename of beat.core.test
pass
@pytest.fixture(scope="module")
def beat_context(test_prefix):
Context = namedtuple("Context", ["meta"])
context = Context(meta={"--user": "user", "--prefix": test_prefix})
config = Configuration(context.meta)
context.meta["config"] = config
return context
......@@ -69,10 +69,12 @@ class TestAssetWidget:
def get_asset_path(self, prefix, asset_type, asset_name):
return os.path.join(prefix, AssetType.path(asset_type), asset_name + ".json")
def test_matching_editor(self, qtbot, test_prefix, asset_type_prefix_entry_map):
def test_matching_editor(
self, qtbot, test_prefix, beat_context, asset_type_prefix_entry_map
):
asset_widget = AssetWidget()
qtbot.addWidget(asset_widget)
asset_widget.set_prefix_root(test_prefix)
asset_widget.set_context(beat_context)
assert isinstance(asset_widget.current_editor, PlaceholderEditor)
......@@ -86,10 +88,12 @@ class TestAssetWidget:
)
assert isinstance(asset_widget.current_editor, editor_type)
def test_dirty(self, qtbot, monkeypatch, test_prefix, asset_type_prefix_entry_map):
def test_dirty(
self, qtbot, monkeypatch, test_prefix, beat_context, asset_type_prefix_entry_map
):
asset_widget = AssetWidget()
qtbot.addWidget(asset_widget)
asset_widget.set_prefix_root(test_prefix)
asset_widget.set_context(beat_context)
description = "dummy description"
......@@ -120,10 +124,12 @@ class TestAssetWidget:
description_key = "#" + description_key
assert dumped_json[description_key] == description
def test_save(self, qtbot, monkeypatch, test_prefix, asset_type_prefix_entry_map):
def test_save(
self, qtbot, monkeypatch, test_prefix, beat_context, asset_type_prefix_entry_map
):
asset_widget = AssetWidget()
qtbot.addWidget(asset_widget)
asset_widget.set_prefix_root(test_prefix)
asset_widget.set_context(beat_context)
description = "dummy description"
......
......@@ -322,7 +322,7 @@ class TestDatabaseWidget:
class TestDatabaseEditor:
"""Test that the database editor works correctly"""
def test_load_and_dump(self, qtbot, test_prefix, test_database):
def test_load_and_dump(self, qtbot, beat_context, test_prefix, test_database):
asset_model = AssetModel()
asset_model.asset_type = AssetType.DATABASE
asset_model.prefix_path = test_prefix
......@@ -330,7 +330,7 @@ class TestDatabaseEditor:
with open(asset_model.json_path(test_database), "rt") as json_file:
json_data = json.load(json_file)
editor = DatabaseEditor()
editor.setPrefixPath(test_prefix)
editor.set_context(beat_context)
editor.load_json(json_data)
assert editor.dump_json() == json_data
......@@ -225,10 +225,10 @@ class TestDataformatEditor:
{"array_of_object": [0, 0, {"test1": DEFAULT_TYPE}]},
],
)
def test_load_and_dump_data(self, qtbot, monkeypatch, test_prefix, reference_json):
def test_load_and_dump_data(self, qtbot, monkeypatch, beat_context, reference_json):
editor = DataformatEditor()
qtbot.addWidget(editor)
editor.setPrefixPath(test_prefix)
editor.set_context(beat_context)
editor.load_json(reference_json)
......@@ -236,10 +236,10 @@ class TestDataformatEditor:
validated, errors = editor.is_valid()
assert validated, errors
def test_add(self, qtbot, monkeypatch, test_prefix, dataformat_model):
def test_add(self, qtbot, monkeypatch, beat_context, dataformat_model):
editor = DataformatEditor()
qtbot.addWidget(editor)
editor.setPrefixPath(test_prefix)
editor.set_context(beat_context)
model_index = dataformat_model.index(0, 0)
value = model_index.data()
......
......@@ -30,22 +30,22 @@ from ..widgets.libraryeditor import LibraryEditor
class TestLibraryEditor:
"""Test that the mock editor works correctly"""
def test_load_and_dump(self, qtbot, test_prefix):
def test_load_and_dump(self, qtbot, beat_context):
reference_json = {"description": "test", "uses": {}}
editor = LibraryEditor()
qtbot.addWidget(editor)
editor.setPrefixPath(test_prefix)
editor.set_context(beat_context)
editor.load_json(reference_json)
assert editor.dump_json() == reference_json
assert editor.is_valid()
def test_load_and_dump_wrong(self, qtbot, test_prefix):
def test_load_and_dump_wrong(self, qtbot, beat_context):
faulty_json = {"description": "test", "uses": {"alias": "test/dummy/1"}}
reference_json = {"description": "test", "uses": {}}
editor = LibraryEditor()
qtbot.addWidget(editor)
editor.setPrefixPath(test_prefix)
editor.set_context(beat_context)
editor.load_json(faulty_json)
assert editor.dump_json() == reference_json
......
......@@ -118,7 +118,7 @@ class TestProtocolTemplateEditor:
assert editor.dump_json() == json_data
def test_add_set(self, qtbot, monkeypatch, test_prefix, reference_pt_json):
def test_add_set(self, qtbot, monkeypatch, beat_context, reference_pt_json):
edited_json = copy.deepcopy(reference_pt_json)
edited_json["schema_version"] = 1
edited_json["sets"].append(
......@@ -130,7 +130,7 @@ class TestProtocolTemplateEditor:
)
editor = ProtocolTemplateEditor()
editor.setPrefixPath(test_prefix)
editor.set_context(beat_context)
editor.load_json(reference_pt_json)
qtbot.addWidget(editor)
......@@ -138,13 +138,13 @@ class TestProtocolTemplateEditor:
assert editor.dump_json() == edited_json
def test_remove_set(self, qtbot, test_prefix, reference_pt_json):
def test_remove_set(self, qtbot, beat_context, reference_pt_json):
edited_json = copy.deepcopy(reference_pt_json)
edited_json["schema_version"] = 1
edited_json["sets"] = []
editor = ProtocolTemplateEditor()
editor.setPrefixPath(test_prefix)
editor.set_context(beat_context)
editor.load_json(reference_pt_json)
qtbot.addWidget(editor)
......
......@@ -119,9 +119,11 @@ class AssetBrowser(QWidget):
if self.filesystem_model.type(source_index).lower() == "json file":
self.json_selected.emit(self.filesystem_model.filePath(source_index))
def set_prefix_root(self, prefix_root_path):
def set_context(self, context):
"""Sets the root path of the prefix to edit"""
prefix_root_path = context.meta["config"].path
self.filesystem_model.setRootPath(prefix_root_path)
root_index = self.filesystem_model.index(prefix_root_path)
self.view.setRootIndex(self.proxy_model.mapFromSource(root_index))
......@@ -39,6 +39,7 @@ from PyQt5.QtWidgets import QStackedWidget
from PyQt5.QtWidgets import QMessageBox
from ..backend.assetmodel import AssetType
from ..utils import frozen
from .editor import PlaceholderEditor
from .algorithmeditor import AlgorithmEditor
......@@ -109,6 +110,7 @@ class FileBlocker:
self.watcher.addPath(self.path)
@frozen
class AssetWidget(QWidget):
"""
This widget will show the asset specific editor and the JSON view of it.
......@@ -122,6 +124,7 @@ class AssetWidget(QWidget):
super(AssetWidget, self).__init__(parent)
self.context = None
self.current_json = None
self.watcher = QFileSystemWatcher()
self.update_timer = QTimer()
......@@ -166,6 +169,12 @@ class AssetWidget(QWidget):
return self.editors.currentWidget()
@property
def prefix_root_path(self):
"""Returns the prefix root path"""
return self.context.meta["config"].path
def __asset_type_for_path(self, path):
"""Returns the AssetType matching the path given
......@@ -173,6 +182,7 @@ class AssetWidget(QWidget):
:return: AssetType matching the path given
"""
asset_path = path[len(self.prefix_root_path) :] # noqa
asset_str = asset_path.split("/")[1]
return AssetType.from_path(asset_str)
......@@ -246,12 +256,13 @@ class AssetWidget(QWidget):
self.save_button.setEnabled(False)
def set_prefix_root(self, prefix_root_path):
"""Sets the root path of the prefix to edit"""
def set_context(self, context):
"""Sets the BEAT context"""
self.context = context
self.prefix_root_path = prefix_root_path
for i in range(0, self.editors.count()):
self.editors.widget(i).prefix_path = prefix_root_path
self.editors.widget(i).set_context(context)
def load_json(self, file_path):
""" Load the content of the file given in parameter
......
......@@ -753,8 +753,8 @@ class DatabaseEditor(AbstractAssetEditor):
self.layout().addWidget(self.stacked_widget, 1)
self.database_widget.dataChanged.connect(self.dataChanged)
self.prefixPathChanged.connect(
lambda path: self.database_widget.setPrefixPath(path)
self.contextChanged.connect(
lambda: self.database_widget.setPrefixPath(self.prefix_path)
)
def _load_json(self, json_object):
......
......@@ -598,8 +598,8 @@ class DataformatEditor(AbstractAssetEditor):
lambda: self.__add_entry([0, default_object_dataformat()])
)
self.prefixPathChanged.connect(
lambda path: self.dataformat_model.setPrefixPath(path)
self.contextChanged.connect(
lambda: self.dataformat_model.setPrefixPath(self.prefix_path)
)
def __add_entry(self, content):
......
......@@ -23,7 +23,6 @@
# #
###############################################################################
from ..backend.assetmodel import AssetType
import simplejson as json
from PyQt5.QtCore import pyqtSignal
......@@ -36,12 +35,14 @@ from PyQt5.QtWidgets import QLineEdit
from PyQt5.QtWidgets import QWidget
from PyQt5.QtWidgets import QVBoxLayout
from ..backend.assetmodel import AssetType
class AbstractAssetEditor(QWidget):
"""Base class for BEAT asset editors"""
dataChanged = pyqtSignal()
prefixPathChanged = pyqtSignal(str)
contextChanged = pyqtSignal()
def __init__(self, asset_type, parent=None):
"""Constructor
......@@ -50,11 +51,14 @@ class AbstractAssetEditor(QWidget):
:param parent QWidget: parent of this widget
"""
if not isinstance(asset_type, AssetType):
raise RuntimeError("Invalid parameter")
super(AbstractAssetEditor, self).__init__(parent)
self.context = None
self.asset_type = asset_type
self.schema_version = None
self._prefix_path = None
self.clear_dirty()
self.title_label = QLabel(self.tr("Unknown"))
......@@ -136,25 +140,19 @@ class AbstractAssetEditor(QWidget):
self.title_label.setText(title)
def setPrefixPath(self, prefix):
"""Set the prefix root path
Default: does nothing
def prefixPath(self):
return self.context.meta["config"].path
:param prefix str: prefix path
"""
if self._prefix_path == prefix:
return
prefix_path = pyqtProperty(str, fget=prefixPath)
self._prefix_path = prefix
self.prefixPathChanged.emit(prefix)
def set_context(self, context):
"""Sets the BEAT context"""
def prefixPath(self):
return self._prefix_path
if self.context == context:
return
prefix_path = pyqtProperty(
str, fget=prefixPath, fset=setPrefixPath, notify=prefixPathChanged
)
self.context = context
self.contextChanged.emit()
def load_json(self, json_object):
"""Load the json object passed as parameter"""
......
......@@ -53,8 +53,8 @@ class LibraryEditor(AbstractAssetEditor):
self.layout().addWidget(self.libraries_widget)
self.libraries_widget.dataChanged.connect(self.dataChanged)
self.prefixPathChanged.connect(
lambda path: self.library_model.setPrefixPath(path)
self.contextChanged.connect(
lambda: self.library_model.setPrefixPath(self.prefix_path)
)
def _load_json(self, json_object):
......
......@@ -73,11 +73,11 @@ class MainWindow(QMainWindow):
self.load_settings()
def set_prefix_root(self, prefix_root_path):
"""Sets the root path of the prefix to edit"""
def set_context(self, context):
"""Sets the BEAT context to use"""
self.assetBrowser.set_prefix_root(prefix_root_path)
self.assetWidget.set_prefix_root(prefix_root_path)
self.assetBrowser.set_context(context)
self.assetWidget.set_context(context)
def show_about(self):
"""About box for the application"""
......
......@@ -220,8 +220,8 @@ class ProtocolTemplateEditor(AbstractAssetEditor):
self.layout().addWidget(self.add_set_button, 1)
self.add_set_button.clicked.connect(self.__add_set)
self.prefixPathChanged.connect(
lambda path: self.dataformat_model.setPrefixPath(path)
self.contextChanged.connect(
lambda: self.dataformat_model.setPrefixPath(self.prefix_path)
)
def __on_remove_requested(self):
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment