Skip to content

Commit

Permalink
Implemented Downgrade, Reinstall, DistroSync. It should be resolve #242
Browse files Browse the repository at this point in the history
  • Loading branch information
anaselli committed Dec 31, 2024
1 parent 4164a5f commit 15dd2b3
Show file tree
Hide file tree
Showing 3 changed files with 251 additions and 20 deletions.
14 changes: 14 additions & 0 deletions dnfdragora/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

# NOTE part of this code is imported from yumex-dnf

from enum import Enum
import os.path
import re
import subprocess
Expand Down Expand Up @@ -62,6 +63,19 @@

# Constants

class Actions(Enum):
'''
Enum
NORMAL Install/Update/Remove packages
REINSTALL Reinstall packages
DOWNGRADE Downgrade packages
DISTRO_SYNC Use Distro Sync to synchronize packages with repo
'''
NORMAL = 1
REINSTALL = 2
DOWNGRADE = 3
DISTRO_SYNC = 4

# Main UI stack names
PAGE_PACKAGES = 'packages'
PAGE_QUEUE = 'queue'
Expand Down
96 changes: 96 additions & 0 deletions dnfdragora/dialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,102 @@ def run(self, data):
return performedUndo


class PackageActionDialog:
'''
PackageActionDialog is a dialog that allows to select the action
to be performed on packages. Default behaviorn is Normal e.g.
Installing, Updating or removing selected packages.
'''

def __init__(self, parent, actionValue):
self.parent = parent
self.factory = self.parent.factory
self.actionValue = self.savedActionValue = actionValue

def run(self):
'''
Propose a radio button list related to the possible actions
'''

## push application title
appTitle = yui.YUI.app().applicationTitle()
## set new title to get it in dialog
yui.YUI.app().setApplicationTitle(_("Action on selected packages") )
minWidth = 60;
minHeight = 10;
dlg = self.factory.createPopupDialog(yui.YDialogNormalColor)
minSize = self.factory.createMinSize(dlg, minWidth, minHeight)
layout = self.factory.createVBox(minSize)

#labeledFrameBox - Actions
frame = self.factory.createFrame(layout, "Actions")
frame.setWeight( yui.YD_HORIZ, 1 )
frame = self.factory.createHVCenter( frame )
frame = self.factory.createHVSquash( frame )
frame = self.factory.createVBox( frame )

rbg = self.factory.createRadioButtonGroup(frame)
frame = self.factory.createVBox(rbg)
Normal = self.factory.createRadioButton(self.factory.createLeft(frame), _("Normal (Install/Upgrade/Remove)"), self.actionValue == const.Actions.NORMAL)
Normal.setNotify(True)
rbg.addRadioButton(Normal)

Reinstall = self.factory.createRadioButton(self.factory.createLeft(frame), _("Reinstall"), self.actionValue == const.Actions.REINSTALL)
Reinstall.setNotify(True)
if self.parent.update_only :
Reinstall.setDisabled()
rbg.addRadioButton(Reinstall)

Downgrade = self.factory.createRadioButton(self.factory.createLeft(frame), _("Downgrade"), self.actionValue == const.Actions.DOWNGRADE)
Downgrade.setNotify(True)
if self.parent.update_only :
Downgrade.setDisabled()
rbg.addRadioButton(Downgrade)

DistroSync = self.factory.createRadioButton(self.factory.createLeft(frame), _("Distro Sync"), self.actionValue == const.Actions.DISTRO_SYNC)
DistroSync.setNotify(True)
rbg.addRadioButton(DistroSync)

align = self.factory.createRight(layout)
hbox = self.factory.createHBox(align)
okButton = self.factory.createPushButton(hbox, _("&Ok"))
cancelButton = self.factory.createPushButton(hbox, _("&Cancel"))
dlg.pollEvent()
dlg.setDefaultButton(cancelButton)

while (True) :
event = dlg.waitForEvent()
eventType = event.eventType()
#event type checking
if (eventType == yui.YEvent.CancelEvent) :
self.actionValue = self.savedActionValue
break
elif (eventType == yui.YEvent.WidgetEvent) :
# widget selected
widget = event.widget()

if (widget == cancelButton) :
self.actionValue = self.savedActionValue
break
elif (widget == okButton) :
break
elif (widget == Normal) :
self.actionValue = const.Actions.NORMAL
elif (widget == Reinstall) :
self.actionValue = const.Actions.REINSTALL
elif (widget == Downgrade) :
self.actionValue = const.Actions.DOWNGRADE
elif (widget == DistroSync) :
self.actionValue = const.Actions.DISTRO_SYNC

dlg.destroy()

#restore old application title
yui.YUI.app().setApplicationTitle(appTitle)

return self.actionValue


class TransactionResult:
'''
TransactionResult is a dialog that shows the transaction dependencies before
Expand Down
161 changes: 141 additions & 20 deletions dnfdragora/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ def __init__(self, options={}):
'downloads' : {}
} # obsoletes _files_to_download and _files_downloaded

self.packageActionValue = const.Actions.NORMAL
# TODO... _package_name, _gpg_confirm imported from old event management
# Try to remove them when fixing progress bar
self._package_name = None
Expand Down Expand Up @@ -600,6 +601,16 @@ def _setupUI(self) :
for k in self.fileMenu.keys():
self.fileMenu[k].this.own(False)

# building Actions menu
mItem = self.menubar.addMenu(_("&Actions"))
self.ActionMenu = {
'menu_name' : mItem,
'actions' : yui.YMenuItem(mItem, _("&Action on packages")),
}
#Items must be "disowned"
for k in self.ActionMenu.keys():
self.ActionMenu[k].this.own(False)

# # building Information menu
# mItem = self.menubar.addMenu(_("&Information"))
# self.infoMenu = {
Expand Down Expand Up @@ -1339,19 +1350,42 @@ def _populate_transaction(self) :
'''
Populate a transaction
'''
for action in const.QUEUE_PACKAGE_TYPES:
pkg_ids = self.packageQueue.get(action)
if self.packageActionValue == const.Actions.NORMAL:
for action in const.QUEUE_PACKAGE_TYPES.keys():
pkg_ids = self.packageQueue.get(action)
if len(pkg_ids) >0:
pkgs = [dnfdragora.misc.pkg_id_to_full_nevra(pkg_id) for pkg_id in pkg_ids]
logger.debug('adding: %s %s' %(const.QUEUE_PACKAGE_TYPES[action], pkgs))
if action == 'i':
self.backend.Install(pkgs, sync=True)
elif action == 'u':
self.backend.Update(pkgs, sync=True)
elif action == 'r':
self.backend.Remove(pkgs, sync=True)
else:
logger.error('Action %s not managed' % (action))
elif self.packageActionValue == const.Actions.REINSTALL:
pkg_ids = self.packageQueue.get('r')
if len(pkg_ids) >0:
pkgs = [dnfdragora.misc.pkg_id_to_full_nevra(pkg_id) for pkg_id in pkg_ids]
logger.debug('adding: %s %s' %(const.QUEUE_PACKAGE_TYPES[action], pkgs))
if action == 'i':
self.backend.Install(pkgs, sync=True)
elif action == 'u':
self.backend.Update(pkgs, sync=True)
elif action == 'r':
self.backend.Remove(pkgs, sync=True)
else:
logger.error('Action %s not managed' % (action))
pkgs = [dnfdragora.misc.pkg_id_to_full_nevra(pkg_id) for pkg_id in pkg_ids]
logger.debug('Reinstalling %s' %(pkgs))
self.backend.Reinstall(pkgs, sync=True)
elif self.packageActionValue == const.Actions.DOWNGRADE:
pkg_ids = self.packageQueue.get('r')
if len(pkg_ids) >0:
pkgs = [dnfdragora.misc.pkg_id_to_full_nevra(pkg_id) for pkg_id in pkg_ids]
logger.debug('Reinstalling %s' %(pkgs))
self.backend.Downgrade(pkgs, sync=True)
elif self.packageActionValue == const.Actions.DISTRO_SYNC:
pkg_ids = self.packageQueue.get('r')
pkgs=[]
if len(pkg_ids) >0:
pkgs.extend([ dnfdragora.misc.to_pkg_tuple(pkg_id)[0] for pkg_id in pkg_ids])
pkg_ids = self.packageQueue.get('u')
if len(pkg_ids) >0:
pkgs.extend([dnfdragora.misc.to_pkg_tuple(pkg_id)[0] for pkg_id in pkg_ids])
logger.debug('Distro Sync %s' %(pkgs))
self.backend.DistroSync(pkgs, sync=True)

def _undo_transaction(self):
'''
Expand Down Expand Up @@ -1467,6 +1501,82 @@ def _load_history(self, transactions):
hw = None
return undo

def _updateActionView(self, newAction):
'''
Prepare the dnfdragora view according to the selected new action.
Remove any selections if the action is changed (easy way to manage new action)
Force to rebuild the view if action is changed (easy way to manage new action)
Args:
newAction: action to be peroform on selected packages
Returns:
if packages view needs to be rebuilt
'''
rebuild_package_list = False
if newAction != self.packageActionValue:
rebuild_package_list = True
self.packageActionValue = newAction
ordered_filters = None
# reset any old selection to simplfy
self.packageQueue.clear()
#1. Changing Filters
filter_item = 'installed' #default
enable_select_all = False
apply_button_text = _("&Apply")
enable_apply_button = False
if newAction == const.Actions.NORMAL:
disable_select_all = True
#let's get back the last saved filter for NORMAL actions
filter_item = self.config.userPreferences['view']['filter']
ordered_filters = [ 'all', 'installed', 'to_update', 'not_installed' ]
if platform.machine() == "x86_64" :
ordered_filters.append('skip_other')
elif newAction == const.Actions.DOWNGRADE:
ordered_filters = [ 'installed' ]
apply_button_text = _("&Downgrade")
elif newAction == const.Actions.REINSTALL:
ordered_filters = [ 'installed' ]
apply_button_text = _("&Reinstall")
elif newAction == const.Actions.DISTRO_SYNC:
ordered_filters = [ 'installed', 'to_update' ]
apply_button_text = _("&Distro Sync")
#distro sync can ber run without any file selected (sync all)
enable_apply_button = True
if self.update_only:
filter_item = 'to_update'

for f in self.filters:
self.filters[f]['item'] = None

itemColl = yui.YItemCollection()
for f in ordered_filters:
item = yui.YItem(self.filters[f]['title'])
if filter_item == f:
item.setSelected(True)
# adding item to filters to find the item selected
self.filters[f]['item'] = item
itemColl.push_back(item)
item.this.own(False)

self.filter_box.startMultipleChanges()
self.filter_box.deleteAllItems()
self.filter_box.addItems(itemColl)
#self.filter_box.setEnabled(not self.update_only)
self.filter_box.doneMultipleChanges()

# fixing groups
view = self._viewNameSelected()
filter = self._filterNameSelected()
self._fillGroupTree()

#Change Apply Button text accordingly
self.applyButton.setLabel(apply_button_text)
self.applyButton.setEnabled(enable_apply_button)
#disable "select all" to avoid mistakes
self.checkAllButton.setEnabled(enable_select_all)


return rebuild_package_list

def handleevent(self):
"""
Event-handler for the maindialog
Expand Down Expand Up @@ -1506,6 +1616,11 @@ def handleevent(self):
elif item == self.optionsMenu['user_prefs'] :
up = dialogs.OptionDialog(self)
up.run()
elif item == self.ActionMenu['actions'] :
actDlg = dialogs.PackageActionDialog(self, self.packageActionValue)
newAction = actDlg.run()
rebuild_package_list = self._updateActionView(newAction)

#elif item == self.infoMenu['history'] :
# self.backend.GetHistoryByDays(0, 120) #TODO add in config file
elif item == self.helpMenu['help'] :
Expand Down Expand Up @@ -1632,10 +1747,13 @@ def handleevent(self):
else:
self.info.setValue("")

if self.packageQueue.total() > 0 and not self.applyButton.isEnabled():
if self.packageActionValue != const.Actions.DISTRO_SYNC:
if self.packageQueue.total() > 0 and not self.applyButton.isEnabled():
self.applyButton.setEnabled()
elif self.packageQueue.total() == 0 and self.applyButton.isEnabled():
self.applyButton.setEnabled(False)
elif not self.applyButton.isEnabled():
self.applyButton.setEnabled()
elif self.packageQueue.total() == 0 and self.applyButton.isEnabled():
self.applyButton.setEnabled(False)

# Save user prefs on exit
self.saveUserPreference()
Expand Down Expand Up @@ -2299,6 +2417,8 @@ def _OnBuildTransaction(self, info):
'Install': {},
'Remove':{},
'Upgrade': {},
'Reinstall':{},
'Downgrade':{},
}
for typ, action, who, unk, pkg in resolve:
'''
Expand Down Expand Up @@ -2346,11 +2466,12 @@ def _OnBuildTransaction(self, info):
# and we are here most probably for a GPG key confirmed during last transaction
#TODO dialog to confirm transaction, NOTE that there is no clean transaction if user say no
if ok and not self.always_yes and self._status != DNFDragoraStatus.RUN_TRANSACTION:
transaction_result_dlg = dialogs.TransactionResult(self)
ok = transaction_result_dlg.run(self.started_transaction)
if not ok:
self._enableAction(True)
return
if len(resolve) >0:
transaction_result_dlg = dialogs.TransactionResult(self)
ok = transaction_result_dlg.run(self.started_transaction)
if not ok:
self._enableAction(True)
return
elif ok !=0:
logger.error("Build transaction error %d", ok) #TODO read errors from dnf daemon

Expand Down

0 comments on commit 15dd2b3

Please sign in to comment.