Addon Manager: Add <bundle> and <other>
This commit is contained in:
@@ -489,23 +489,17 @@ class Addon:
|
||||
|
||||
if self.repo_type == Addon.Kind.WORKBENCH:
|
||||
return True
|
||||
if self.repo_type == Addon.Kind.PACKAGE:
|
||||
if self.metadata is None:
|
||||
fci.Console.PrintLog(
|
||||
f"Addon Manager internal error: lost metadata for package {self.name}\n"
|
||||
)
|
||||
return False
|
||||
content = self.metadata.content
|
||||
if not content:
|
||||
return False
|
||||
return "workbench" in content
|
||||
return False
|
||||
return self.contains_packaged_content("workbench")
|
||||
|
||||
def contains_macro(self) -> bool:
|
||||
"""Determine if this package contains (or is) a macro"""
|
||||
|
||||
if self.repo_type == Addon.Kind.MACRO:
|
||||
return True
|
||||
return self.contains_packaged_content("macro")
|
||||
|
||||
def contains_packaged_content(self, content_type: str):
|
||||
"""Determine if the package contains content_type"""
|
||||
if self.repo_type == Addon.Kind.PACKAGE:
|
||||
if self.metadata is None:
|
||||
fci.Console.PrintLog(
|
||||
@@ -513,21 +507,20 @@ class Addon:
|
||||
)
|
||||
return False
|
||||
content = self.metadata.content
|
||||
return "macro" in content
|
||||
return content_type in content
|
||||
return False
|
||||
|
||||
def contains_preference_pack(self) -> bool:
|
||||
"""Determine if this package contains a preference pack"""
|
||||
return self.contains_packaged_content("preferencepack")
|
||||
|
||||
if self.repo_type == Addon.Kind.PACKAGE:
|
||||
if self.metadata is None:
|
||||
fci.Console.PrintLog(
|
||||
f"Addon Manager internal error: lost metadata for package {self.name}\n"
|
||||
)
|
||||
return False
|
||||
content = self.metadata.content
|
||||
return "preferencepack" in content
|
||||
return False
|
||||
def contains_bundle(self) -> bool:
|
||||
"""Determine if this package contains a bundle"""
|
||||
return self.contains_packaged_content("bundle")
|
||||
|
||||
def contains_other(self) -> bool:
|
||||
"""Determine if this package contains an "other" content item"""
|
||||
return self.contains_packaged_content("other")
|
||||
|
||||
def get_best_icon_relative_path(self) -> str:
|
||||
"""Get the path within the repo the addon's icon. Usually specified by
|
||||
|
||||
@@ -93,6 +93,8 @@ class TestAddon(unittest.TestCase):
|
||||
self.assertTrue(addon_with_workbench.contains_workbench())
|
||||
self.assertFalse(addon_with_workbench.contains_macro())
|
||||
self.assertFalse(addon_with_workbench.contains_preference_pack())
|
||||
self.assertFalse(addon_with_workbench.contains_bundle())
|
||||
self.assertFalse(addon_with_workbench.contains_other())
|
||||
|
||||
# Macros
|
||||
addon_with_macro = Addon(
|
||||
@@ -105,6 +107,8 @@ class TestAddon(unittest.TestCase):
|
||||
self.assertFalse(addon_with_macro.contains_workbench())
|
||||
self.assertTrue(addon_with_macro.contains_macro())
|
||||
self.assertFalse(addon_with_macro.contains_preference_pack())
|
||||
self.assertFalse(addon_with_workbench.contains_bundle())
|
||||
self.assertFalse(addon_with_workbench.contains_other())
|
||||
|
||||
# Preference Packs
|
||||
addon_with_prefpack = Addon(
|
||||
@@ -117,6 +121,8 @@ class TestAddon(unittest.TestCase):
|
||||
self.assertFalse(addon_with_prefpack.contains_workbench())
|
||||
self.assertFalse(addon_with_prefpack.contains_macro())
|
||||
self.assertTrue(addon_with_prefpack.contains_preference_pack())
|
||||
self.assertFalse(addon_with_workbench.contains_bundle())
|
||||
self.assertFalse(addon_with_workbench.contains_other())
|
||||
|
||||
# Combination
|
||||
addon_with_all = Addon(
|
||||
@@ -129,6 +135,8 @@ class TestAddon(unittest.TestCase):
|
||||
self.assertTrue(addon_with_all.contains_workbench())
|
||||
self.assertTrue(addon_with_all.contains_macro())
|
||||
self.assertTrue(addon_with_all.contains_preference_pack())
|
||||
self.assertTrue(addon_with_all.contains_bundle())
|
||||
self.assertTrue(addon_with_all.contains_other())
|
||||
|
||||
# Now do the simple, explicitly-set cases
|
||||
addon_wb = Addon(
|
||||
|
||||
@@ -616,6 +616,22 @@ class TestMetadataReaderIntegration(unittest.TestCase):
|
||||
expected_packs.remove(wb.name)
|
||||
self.assertEqual(len(expected_packs), 0)
|
||||
|
||||
def test_bundle(self):
|
||||
from addonmanager_metadata import MetadataReader
|
||||
|
||||
filename = os.path.join(self.test_data_dir, "bundle_only.xml")
|
||||
metadata = MetadataReader.from_file(filename)
|
||||
self.assertIn("bundle", metadata.content)
|
||||
self.assertEqual(len(metadata.content["bundle"]), 1)
|
||||
|
||||
def test_other(self):
|
||||
from addonmanager_metadata import MetadataReader
|
||||
|
||||
filename = os.path.join(self.test_data_dir, "other_only.xml")
|
||||
metadata = MetadataReader.from_file(filename)
|
||||
self.assertIn("other", metadata.content)
|
||||
self.assertEqual(len(metadata.content["other"]), 1)
|
||||
|
||||
def test_content_combination(self):
|
||||
from addonmanager_metadata import MetadataReader
|
||||
|
||||
|
||||
23
src/Mod/AddonManager/AddonManagerTest/data/bundle_only.xml
Normal file
23
src/Mod/AddonManager/AddonManagerTest/data/bundle_only.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no" ?>
|
||||
<package format="1" xmlns="https://wiki.freecad.org/Package_Metadata">
|
||||
<name>Test Bundle</name>
|
||||
<description>A package.xml file for unit testing.</description>
|
||||
<version>1.0.0</version>
|
||||
<date>2025-02-22</date>
|
||||
<maintainer email="developer@freecad.org">FreeCAD Developer</maintainer>
|
||||
<license file="LICENSE">LGPL-2.1</license>
|
||||
<url type="repository" branch="main">https://github.com/chennes/FreeCAD-Package</url>
|
||||
<url type="readme">https://github.com/chennes/FreeCAD-Package/blob/main/README.md</url>
|
||||
|
||||
<content>
|
||||
<bundle>
|
||||
<name>A bunch of great addons you should install</name>
|
||||
<depend type="addon">TestAddon1</depend>
|
||||
<depend type="addon">TestAddon2</depend>
|
||||
<depend type="addon">TestAddon3</depend>
|
||||
<depend type="addon">TestAddon4</depend>
|
||||
<depend type="addon">TestAddon5</depend>
|
||||
</bundle>
|
||||
</content>
|
||||
|
||||
</package>
|
||||
@@ -23,6 +23,12 @@
|
||||
<preferencepack>
|
||||
<name>MyFirstPack</name>
|
||||
</preferencepack>
|
||||
<bundle>
|
||||
<name>A bundle that bundles nothing</name>
|
||||
</bundle>
|
||||
<other>
|
||||
<name>Mysterious Object</name>
|
||||
</other>
|
||||
</content>
|
||||
|
||||
</package>
|
||||
|
||||
18
src/Mod/AddonManager/AddonManagerTest/data/other_only.xml
Normal file
18
src/Mod/AddonManager/AddonManagerTest/data/other_only.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no" ?>
|
||||
<package format="1" xmlns="https://wiki.freecad.org/Package_Metadata">
|
||||
<name>Test Other</name>
|
||||
<description>A package.xml file for unit testing.</description>
|
||||
<version>1.0.0</version>
|
||||
<date>2025-02-22</date>
|
||||
<maintainer email="developer@freecad.org">FreeCAD Developer</maintainer>
|
||||
<license file="LICENSE">LGPL-2.1</license>
|
||||
<url type="repository" branch="main">https://github.com/chennes/FreeCAD-Package</url>
|
||||
<url type="readme">https://github.com/chennes/FreeCAD-Package/blob/main/README.md</url>
|
||||
|
||||
<content>
|
||||
<other>
|
||||
<name>A thing that's not a workbench, macro, preference pack, or bundle</name>
|
||||
</other>
|
||||
</content>
|
||||
|
||||
</package>
|
||||
@@ -118,6 +118,7 @@ SET(AddonManagerTestsGui_SRCS
|
||||
SET(AddonManagerTestsFiles_SRCS
|
||||
AddonManagerTest/data/__init__.py
|
||||
AddonManagerTest/data/addon_update_stats.json
|
||||
AddonManagerTest/data/bundle_only.xml
|
||||
AddonManagerTest/data/combination.xml
|
||||
AddonManagerTest/data/corrupted_metadata.zip
|
||||
AddonManagerTest/data/depends_on_all_workbenches.xml
|
||||
@@ -131,6 +132,7 @@ SET(AddonManagerTestsFiles_SRCS
|
||||
AddonManagerTest/data/MacrosRecipesWikiPage.zip
|
||||
AddonManagerTest/data/metadata.zip
|
||||
AddonManagerTest/data/missing_macro_metadata.FCStd
|
||||
AddonManagerTest/data/other_only.xml
|
||||
AddonManagerTest/data/prefpack_only.xml
|
||||
AddonManagerTest/data/test_addon_with_fcmacro.zip
|
||||
AddonManagerTest/data/test_github_style_repo.zip
|
||||
|
||||
@@ -69,6 +69,8 @@ class ContentFilter(IntEnum):
|
||||
WORKBENCH = 1
|
||||
MACRO = 2
|
||||
PREFERENCE_PACK = 3
|
||||
BUNDLE = 4
|
||||
OTHER = 5
|
||||
|
||||
|
||||
class Filter:
|
||||
@@ -116,6 +118,14 @@ class WidgetFilterSelector(QtWidgets.QComboBox):
|
||||
translate("AddonsInstaller", "Preference Pack"),
|
||||
(FilterType.PACKAGE_CONTENTS, ContentFilter.PREFERENCE_PACK),
|
||||
)
|
||||
self.addItem(
|
||||
translate("AddonsInstaller", "Bundle"),
|
||||
(FilterType.PACKAGE_CONTENTS, ContentFilter.BUNDLE),
|
||||
)
|
||||
self.addItem(
|
||||
translate("AddonsInstaller", "Other"),
|
||||
(FilterType.PACKAGE_CONTENTS, ContentFilter.OTHER),
|
||||
)
|
||||
self.insertSeparator(self.count())
|
||||
self.addItem(translate("AddonsInstaller", "Installation Status"))
|
||||
self.installation_status_index = self.count() - 1
|
||||
|
||||
@@ -71,6 +71,8 @@ class AddContent:
|
||||
self.dialog.addonKindComboBox.setItemData(0, "macro")
|
||||
self.dialog.addonKindComboBox.setItemData(1, "preferencepack")
|
||||
self.dialog.addonKindComboBox.setItemData(2, "workbench")
|
||||
self.dialog.addonKindComboBox.setItemData(3, "bundle")
|
||||
self.dialog.addonKindComboBox.setItemData(4, "other")
|
||||
|
||||
self.people_table = PeopleTable()
|
||||
self.licenses_table = LicensesTable()
|
||||
@@ -148,6 +150,8 @@ class AddContent:
|
||||
self.dialog.macroFileLineEdit.setText(files[0])
|
||||
elif addon_kind == "preferencepack":
|
||||
self.dialog.prefPackNameLineEdit.setText(self.metadata.Name)
|
||||
elif addon_kind == "bundle" or addon_kind == "other":
|
||||
pass
|
||||
else:
|
||||
raise RuntimeError("Invalid data found for selection")
|
||||
|
||||
|
||||
@@ -367,7 +367,7 @@ class MetadataReader:
|
||||
def _parse_content(namespace: str, metadata: Metadata, root: ET.Element):
|
||||
"""Given a content node, loop over its children, and if they are a recognized
|
||||
element type, recurse into each one to parse it."""
|
||||
known_content_types = ["workbench", "macro", "preferencepack"]
|
||||
known_content_types = ["workbench", "macro", "preferencepack", "bundle", "other"]
|
||||
for child in root:
|
||||
content_type = child.tag[len(namespace) :]
|
||||
if content_type in known_content_types:
|
||||
|
||||
@@ -569,7 +569,7 @@ class PackageListFilter(QtCore.QSortFilterProxyModel):
|
||||
|
||||
def setPackageFilter(
|
||||
self, package_type: int
|
||||
) -> None: # 0=All, 1=Workbenches, 2=Macros, 3=Preference Packs
|
||||
) -> None: # 0=All, 1=Workbenches, 2=Macros, 3=Preference Packs, 4=Bundles, 5=Other
|
||||
"""Set the package filter to package_type and refreshes."""
|
||||
self.package_type = package_type
|
||||
self.invalidateFilter()
|
||||
@@ -634,6 +634,12 @@ class PackageListFilter(QtCore.QSortFilterProxyModel):
|
||||
elif self.package_type == 3:
|
||||
if not data.contains_preference_pack():
|
||||
return False
|
||||
elif self.package_type == 4:
|
||||
if not data.contains_bundle():
|
||||
return False
|
||||
elif self.package_type == 5:
|
||||
if not data.contains_other():
|
||||
return False
|
||||
|
||||
if self.status == StatusFilter.INSTALLED:
|
||||
if data.status() == Addon.Status.NOT_INSTALLED:
|
||||
|
||||
Reference in New Issue
Block a user