Skip to content

Commit 81d038f

Browse files
committed
feat: enhance package management and search ranking with RapidFuzz integration
1 parent 34fe2bc commit 81d038f

5 files changed

Lines changed: 292 additions & 203 deletions

File tree

archpkg/cli.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,7 +1485,8 @@ def config(
14851485
# Set configuration value
14861486
try:
14871487
# Convert value to appropriate type
1488-
converted_value: any = value
1488+
from typing import Any
1489+
converted_value: Any = value
14891490
if value.lower() in ('true', 'false'):
14901491
converted_value = value.lower() == 'true'
14911492
elif value.isdigit():
@@ -1527,11 +1528,11 @@ def list_installed(
15271528

15281529
for pkg in packages:
15291530
update_status = "[green]No[/green]" if not pkg.update_available else "[red]Yes[/red]"
1530-
last_updated = pkg.last_updated or "Never"
1531+
last_updated = pkg.last_update_check or pkg.install_date or "Never"
15311532
table.add_row(
15321533
pkg.name,
15331534
pkg.source,
1534-
pkg.installed_version or "Unknown",
1535+
pkg.version or "Unknown",
15351536
update_status,
15361537
last_updated
15371538
)
@@ -1560,7 +1561,7 @@ def service(
15601561
if config.auto_update_enabled:
15611562
console.print("[green]Background update service is enabled[/green]")
15621563
console.print(f"Check interval: {config.update_check_interval_hours} hours")
1563-
console.print(f"Auto-install: {'Enabled' if config.auto_install_updates else 'Disabled'}")
1564+
console.print(f"Auto-update mode: {config.auto_update_mode}")
15641565
else:
15651566
console.print("[yellow]Background update service is disabled[/yellow]")
15661567
else:

archpkg/gui.py

Lines changed: 124 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -306,13 +306,28 @@ def create_installed_tab(self):
306306
controls_layout.addWidget(audit_button)
307307

308308
layout.addLayout(controls_layout)
309-
310-
# Installed packages list
311-
self.installed_list = QListWidget()
312-
layout.addWidget(self.installed_list)
309+
310+
# Installed packages split view
311+
split_layout = QHBoxLayout()
312+
313+
apps_group = QGroupBox("Applications (Explicit/Manual)")
314+
apps_layout = QVBoxLayout()
315+
self.apps_list = QListWidget()
316+
apps_layout.addWidget(self.apps_list)
317+
apps_group.setLayout(apps_layout)
318+
split_layout.addWidget(apps_group)
319+
320+
deps_group = QGroupBox("Dependencies (Auto-installed)")
321+
deps_layout = QVBoxLayout()
322+
self.deps_list = QListWidget()
323+
deps_layout.addWidget(self.deps_list)
324+
deps_group.setLayout(deps_layout)
325+
split_layout.addWidget(deps_group)
326+
327+
layout.addLayout(split_layout)
313328

314329
# Info label
315-
self.installed_info_label = QLabel("Click 'Refresh List' to load installed packages")
330+
self.installed_info_label = QLabel("Click 'Refresh List' to load applications and dependencies")
316331
layout.addWidget(self.installed_info_label)
317332

318333
self.tabs.addTab(installed_widget, "Installed Packages")
@@ -744,39 +759,119 @@ def remove_selected_package(self):
744759

745760
def refresh_installed_packages(self):
746761
"""Refresh the list of installed packages."""
747-
self.installed_list.clear()
762+
self.apps_list.clear()
763+
self.deps_list.clear()
748764
self.installed_info_label.setText("Loading installed packages...")
749-
765+
750766
try:
751-
# Get installed packages based on distro
767+
apps = set()
768+
deps = set()
769+
770+
# Get installed package split based on distro
752771
if self.distro_family == 'arch':
753-
result = subprocess.run(['paru', '-Q'], capture_output=True, text=True, timeout=30)
772+
apps_result = subprocess.run(['paru', '-Qe'], capture_output=True, text=True, timeout=30)
773+
deps_result = subprocess.run(['paru', '-Qd'], capture_output=True, text=True, timeout=30)
774+
775+
if apps_result.returncode == 0:
776+
for line in apps_result.stdout.split('\n'):
777+
parts = line.split()
778+
if parts:
779+
apps.add(parts[0])
780+
781+
if deps_result.returncode == 0:
782+
for line in deps_result.stdout.split('\n'):
783+
parts = line.split()
784+
if parts:
785+
deps.add(parts[0])
786+
754787
elif self.distro_family == 'debian':
755-
result = subprocess.run(['dpkg', '-l'], capture_output=True, text=True, timeout=30)
788+
apps_result = subprocess.run(['apt-mark', 'showmanual'], capture_output=True, text=True, timeout=30)
789+
deps_result = subprocess.run(['apt-mark', 'showauto'], capture_output=True, text=True, timeout=30)
790+
791+
if apps_result.returncode == 0:
792+
for line in apps_result.stdout.split('\n'):
793+
pkg = line.strip()
794+
if pkg:
795+
apps.add(pkg)
796+
797+
if deps_result.returncode == 0:
798+
for line in deps_result.stdout.split('\n'):
799+
pkg = line.strip()
800+
if pkg:
801+
deps.add(pkg)
802+
756803
elif self.distro_family == 'fedora':
757-
result = subprocess.run(['dnf', 'list', 'installed'], capture_output=True, text=True, timeout=30)
804+
user_result = subprocess.run(
805+
['dnf', 'repoquery', '--userinstalled', '--qf', '%{name}'],
806+
capture_output=True,
807+
text=True,
808+
timeout=30
809+
)
810+
all_result = subprocess.run(
811+
['dnf', 'repoquery', '--installed', '--qf', '%{name}'],
812+
capture_output=True,
813+
text=True,
814+
timeout=30
815+
)
816+
817+
if user_result.returncode == 0:
818+
for line in user_result.stdout.split('\n'):
819+
pkg = line.strip()
820+
if pkg:
821+
apps.add(pkg)
822+
823+
if all_result.returncode == 0:
824+
all_pkgs = set()
825+
for line in all_result.stdout.split('\n'):
826+
pkg = line.strip()
827+
if pkg:
828+
all_pkgs.add(pkg)
829+
deps = all_pkgs - apps
830+
758831
elif self.distro_family == 'suse':
759-
result = subprocess.run(['zypper', 'se', '--installed-only'], capture_output=True, text=True, timeout=30)
832+
user_result = subprocess.run(
833+
['zypper', '--quiet', 'pa', '--userinstalled'],
834+
capture_output=True,
835+
text=True,
836+
timeout=30
837+
)
838+
all_result = subprocess.run(['rpm', '-qa'], capture_output=True, text=True, timeout=30)
839+
840+
if user_result.returncode == 0:
841+
for line in user_result.stdout.split('\n'):
842+
line = line.strip()
843+
if line and '|' in line:
844+
parts = [part.strip() for part in line.split('|')]
845+
if len(parts) >= 3 and parts[0] in {'i', 'i+'}:
846+
name = parts[2]
847+
if name and name != 'Name':
848+
apps.add(name)
849+
850+
if all_result.returncode == 0:
851+
all_pkgs = set(pkg.strip() for pkg in all_result.stdout.split('\n') if pkg.strip())
852+
deps = all_pkgs - apps
853+
760854
else:
761855
self.installed_info_label.setText("Package listing not supported for this distribution")
762856
return
763-
764-
if result.returncode == 0:
765-
lines = result.stdout.split('\n')
766-
package_count = 0
767-
768-
for line in lines:
769-
if line.strip():
770-
parts = line.split()
771-
if parts:
772-
pkg_name = parts[0]
773-
self.installed_list.addItem(pkg_name)
774-
package_count += 1
775-
776-
self.installed_info_label.setText(f"Total installed packages: {package_count}")
777-
else:
778-
self.installed_info_label.setText("Failed to load installed packages")
779-
857+
858+
# Remove overlap defensively
859+
deps = deps - apps
860+
861+
for pkg in sorted(apps):
862+
self.apps_list.addItem(pkg)
863+
864+
for pkg in sorted(deps):
865+
self.deps_list.addItem(pkg)
866+
867+
total_count = len(apps) + len(deps)
868+
self.installed_info_label.setText(
869+
f"Applications: {len(apps)} | Dependencies: {len(deps)} | Total: {total_count}"
870+
)
871+
872+
if total_count == 0:
873+
self.installed_info_label.setText("No installed packages found or package manager query failed")
874+
780875
except Exception as e:
781876
self.installed_info_label.setText(f"Error: {str(e)}")
782877

0 commit comments

Comments
 (0)