Source code for ooodev.utils.data_type.cell_obj

from __future__ import annotations
import contextlib
from typing import cast, overload, TYPE_CHECKING
from dataclasses import dataclass, field
from weakref import ref
from ooo.dyn.table.cell_address import CellAddress

from ooodev.loader import lo as mLo
from ooodev.utils import table_helper as mTb
from ooodev.utils.validation import check
from ooodev.loader.inst.doc_type import DocType

if TYPE_CHECKING:
    from ooodev.calc.calc_doc import CalcDoc


[docs]@dataclass(frozen=True) class CellObj: """ Cell Parts .. seealso:: - :ref:`ooodev.utils.data_type.cell_obj.CellObj` .. versionchanged:: 0.32.0 If index is set to ``-2`` then no attempt is made to get index from spreadsheet. .. versionadded:: 0.8.2 """ # region init col: str """Column such as ``A``""" row: int """One based row such as ``125``""" sheet_idx: int = -1 """ Sheet index that this cell value belongs to. If value is ``-1`` then the active spreadsheet, if available, is used to get the sheet index. If the value is ``-2`` then no sheet index is applied. """ range_obj: mRngObj.RangeObj | None = field(repr=False, hash=False, default=None) """Range Object that instance is part of""" def __post_init__(self): object.__setattr__(self, "col", self.col.upper()) try: # convert col to index for the purpose of validation _ = mTb.TableHelper.col_name_to_int(name=self.col) except ValueError as e: raise AssertionError from e check(self.row >= 1, f"{self}", f"Expected a row of 1 or greater. Got: {self.row}") if self.sheet_idx == -1: # do not use the commented out code below!!! It will cause recursion error. # if self.range_obj: # if self.range_obj.sheet_idx >= 0: # object.__setattr__(self, "sheet_idx", self.range_obj.sheet_idx) # else: with contextlib.suppress(Exception): # pylint: disable=no-member if mLo.Lo.is_loaded and mLo.Lo.current_doc.DOC_TYPE == DocType.CALC: doc = cast("CalcDoc", mLo.Lo.current_doc) sheet = doc.get_active_sheet() idx = sheet.get_sheet_index() object.__setattr__(self, "sheet_idx", idx) # endregion init # region static methods
[docs] def set_sheet_index(self, idx: int | None = None) -> CellObj: """ Set the sheet index for the cell. If ``idx`` is ``None`` then the active sheet index is used. Args: idx (int, optional): Sheet index, Default ``None``. Returns: RangeObj: Self .. versionadded:: 0.32.0 """ if idx is None: try: # pylint: disable=no-member if mLo.Lo.is_loaded and mLo.Lo.current_doc.DOC_TYPE == DocType.CALC: doc = cast("CalcDoc", mLo.Lo.current_doc) sheet = doc.get_active_sheet() idx = sheet.get_sheet_index() object.__setattr__(self, "sheet_idx", idx) except Exception: object.__setattr__(self, "sheet_idx", -1) return self if idx != self.sheet_idx: object.__setattr__(self, "sheet_idx", idx) return self
# region from_cell() @overload @staticmethod def from_cell(cell_val: str) -> CellObj: ... @overload @staticmethod def from_cell(cell_val: CellObj) -> CellObj: ... @overload @staticmethod def from_cell(cell_val: CellAddress) -> CellObj: ... @overload @staticmethod def from_cell(cell_val: mCellVals.CellValues) -> CellObj: ...
[docs] @staticmethod def from_cell(cell_val: str | CellAddress | mCellVals.CellValues | CellObj) -> CellObj: """ Gets a ``CellObj`` instance from a string Args: cell_val (str | CellAddress | CellValues | CellObj): Cell value. If ``cell_val`` is `CellObj`` then that instance is returned. Returns: CellObj: Cell Object Note: If a range name such as ``A23:G45`` or ``Sheet1.A23:G45`` then only the first cell is used. """ if isinstance(cell_val, CellObj): return cell_val if isinstance(cell_val, str): # split will cover if a range is passed in, return first cell parts = mTb.TableHelper.get_cell_parts(cell_val) idx = -1 if parts.sheet else -2 if idx == -1: with contextlib.suppress(Exception): # pylint: disable=no-member if mLo.Lo.is_loaded and mLo.Lo.current_doc.DOC_TYPE == DocType.CALC: doc = cast("CalcDoc", mLo.Lo.current_doc) sheet = sheet = doc.get_sheet(sheet_name=parts.sheet) idx = sheet.get_sheet_index() return CellObj(col=parts.col, row=parts.row, sheet_idx=idx) cv = mCellVals.CellValues.from_cell(cell_val) col = mTb.TableHelper.make_column_name(cv.col, True) return CellObj(col=col, row=cv.row + 1, sheet_idx=cv.sheet_idx)
# endregion from_cell()
[docs] @staticmethod def from_idx(col_idx: int, row_idx: int, sheet_idx: int = -1) -> CellObj: """ Gets a ``CellObj`` from zero-based col and row indexes Args: col_idx (int): Column index row_idx (int): Row index sheet_idx (int, optional): Sheet index Returns: CellObj: Cell object """ col = mTb.TableHelper.make_column_name(col=col_idx, zero_index=True) return CellObj(col=col, row=row_idx + 1, sheet_idx=sheet_idx)
# endregion static methods # region methods
[docs] def to_string(self, include_sheet_name: bool = False) -> str: """ Get a string representation of range Args: include_sheet_name (bool, optional): If ``True`` and there is a sheet name then it is included in format of ``Sheet1.A2``; Otherwise format of ``A2``.Defaults to ``False``. Returns: str: Gets a string representation such as ``A1:T12`` .. versionadded:: 0.47.17 """ s = f"{self.col}{self.row}" if include_sheet_name and self.sheet_name: s = f"{self.sheet_name}.{s}" return s
[docs] def get_cell_values(self) -> mCellVals.CellValues: """ Gets cell values Returns: CellValues: Cell Values instance. """ return mCellVals.CellValues.from_cell(self.get_cell_address())
[docs] def get_cell_address(self) -> CellAddress: """ Gets a cell address Returns: CellAddress: Cell Address """ column = mTb.TableHelper.col_name_to_int(self.col, True) return CellAddress(Sheet=self.sheet_idx, Column=column, Row=self.row - 1)
[docs] def get_range_obj(self) -> mRngObj.RangeObj: """ Gets a Range object that has start and end column set to this instance cell values. Returns: RangeObj: Range Object """ return mRngObj.RangeObj( col_start=self.col, col_end=self.col, row_start=self.row, row_end=self.row, sheet_idx=self.sheet_idx )
[docs] def copy(self) -> CellObj: """ Copy the current instance. Returns: CellObj: New instance of CellObj .. versionadded:: 0.47.5 """ return self.__copy__()
# endregion methods # region dunder methods def __copy__(self) -> CellObj: if self.range_obj is None: co = CellObj(col=self.col, row=self.row, sheet_idx=self.sheet_idx, range_obj=None) else: rng_obj = self.range_obj.copy() co = CellObj(col=self.col, row=self.row, sheet_idx=self.sheet_idx, range_obj=rng_obj) if hasattr(self, "_sheet_name"): object.__setattr__(co, "_sheet_name", getattr(self, "_sheet_name")) return co def __str__(self) -> str: return self.to_string(False) def __eq__(self, other: object) -> bool: if isinstance(other, CellObj): return self.sheet_idx == other.sheet_idx and self.col == other.col and self.row == other.row return str(self) == other.upper() if isinstance(other, str) else False def __lt__(self, other: object) -> bool: try: oth = None if isinstance(other, str): oth = CellObj.from_cell(other) elif isinstance(other, CellObj): oth = other if oth is not None: return self.get_cell_values() < oth.get_cell_values() except IndexError: raise except AssertionError as e: raise IndexError from e except Exception: pass return NotImplemented def __le__(self, other: object) -> bool: return self == other or self < other def __gt__(self, other: object) -> bool: try: oth = None if isinstance(other, str): oth = CellObj.from_cell(other) elif isinstance(other, CellObj): oth = other if oth is not None: return self.get_cell_values() > oth.get_cell_values() except IndexError: raise except AssertionError as e: raise IndexError from e except Exception: pass return NotImplemented def __ge__(self, other: object) -> bool: return self == other or self > other def __add__(self, other: object) -> CellObj: try: if isinstance(other, str): # string mean add column col = cast(mCol.ColObj, self.col_obj + other) return CellObj(col=col.value, row=self.row, sheet_idx=self.sheet_idx, range_obj=self.range_obj) if isinstance(other, mCol.ColObj): return CellObj(col=other.value, row=self.row, sheet_idx=self.sheet_idx, range_obj=self.range_obj) if isinstance(other, mRow.RowObj): return CellObj(col=self.col, row=other.value, sheet_idx=self.sheet_idx, range_obj=self.range_obj) if isinstance(other, int): return CellObj(col=self.col, row=self.row + other, sheet_idx=self.sheet_idx, range_obj=self.range_obj) if isinstance(other, CellObj): # add row and column: col = self.col_obj + other.col_obj row = self.row_obj + other.row_obj return CellObj(col=col.value, row=row.value, sheet_idx=self.sheet_idx, range_obj=self.range_obj) except IndexError: raise except AssertionError as e: raise IndexError from e except Exception: pass return NotImplemented def __sub__(self, other: object) -> CellObj: try: if isinstance(other, str): # string mean subtract column col = self.col_obj - other return CellObj(col=col.value, row=self.row, sheet_idx=self.sheet_idx, range_obj=self.range_obj) if isinstance(other, mCol.ColObj): return CellObj(col=other.value, row=self.row, sheet_idx=self.sheet_idx, range_obj=self.range_obj) if isinstance(other, mRow.RowObj): return CellObj(col=self.col, row=other.value, sheet_idx=self.sheet_idx, range_obj=self.range_obj) if isinstance(other, int): return CellObj(col=self.col, row=self.row - other, sheet_idx=self.sheet_idx, range_obj=self.range_obj) if isinstance(other, CellObj): # subtract row and column: col = self.col_obj - other.col_obj row = self.row_obj - other.row_obj return CellObj(col=col.value, row=row.value, sheet_idx=self.sheet_idx, range_obj=self.range_obj) except IndexError: raise except AssertionError as e: raise IndexError from e except Exception: pass return NotImplemented def __mul__(self, other: object) -> CellObj: try: if isinstance(other, str): # string mean subtract column col = self.col_obj * other return CellObj(col=col.value, row=self.row, sheet_idx=self.sheet_idx, range_obj=self.range_obj) if isinstance(other, mCol.ColObj): return CellObj(col=other.value, row=self.row, sheet_idx=self.sheet_idx, range_obj=self.range_obj) if isinstance(other, mRow.RowObj): return CellObj(col=self.col, row=other.value, sheet_idx=self.sheet_idx, range_obj=self.range_obj) if isinstance(other, int): row = self.row_obj * other return CellObj(col=self.col, row=row.value, sheet_idx=self.sheet_idx, range_obj=self.range_obj) if isinstance(other, CellObj): # multiply row and column: col = self.col_obj * other.col_obj row = self.row_obj * other.row_obj return CellObj(col=col.value, row=row.value, sheet_idx=self.sheet_idx, range_obj=self.range_obj) except IndexError: raise except AssertionError as e: raise IndexError from e except Exception: pass return NotImplemented def __rmul__(self, other: object) -> CellObj: return self if other == 0 else self.__mul__(other) def __truediv__(self, other: object) -> CellObj: try: if isinstance(other, str): col = self.col_obj / other return CellObj(col=col.value, row=self.row, sheet_idx=self.sheet_idx, range_obj=self.range_obj) if isinstance(other, mCol.ColObj): return CellObj(col=other.value, row=self.row, sheet_idx=self.sheet_idx, range_obj=self.range_obj) if isinstance(other, mRow.RowObj): return CellObj(col=self.col, row=other.value, sheet_idx=self.sheet_idx, range_obj=self.range_obj) if isinstance(other, int): row = self.row_obj / other return CellObj(col=self.col, row=row.value, sheet_idx=self.sheet_idx, range_obj=self.range_obj) if isinstance(other, CellObj): # divide row and column: col = self.col_obj / other.col_obj row = self.row_obj / other.row_obj return CellObj(col=col.value, row=row.value, sheet_idx=self.sheet_idx, range_obj=self.range_obj) except IndexError: raise except AssertionError as e: raise IndexError from e except Exception: pass return NotImplemented def __rtruediv__(self, other: object) -> CellObj: try: i = self.col_obj.index + 1 if isinstance(other, str): oth = CellObj.from_cell(other) return oth.__truediv__(self.col) if isinstance(other, (int, float)): check(other != 0, f"{repr(self)}", "Cannot be divided by zero") check(other >= i, f"{repr(self)}", "Cannot be divided by greater number") oth = CellObj.from_idx(col_idx=self.col_obj.index, row_idx=round(other - 1), sheet_idx=self.sheet_idx) return oth.__truediv__(self.row) except IndexError: raise except AssertionError as e: raise IndexError from e except Exception: pass return NotImplemented def __hash__(self) -> int: return hash((self.sheet_idx, self.row, self.col)) # endregion dunder methods # region properties @property def sheet_name(self) -> str: """ Gets sheet name .. versionadded:: 0.47.17 """ # return self._sheet_name try: return self._sheet_name # type: ignore except AttributeError: name = "" if self.sheet_idx < 0: return name with contextlib.suppress(Exception): # pylint: disable=no-member if mLo.Lo.is_loaded and mLo.Lo.current_doc.DOC_TYPE == DocType.CALC: doc = cast("CalcDoc", mLo.Lo.current_doc) sheet = doc.sheets[self.sheet_idx] name = sheet.name object.__setattr__(self, "_sheet_name", name) return name @property def col_obj(self) -> mCol.ColObj: """Gets Column object""" # pylint: disable=no-member try: inf = self._col_info # type: ignore if inf() is None: raise AttributeError return inf() except AttributeError: obj = mCol.ColObj(value=self.col, cell_obj=self) object.__setattr__(self, "_col_info", ref(obj)) return self._col_info() # type: ignore @property def row_obj(self) -> mRow.RowObj: """Gets Row object""" # pylint: disable=no-member try: inf = self._row_info # type: ignore if inf() is None: raise AttributeError return inf() except AttributeError: obj = mRow.RowObj(value=self.row, cell_obj=self) object.__setattr__(self, "_row_info", ref(obj)) return self._row_info() # type: ignore @property def right(self) -> CellObj: """Gets the cell to the right of current cell""" # pylint: disable=no-member try: co = self._cell_right # type: ignore if co() is None: raise AttributeError return co() except AttributeError: co = CellObj(col=self.col_obj.next.value, row=self.row, sheet_idx=self.sheet_idx, range_obj=self.range_obj) object.__setattr__(self, "_cell_right", ref(co)) return self._cell_right() # type: ignore @property def left(self) -> CellObj: """ Gets the cell to the left of current cell Raises: IndexError: If cell left is out of range """ # pylint: disable=no-member # pylint: disable=try-except-raise try: co = self._cell_left # type: ignore if co() is None: raise AttributeError return co() except AttributeError: try: co = CellObj( col=self.col_obj.prev.value, row=self.row, sheet_idx=self.sheet_idx, range_obj=self.range_obj ) object.__setattr__(self, "_cell_left", ref(co)) except IndexError: raise except AssertionError as e: raise IndexError from e return self._cell_left() # type: ignore @property def down(self) -> CellObj: """Gets the cell below of current cell""" # pylint: disable=no-member try: co = self._cell_down # type: ignore if co() is None: raise AttributeError return co() except AttributeError: co = CellObj(col=self.col, row=self.row_obj.next.value, sheet_idx=self.sheet_idx, range_obj=self.range_obj) object.__setattr__(self, "_cell_down", ref(co)) return self._cell_down() # type: ignore @property def up(self) -> CellObj: """ Gets the cell above of current cell Raises: IndexError: If cell above is out of range """ # pylint: disable=no-member # pylint: disable=try-except-raise try: co = self._cell_up # type: ignore if co() is None: raise AttributeError return co() except AttributeError: try: co = CellObj( col=self.col, row=self.row_obj.prev.value, sheet_idx=self.sheet_idx, range_obj=self.range_obj ) object.__setattr__(self, "_cell_up", ref(co)) except IndexError: raise except AssertionError as e: raise IndexError from e return self._cell_up() # type: ignore
# endregion properties from ooodev.utils.data_type import cell_values as mCellVals # noqa # type: ignore from ooodev.utils.data_type import col_obj as mCol # noqa # type: ignore from ooodev.utils.data_type import range_obj as mRngObj # noqa # type: ignore from ooodev.utils.data_type import row_obj as mRow # noqa # type: ignore