File: C:/Users/fred/anaconda3/Lib/site-packages/anaconda_navigator/widgets/manager/model.py
# -*- coding: utf-8 -*-
# pylint: disable=invalid-name
# -----------------------------------------------------------------------------
# Copyright (c) 2016-2017 Anaconda, Inc.
#
# May be copied and distributed freely only as part of an Anaconda or
# Miniconda installation.
# -----------------------------------------------------------------------------
"""Conda Packages Table model."""
from qtpy.QtCore import QAbstractTableModel, QModelIndex, Qt # pylint: disable=no-name-in-module
from anaconda_navigator.utils import constants as C
from anaconda_navigator.utils import sort_versions
from anaconda_navigator.utils.styles import SASS_VARIABLES
class CondaPackagesModel(QAbstractTableModel): # pylint: disable=too-many-public-methods
"""Abstract Model to handle the packages in a conda environment."""
def __init__(self, parent, packages, data):
"""Abstract Model to handle the packages in a conda environment."""
super().__init__(parent)
self._parent = parent
self._packages = packages
self._rows = data
self._name_to_index = {r[C.COL_NAME]: i for i, r in enumerate(data)}
self._palette = SASS_VARIABLES.process_palette()
def _update_cell(self, row, column):
start = self.index(row, column)
end = self.index(row, column)
self.dataChanged.emit(start, end)
@staticmethod
def flags(index):
"""Override Qt method."""
column = index.column()
if index.isValid() and (column in [C.COL_START, C.COL_END]):
return Qt.ItemFlags(Qt.ItemIsEnabled)
return Qt.ItemFlags(Qt.ItemIsEnabled)
def data( # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements
self, index, role=Qt.DisplayRole,
):
"""Override Qt method."""
if not index.isValid() or not 0 <= index.row() < len(self._rows):
return None
row = index.row()
column = index.column()
P = self._palette # Include the _palette directly here.
if self._rows[row] == row:
action = C.ACTION_NONE
type_ = ''
name = ''
description = ''
version = '-'
status = -1
else:
action = self._rows[row][C.COL_ACTION]
type_ = self._rows[row][C.COL_PACKAGE_TYPE]
name = self._rows[row][C.COL_NAME]
description = self._rows[row][C.COL_DESCRIPTION]
version = self._rows[row][C.COL_VERSION]
status = self._rows[row][C.COL_STATUS]
is_upgradable = self.is_upgradable(self.index(row, C.COL_VERSION))
if role == Qt.DisplayRole:
if column == C.COL_PACKAGE_TYPE:
return type_
if column == C.COL_NAME:
return name
if column == C.COL_VERSION:
return version
if column == C.COL_STATUS:
return status
if column == C.COL_DESCRIPTION:
return description
if column == C.COL_ACTION:
return action
elif role == Qt.TextAlignmentRole:
if column in [C.COL_NAME, C.COL_DESCRIPTION]:
return int(Qt.AlignLeft | Qt.AlignVCenter)
if column in [C.COL_VERSION]:
return int(Qt.AlignLeft | Qt.AlignVCenter)
return int(Qt.AlignCenter)
elif role == Qt.DecorationRole:
if column == C.COL_ACTION:
if action == C.ACTION_NONE:
if status == C.NOT_INSTALLED:
return P['ICON_ACTION_NOT_INSTALLED']
if status in [C.UPGRADABLE, C.MIXGRADABLE]:
return P['ICON_ACTION_INSTALLED']
if status in [C.INSTALLED, C.DOWNGRADABLE, C.MIXGRADABLE]:
return P['ICON_ACTION_INSTALLED']
elif action == C.ACTION_INSTALL:
return P['ICON_ACTION_ADD']
elif action == C.ACTION_REMOVE:
return P['ICON_ACTION_REMOVE']
elif action == C.ACTION_UPGRADE:
return P['ICON_ACTION_UPGRADE']
elif action == C.ACTION_UPDATE:
return P['ICON_ACTION_UPGRADE']
elif action == C.ACTION_DOWNGRADE:
return P['ICON_ACTION_DOWNGRADE']
else:
return None
elif column == C.COL_PACKAGE_TYPE:
if type_ == C.CONDA_PACKAGE:
return P['ICON_ANACONDA']
if type_ == C.PIP_PACKAGE:
return P['ICON_PYTHON']
return None
elif column == C.COL_VERSION:
if is_upgradable:
return P['ICON_UPGRADE_ARROW']
return P['ICON_SPACER']
elif role == Qt.ToolTipRole:
if column == C.COL_PACKAGE_TYPE:
if type_ == C.CONDA_PACKAGE:
return 'Conda package'
if type_ == C.PIP_PACKAGE:
return 'Python package'
elif (column == C.COL_VERSION) and is_upgradable:
return 'Update available'
elif role == Qt.ForegroundRole:
if column in [C.COL_NAME, C.COL_DESCRIPTION]:
if status in [C.NOT_INSTALLED]:
return P['COLOR_FOREGROUND_NOT_INSTALLED']
elif (column in [C.COL_VERSION]) and is_upgradable:
return P['COLOR_FOREGROUND_UPGRADE']
elif (role == Qt.SizeHintRole) and (column in [C.ACTION_COLUMNS, C.COL_PACKAGE_TYPE]):
return P['SIZE_ICONS']
return None
@staticmethod
def headerData(section, orientation, role=Qt.DisplayRole): # pylint: disable=too-many-return-statements
"""Override Qt method."""
if orientation == Qt.Horizontal:
if role == Qt.TextAlignmentRole:
return int(Qt.AlignLeft | Qt.AlignVCenter)
if (role == Qt.ToolTipRole) and (section == C.COL_PACKAGE_TYPE):
return 'Package type: Conda, Pip'
if role == Qt.DisplayRole:
if section == C.COL_PACKAGE_TYPE:
return 'T'
if section == C.COL_NAME:
return 'Name'
if section == C.COL_VERSION:
return 'Version'
if section == C.COL_DESCRIPTION:
return 'Description'
if section == C.COL_STATUS:
return 'Status'
return None
return None
def rowCount(self, index=QModelIndex()): # pylint: disable=unused-argument
"""Override Qt method."""
return len(self._rows)
@staticmethod
def columnCount(index=QModelIndex()): # pylint: disable=unused-argument
"""Override Qt method."""
return len(C.COLUMNS)
def row(self, rownum):
"""Return the row data."""
return self._rows[rownum]
def first_index(self):
"""Return the first model index."""
return self.index(0, 0)
def last_index(self):
"""Return the last model index."""
return self.index(self.rowCount() - 1, self.columnCount() - 1)
def update_row_icon(self, row, column):
"""Update the model index icon."""
if column in C.ACTION_COLUMNS:
r = self._rows[row]
actual_state = r[column]
r[column] = not actual_state
self._rows[row] = r
self._update_cell(row, column)
def is_installable(self, model_index):
"""Return if the pacakge index can be installed."""
row = model_index.row()
status = self._rows[row][C.COL_STATUS]
return status == C.NOT_INSTALLED
def is_removable(self, model_index):
"""Return if the installed pacakge index can be removed."""
row = model_index.row()
status = self._rows[row][C.COL_STATUS]
return status in [C.UPGRADABLE, C.DOWNGRADABLE, C.INSTALLED, C.MIXGRADABLE]
def is_upgradable(self, model_index):
"""Return if the installed pacakge index can be upgraded."""
row = model_index.row()
status = self._rows[row][C.COL_STATUS]
return status in [C.UPGRADABLE, C.MIXGRADABLE]
def is_downgradable(self, model_index):
"""Return if the installed pacakge index can be downgraded."""
row = model_index.row()
status = self._rows[row][C.COL_STATUS]
return status in [C.DOWNGRADABLE, C.MIXGRADABLE]
def action_status(self, model_index):
"""Return the current action status."""
row = model_index.row()
action_status = self._rows[row][C.COL_ACTION]
return action_status
def set_action_status(self, model_index, status, version=None):
"""Set index status action."""
row = model_index.row()
self._rows[row][C.COL_ACTION] = status
self._rows[row][C.COL_ACTION_VERSION] = version
self._update_cell(row, model_index.column())
def clear_actions(self):
"""Clear the selected conda actions."""
for i, row in enumerate(self._rows):
row[C.COL_ACTION] = C.ACTION_NONE
row[C.COL_ACTION_VERSION] = None
self._update_cell(i, C.COL_ACTION)
self._update_cell(i, C.COL_ACTION_VERSION)
def count_remove_actions(self):
"""Return the number of conda remove actions selected."""
count = 0
for row in self._rows:
action = row[C.COL_ACTION]
type_ = row[C.COL_PACKAGE_TYPE]
if (action == C.ACTION_REMOVE) and (type_ == C.CONDA_PACKAGE):
count += 1
return count
def count_install_actions(self):
"""Return number of install/upgrade/downgrade actions selected."""
count = 0
for row in self._rows:
type_ = row[C.COL_PACKAGE_TYPE]
action = row[C.COL_ACTION]
if (action in [C.ACTION_DOWNGRADE, C.ACTION_INSTALL, C.ACTION_UPGRADE]) and (type_ == C.CONDA_PACKAGE):
count += 1
return count
def count_update_actions(self):
"""Return number of update (no version given) actions selected."""
count = 0
for row in self._rows:
type_ = row[C.COL_PACKAGE_TYPE]
action = row[C.COL_ACTION]
if (action in [C.ACTION_UPDATE]) and (type_ == C.CONDA_PACKAGE):
count += 1
return count
def get_actions(self):
"""Return the selected conda actions."""
dic = {
C.CONDA_PACKAGE: {
C.ACTION_INSTALL: [],
C.ACTION_REMOVE: [],
C.ACTION_UPGRADE: [],
C.ACTION_DOWNGRADE: [],
C.ACTION_UPDATE: [],
},
C.PIP_PACKAGE: {
C.ACTION_REMOVE: [],
}
}
for row in self._rows:
action = row[C.COL_ACTION]
name = row[C.COL_NAME]
type_ = row[C.COL_PACKAGE_TYPE]
action_version = row[C.COL_ACTION_VERSION]
current_version = self.get_package_version(name)
if action != C.ACTION_NONE:
version_from = current_version
version_to = action_version
dic[type_][action].append({
'name': name,
'version_from': version_from,
'version_to': version_to,
})
return dic
def get_action_count(self):
"""Count selected actions."""
count = 0
for row in self._rows: # pylint: disable=consider-using-enumerate
action = row[C.COL_ACTION]
if action != C.ACTION_NONE:
count += 1
return count
def get_package_versions(self, name):
"""
Return the package canonical name for a given package `name`.
name : str
Name of the package
"""
package_data = self._packages.get(name)
versions = []
if package_data:
versions = sort_versions(list(package_data.get('versions', [])))
return versions
def get_package_version(self, name):
"""Return the package version for a given package `name`."""
if name in self._name_to_index:
index = self._name_to_index[name]
version = self.row(index)[C.COL_VERSION]
return version.replace(C.UPGRADE_SYMBOL, '')
return ''