From 4e30a4f4df771c540e7fd2088863635939927fba Mon Sep 17 00:00:00 2001 From: Tom Veldman | MA-IT Date: Mon, 2 Mar 2026 12:13:29 +0100 Subject: [PATCH 1/2] Support finding Teams solutions --- src/sysmac_solution.py | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/src/sysmac_solution.py b/src/sysmac_solution.py index 221a5f9..47404a2 100644 --- a/src/sysmac_solution.py +++ b/src/sysmac_solution.py @@ -1,5 +1,6 @@ import copy import logging +import os.path import xml.etree.ElementTree as ET from datetime import datetime from os import PathLike @@ -16,7 +17,7 @@ class SysmacSolution: def __init__(self, solutions_path, uuid): - self.solutions_path = Path(solutions_path) + self.solutions_path = Path(solutions_path) / uuid if uuid else Path(solutions_path) self._uuid = uuid self._name = '' self._author = '' @@ -24,6 +25,8 @@ def __init__(self, solutions_path, uuid): self._last_modified = datetime.fromtimestamp(0) self.global_vars = [] + if not os.path.exists(self.solutions_path / f'{self._uuid}.xml'): + self._set_uuid_from_solution(self.solutions_path) self._get_properties() @property @@ -48,11 +51,11 @@ def uuid(self): def get_global_vars(self) -> List[SysmacDataType]: project_oem_file = f'{self._uuid}.oem' - tree = ET.parse(self.solutions_path / self._uuid / project_oem_file) + tree = ET.parse(self.solutions_path / project_oem_file) root = tree.getroot() global_vars_filename = root.find(".//Entity[@type='Variables'][@subtype='Global']").attrib.get('id') self.global_vars = [SysmacDataType.import_from_slwd(symbol) - for symbol in parse_slwd(self.solutions_path / self._uuid / f"{global_vars_filename}.xml")] + for symbol in parse_slwd(self.solutions_path / f"{global_vars_filename}.xml")] return self.global_vars def get_published_symbols(self) -> List[SysmacDataType]: @@ -147,7 +150,7 @@ def get_published_symbols(self) -> List[SysmacDataType]: def _get_data_types(self) -> Dict[str, SysmacDataType]: project_oem_file = f'{self._uuid}.oem' - tree = ET.parse(self.solutions_path / self._uuid / project_oem_file) + tree = ET.parse(self.solutions_path / project_oem_file) root = tree.getroot() dt = {} @@ -162,7 +165,7 @@ def _get_data_types(self) -> Dict[str, SysmacDataType]: def _get_data_from_namespace(self, datatype_id, namespace=None) -> Dict[str, SysmacDataType]: datatype_file = f"{datatype_id}.xml" - tree = ET.parse(self.solutions_path / self._uuid / datatype_file) + tree = ET.parse(self.solutions_path / datatype_file) root = tree.getroot() data = get_struct_from_namespace(root, namespace) @@ -171,20 +174,43 @@ def _get_data_from_namespace(self, datatype_id, namespace=None) -> Dict[str, Sys def _get_properties(self): try: - tree = ET.parse(self.solutions_path / self._uuid / f'{self._uuid}.xml') + tree = ET.parse(self.solutions_path / f'{self._uuid}.xml') except FileNotFoundError as e: return root = tree.getroot() self._project_type = root.find('.//ProjectType').text self._author = root.find('.//Author').text - self._last_modified = datetime.fromisoformat(root.find('.//DateModified').text) + date_last_modified = root.find('.//DateModified') + if date_last_modified: + self._last_modified = datetime.fromisoformat(date_last_modified.text) - tree = ET.parse(self.solutions_path / self._uuid / f'{self._uuid}.oem') + tree = ET.parse(self.solutions_path / f'{self._uuid}.oem') root = tree.getroot() solution_element = root.find(".//Entity[@type='Solution']") self._name = solution_element.attrib.get('name') if solution_element is not None else '' + def _set_uuid_from_solution(self, solution_dir: str | bytes | PathLike): + """Sets the UUID of this solution, based on its .oem filename. + Handles local solutions, temporary solutions and Team solutions. + Leaves UUID unchanged if none could be found""" + + found_uuid = "" + candidates = ( + Path(solution_dir), + (Path(solution_dir) / "Project"), + ) + + for candidate in candidates: + found_file = next(candidate.glob('*.oem'), None) + if not found_file: + continue + + found_uuid = os.path.splitext(os.path.basename(found_file))[0] + self._uuid = found_uuid + self.solutions_path = candidate + break + def get_solutions(solutions_path: str | bytes | PathLike) -> List[SysmacSolution]: solutions = [SysmacSolution(solutions_path, s.stem) for s in Path(solutions_path).glob('*/')] From 42b8ab1a12835bb4818bd6e1a0b152108998973f Mon Sep 17 00:00:00 2001 From: Tom Veldman | MA-IT Date: Mon, 2 Mar 2026 13:46:27 +0100 Subject: [PATCH 2/2] Support getting variables from Team solutions --- src/main.py | 5 ++++- src/sysmac_solution.py | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main.py b/src/main.py index fbf1395..4d1f362 100644 --- a/src/main.py +++ b/src/main.py @@ -64,6 +64,7 @@ def __init__(self): content_frame.pack(fill=tk.BOTH, expand=True) main_frm.pack(fill=tk.BOTH, expand=True) + self.solutions = dict() self.__check_result_queue() self.protocol("WM_DELETE_WINDOW", self.on_closing) @@ -153,6 +154,7 @@ def _path_button_cb(self): command = 'get_solutions' cmd_args = (input_path,) self.task_queue.put((command, cmd_args)) + self.solutions.clear() self.status_bar.set_text(f'Looking for projects in {input_path}. Please wait ...') self.settings.set('general', 'solution_path', input_path) @@ -163,6 +165,7 @@ def __check_result_queue(self): while True: message, data = self.result_queue.get_nowait() if message == 'get_solutions': + self.solutions = {solution.uuid: solution for solution in data} self.projects_tv.update_projects(data) self.status_bar.set_text(f'{len(data)} projects found') elif message == 'get_vars_from_solution': @@ -210,7 +213,7 @@ def on_project_tv_double_click(self, event): solutions_path = self.path_entry_var.get() project_uuid = self.projects_tv.item(tv_selection[0], "text") command = 'get_vars_from_solution' - cmd_args = (solutions_path, project_uuid) + cmd_args = (self.solutions[project_uuid].solutions_path, project_uuid) self.task_queue.put((command, cmd_args)) self.projects_tv.disable_selection() diff --git a/src/sysmac_solution.py b/src/sysmac_solution.py index 47404a2..2cdcf33 100644 --- a/src/sysmac_solution.py +++ b/src/sysmac_solution.py @@ -17,7 +17,7 @@ class SysmacSolution: def __init__(self, solutions_path, uuid): - self.solutions_path = Path(solutions_path) / uuid if uuid else Path(solutions_path) + self.solutions_path = Path(solutions_path) self._uuid = uuid self._name = '' self._author = '' @@ -213,7 +213,7 @@ def _set_uuid_from_solution(self, solution_dir: str | bytes | PathLike): def get_solutions(solutions_path: str | bytes | PathLike) -> List[SysmacSolution]: - solutions = [SysmacSolution(solutions_path, s.stem) for s in Path(solutions_path).glob('*/')] + solutions = [SysmacSolution(f"{solutions_path}/{s.stem}", s.stem) for s in Path(solutions_path).glob('*/')] # Sort the project by last modification date by descending (most recently modified first) return sorted(solutions, key=lambda x: x.last_modified, reverse=True)