Addon Manager: Rework backend to use package.xml
This shifts to use the model-view-controller pattern for the list of addons, and moves to using a full model class rather than an indexed array for the data storage and management. This enables much more information to be stored as part of the new AddonManagerRepo data type. It now wraps the Macro class for macros, supports Preference Packs, and provides access to the Metadata object.
This commit is contained in:
164
src/Mod/AddonManager/AddonManagerRepo.py
Normal file
164
src/Mod/AddonManager/AddonManagerRepo.py
Normal file
@@ -0,0 +1,164 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
|
||||
#* *
|
||||
#* This program is free software; you can redistribute it and/or modify *
|
||||
#* it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
#* as published by the Free Software Foundation; either version 2 of *
|
||||
#* the License, or (at your option) any later version. *
|
||||
#* for detail see the LICENCE text file. *
|
||||
#* *
|
||||
#* This program is distributed in the hope that it will be useful, *
|
||||
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
#* GNU Library General Public License for more details. *
|
||||
#* *
|
||||
#* You should have received a copy of the GNU Library General Public *
|
||||
#* License along with this program; if not, write to the Free Software *
|
||||
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
#* USA *
|
||||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
import FreeCAD
|
||||
|
||||
import os
|
||||
|
||||
from addonmanager_macro import Macro
|
||||
|
||||
class AddonManagerRepo:
|
||||
"Encapsulate information about a FreeCAD addon"
|
||||
|
||||
from enum import Enum
|
||||
|
||||
class RepoType(Enum):
|
||||
WORKBENCH = 1
|
||||
MACRO = 2
|
||||
PACKAGE = 3
|
||||
|
||||
def __str__(self) ->str :
|
||||
if self.value == 1:
|
||||
return "Workbench"
|
||||
elif self.value == 2:
|
||||
return "Macro"
|
||||
elif self.value == 3:
|
||||
return "Package"
|
||||
|
||||
class UpdateStatus(Enum):
|
||||
NOT_INSTALLED = 0
|
||||
UNCHECKED = 1
|
||||
NO_UPDATE_AVAILABLE = 2
|
||||
UPDATE_AVAILABLE = 3
|
||||
PENDING_RESTART = 4
|
||||
|
||||
def __lt__(self, other):
|
||||
if self.__class__ is other.__class__:
|
||||
return self.value < other.value
|
||||
return NotImplemented
|
||||
|
||||
def __str__(self) ->str :
|
||||
if self.value == 0:
|
||||
return "Not installed"
|
||||
elif self.value == 1:
|
||||
return "Unchecked"
|
||||
elif self.value == 2:
|
||||
return "No update available"
|
||||
elif self.value == 3:
|
||||
return "Update available"
|
||||
elif self.value == 4:
|
||||
return "Restart required"
|
||||
|
||||
def __init__ (self, name:str, url:str, status:UpdateStatus, branch:str):
|
||||
self.name = name
|
||||
self.url = url
|
||||
self.branch = branch
|
||||
self.update_status = status
|
||||
self.repo_type = AddonManagerRepo.RepoType.WORKBENCH
|
||||
self.description = None
|
||||
from addonmanager_utilities import construct_git_url
|
||||
self.metadata_url = "" if not self.url else construct_git_url(self, "package.xml")
|
||||
self.metadata = None
|
||||
self.icon = None
|
||||
self.cached_icon_filename = ""
|
||||
self.macro = None # Bridge to Gaël Écorchard's macro management class
|
||||
|
||||
def __str__ (self) -> str:
|
||||
result = f"FreeCAD {self.repo_type}\n"
|
||||
result += f"Name: {self.name}\n"
|
||||
result += f"URL: {self.url}\n"
|
||||
result += "Has metadata\n" if self.metadata is not None else "No metadata found\n"
|
||||
if self.macro is not None:
|
||||
result += "Has linked Macro object\n"
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def from_macro (self, macro:Macro):
|
||||
if macro.is_installed():
|
||||
status = AddonManagerRepo.UpdateStatus.UNCHECKED
|
||||
else:
|
||||
status = AddonManagerRepo.UpdateStatus.NOT_INSTALLED
|
||||
instance = AddonManagerRepo(macro.name, macro.url, status, "master")
|
||||
instance.macro = macro
|
||||
instance.repo_type = AddonManagerRepo.RepoType.MACRO
|
||||
instance.description = macro.desc
|
||||
return instance
|
||||
|
||||
def contains_workbench(self) -> bool:
|
||||
""" Determine if this package contains (or is) a workbench """
|
||||
|
||||
if self.repo_type == AddonManagerRepo.RepoType.WORKBENCH:
|
||||
return True
|
||||
elif self.repo_type == AddonManagerRepo.RepoType.PACKAGE:
|
||||
content = self.metadata.Content
|
||||
return "workbench" in content
|
||||
else:
|
||||
return False
|
||||
|
||||
def contains_macro(self) -> bool:
|
||||
""" Determine if this package contains (or is) a macro """
|
||||
|
||||
if self.repo_type == AddonManagerRepo.RepoType.MACRO:
|
||||
return True
|
||||
elif self.repo_type == AddonManagerRepo.RepoType.PACKAGE:
|
||||
content = self.metadata.Content
|
||||
return "macro" in content
|
||||
else:
|
||||
return False
|
||||
|
||||
def contains_preference_pack(self) -> bool:
|
||||
""" Determine if this package contains a preference pack """
|
||||
|
||||
if self.repo_type == AddonManagerRepo.RepoType.PACKAGE:
|
||||
content = self.metadata.Content
|
||||
return "preferencepack" in content
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_cached_icon_filename(self) ->str:
|
||||
""" Get the filename for the locally-cached copy of the icon """
|
||||
|
||||
if self.cached_icon_filename:
|
||||
return self.cached_icon_filename
|
||||
|
||||
real_icon = self.metadata.Icon
|
||||
if not real_icon:
|
||||
# If there is no icon set for the entire package, see if there are any workbenches, which
|
||||
# are required to have icons, and grab the first one we find:
|
||||
content = self.metadata.Content
|
||||
if "workbench" in content:
|
||||
wb = content["workbench"][0]
|
||||
if wb.Icon:
|
||||
if wb.Subdirectory:
|
||||
subdir = wb.Subdirectory
|
||||
else:
|
||||
subdir = wb.Name
|
||||
real_icon = subdir + wb.Icon
|
||||
|
||||
real_icon = real_icon.replace("/", os.path.sep) # Required path separator in the metadata.xml file to local separator
|
||||
|
||||
_, file_extension = os.path.splitext(real_icon)
|
||||
store = os.path.join(FreeCAD.getUserAppDataDir(), "AddonManager", "PackageMetadata")
|
||||
self.cached_icon_filename = os.path.join(store, self.name, "cached_icon"+file_extension)
|
||||
|
||||
return self.cached_icon_filename
|
||||
Reference in New Issue
Block a user