Coverage for pandalone/xleash/_capture.py : 100%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
#!/usr/bin/env python # -*- coding: UTF-8 -*- # # Copyright 2014-2019European Commission (JRC); # Licensed under the EUPL (the 'Licence'); # You may not use this work except in compliance with the Licence. # You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl The algorithmic part of :term:`capturing`.
Prefer accessing the public members from the parent module. """
# TODO: Try different backends providing `colname` function. except ImportError: log.warning("One of `xlrd`, `...` libraries is needed, will crash later!")
"""When `True`, most coord-functions accept any 2-tuples."""
""" Thrown when :term:`targeting` fails. """
"L": Coords(0, -1), "U": Coords(-1, 0), "R": Coords(0, 1), "D": Coords(1, 0), }
"""Make *A1* :class:`Cell` from *resolved* coords, with rudimentary error-checking.
Examples::
>>> coords2Cell(row=0, col=0) Cell(row='1', col='A') >>> coords2Cell(row=0, col=26) Cell(row='1', col='AA')
>>> coords2Cell(row=10, col='.') Cell(row='11', col='.')
>>> coords2Cell(row=-3, col=-2) Traceback (most recent call last): AssertionError: negative row!
"""
""" Resolves special coords or converts Excel 1-based rows to zero-based, reporting invalids.
:param str, int coord: excel-row coordinate or one of ``^_.`` :return: excel row number, >= 0 :rtype: int
Examples::
>>> row = _row2num('1') >>> row 0 >>> row == _row2num(1) True
Negatives (from bottom) are preserved::
>>> _row2num('-1') -1
Fails ugly::
>>> _row2num('.') Traceback (most recent call last): ValueError: invalid literal for int() with base 10: '.' """
""" Resolves special coords or converts Excel A1 columns to a zero-based, reporting invalids.
:param str coord: excel-column coordinate or one of ``^_.`` :return: excel column number, >= 0 :rtype: int
Examples::
>>> col = _col2num('D') >>> col 3 >>> _col2num('d') == col True >>> _col2num('AaZ') 727 >>> _col2num('10') 9 >>> _col2num(9) 8
Negatives (from left-end) are preserved::
>>> _col2num('AaZ') 727
Fails ugly::
>>> _col2num('%$') Traceback (most recent call last): ValueError: substring not found
>>> _col2num([]) Traceback (most recent call last): TypeError: int() argument must be a string, a bytes-like object or a number, not 'list'
"""
else:
""" Translates special coords or converts Excel string 1-based rows/cols to zero-based, reporting invalids.
:param str cname: the coord-name, one of 'row', 'column' :param function cfunc: the function to convert coord ``str --> int`` :param int, str coord: the "A1" coord to translate :param int up_coord: the resolved *top* or *left* margin zero-based coordinate :param int dn_coord: the resolved *bottom* or *right* margin zero-based coordinate :param int, None base_coords: the resolved basis for dependent coord, if any
:return: the resolved coord or `None` if it were not a special coord.
Row examples::
>>> cname = 'row'
>>> r0 = _resolve_coord(cname, _row2num, '1', 1, 10) >>> r0 0 >>> r0 == _resolve_coord(cname, _row2num, 1, 1, 10) True >>> _resolve_coord(cname, _row2num, '^', 1, 10) 1 >>> _resolve_coord(cname, _row2num, '_', 1, 10) 10 >>> _resolve_coord(cname, _row2num, '.', 1, 10, 13) 13 >>> _resolve_coord(cname, _row2num, '-3', 0, 10) 8
But notice when base-cell missing::
>>> _resolve_coord(cname, _row2num, '.', 0, 10, base_coords=None) Traceback (most recent call last): ValueError: Cannot resolve `relative-row` without `base-coord`!
Other ROW error-checks::
>>> _resolve_coord(cname, _row2num, '0', 0, 10) Traceback (most recent call last): ValueError: invalid row('0') due to: Uncooked-coord cannot be zero!
>>> _resolve_coord(cname, _row2num, 'a', 0, 10) Traceback (most recent call last): ValueError: invalid row('a') due to: invalid literal for int() with base 10: 'a'
>>> _resolve_coord(cname, _row2num, None, 0, 10) Traceback (most recent call last): ValueError: invalid row(None) due to: int() argument must be a string, a bytes-like object or a number, not 'NoneType'
Column examples::
>>> cname = 'column'
>>> _resolve_coord(cname, _col2num, 'A', 1, 10) 0 >>> _resolve_coord(cname, _col2num, 'DADA', 1, 10) 71084 >>> _resolve_coord(cname, _col2num, '.', 1, 10, 13) 13 >>> _resolve_coord(cname, _col2num, '-4', 0, 10) 7
And COLUMN error-checks::
>>> _resolve_coord(cname, _col2num, None, 0, 10) Traceback (most recent call last): ValueError: invalid column(None) due to: int() argument must be a string, a bytes-like object or a number, not 'NoneType'
>>> _resolve_coord(cname, _col2num, 0, 0, 10) Traceback (most recent call last): ValueError: invalid column(0) due to: Uncooked-coord cannot be zero!
""" else:
# Resolve negatives as from the end.
""" Translates any special coords to absolute ones.
To get the margin_coords, use one of:
* :meth:`ABCSheet.get_margin_coords()` * :func:`.io.backend.margin_coords_from_states_matrix()`
:param Cell cell: The "A1" cell to translate its coords. :param Coords up_coords: the top-left resolved coords with full-cells :param Coords dn_coords: the bottom-right resolved coords with full-cells :param Coords base_coords: A resolved cell to base dependent coords (``.``). :return: the resolved cell-coords :rtype: Coords
Examples::
>>> up = Coords(1, 2) >>> dn = Coords(10, 6) >>> base = Coords(40, 50)
>>> _resolve_cell(Cell(col='B', row='5'), up, dn) Coords(row=4, col=1)
>>> _resolve_cell(Cell('^', '^'), up, dn) Coords(row=1, col=2)
>>> _resolve_cell(Cell('_', '_'), up, dn) Coords(row=10, col=6)
>>> base == _resolve_cell(Cell('.', '.'), up, dn, base) True
>>> _resolve_cell(Cell('-1', '-2'), up, dn) Coords(row=10, col=5)
>>> _resolve_cell(Cell('A', 'B'), up, dn) Traceback (most recent call last): ValueError: invalid cell(Cell(row='A', col='B')) due to: invalid row('A') due to: invalid literal for int() with base 10: 'A'
But notice when base-cell missing::
>>> _resolve_cell(Cell('1', '.'), up, dn) Traceback (most recent call last): ValueError: invalid cell(Cell(row='1', col='.')) due to: Cannot resolve `relative-col` without `base-coord`!
""" else: "row", _row2num, cell.row, up_coords[0], dn_coords[0], base_row ) "col", _col2num, cell.col, up_coords[1], dn_coords[1], base_col )
# VECTO_SLICE REVERSE COORD_INDEX "L": (1, -1, lambda r, c: (r, slice(None, c + 1))), "U": (0, -1, lambda r, c: (slice(None, r + 1), c)), "R": (1, 1, lambda r, c: (r, slice(c, None))), "D": (0, 1, lambda r, c: (slice(r, None), c)), }
"""Extract a slice from the states-matrix by starting from `land` and following `mov`."""
""" Follow moves from `land` and stop on the 1st full-cell.
:param np.ndarray states_matrix: A 2D-array with `False` wherever cell are blank or empty. Use :meth:`ABCSheet.get_states_matrix()` to derrive it. :param Coords dn_coords: the bottom-right for the top-left of full-cells :param Coords land: the landing-cell :param str moves: MUST not be empty :return: the identified target-cell's coordinates :rtype: Coords
Examples::
>>> states_matrix = np.array([ ... [0, 0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0, 0], ... [0, 0, 0, 1, 1, 1], ... [0, 0, 1, 0, 0, 1], ... [0, 0, 1, 1, 1, 1] ... ]) >>> args = (states_matrix, Coords(4, 5))
>>> _target_opposite(*(args + (Coords(0, 0), 'DR'))) Coords(row=3, col=2)
>>> _target_opposite(*(args + (Coords(0, 0), 'RD'))) Coords(row=2, col=3)
It fails if a non-empty target-cell cannot be found, or it ends-up beyond bounds::
>>> _target_opposite(*(args + (Coords(0, 0), 'D'))) Traceback (most recent call last): pandalone.xleash._capture.EmptyCaptureException: No opposite-target found while moving(D) from landing-Coords(row=0, col=0)!
>>> _target_opposite(*(args + (Coords(0, 0), 'UR'))) Traceback (most recent call last): pandalone.xleash._capture.EmptyCaptureException: No opposite-target found while moving(UR) from landing-Coords(row=0, col=0)!
But notice that the landing-cell maybe outside of bounds::
>>> _target_opposite(*(args + (Coords(3, 10), 'L'))) Coords(row=3, col=5)
"""
# if states_matrix[target].all(): # return Coords(*target)
# Limit negative coords, since they are valid indices. states_matrix, dn_coords, target, mov1 ) else:
""" :param np.ndarray states_matrix: A 2D-array with `False` wherever cell are blank or empty. Use :meth:`ABCSheet.get_states_matrix()` to derrive it. :param Coords dn_coords: the bottom-right for the top-left of full-cells :param Coords land: The landing-cell, which MUST be full! """ states_matrix, dn_coords, land, mov ) else:
""" Scan term:`exterior` row and column on specified `moves` and stop on the last full-cell.
:param np.ndarray states_matrix: A 2D-array with `False` wherever cell are blank or empty. Use :meth:`ABCSheet.get_states_matrix()` to derrive it. :param Coords dn_coords: the bottom-right for the top-left of full-cells :param Coords land: the landing-cell which MUST be within bounds :param moves: which MUST not be empty :return: the identified target-cell's coordinates :rtype: Coords
Examples::
>>> states_matrix = np.array([ ... [0, 0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0, 0], ... [0, 0, 0, 1, 1, 1], ... [0, 0, 1, 0, 0, 1], ... [0, 0, 1, 1, 1, 1] ... ]) >>> args = (states_matrix, Coords(4, 5))
>>> _target_same(*(args + (Coords(4, 5), 'U'))) Coords(row=2, col=5)
>>> _target_same(*(args + (Coords(4, 5), 'L'))) Coords(row=4, col=2)
>>> _target_same(*(args + (Coords(4, 5), 'UL', ))) Coords(row=2, col=2)
It fails if landing is empty or beyond bounds::
>>> _target_same(*(args + (Coords(2, 2), 'DR'))) Traceback (most recent call last): pandalone.xleash._capture.EmptyCaptureException: No same-target found while moving(DR) from landing-Coords(row=2, col=2)!
>>> _target_same(*(args + (Coords(10, 3), 'U'))) Traceback (most recent call last): pandalone.xleash._capture.EmptyCaptureException: No same-target found while moving(U) from landing-Coords(row=10, col=3)!
"""
states_matrix, dn_coords, np.asarray(land), mov )
""" Sorts rect-vertices in a 2D-array (with vertices in rows).
Example::
>>> _sort_rect((5, 3), (4, 6)) array([[4, 3], [5, 6]]) """
""" Applies the :term:`expansion-moves` based on the `states_matrix`.
:param state: :param Coords r1: any vertice of the rect to expand :param Coords r2: any vertice of the rect to expand :param np.ndarray states_matrix: A 2D-array with `False` wherever cell are blank or empty. Use :meth:`ABCSheet.get_states_matrix()` to derrive it. :param exp_moves: Just the parsed string, and not `None`. :return: a sorted rect top-left/bottom-right
Examples::
>>> states_matrix = np.array([ ... #0 1 2 3 4 5 ... [0, 0, 0, 0, 0, 0], #0 ... [0, 0, 1, 1, 1, 0], #1 ... [0, 1, 0, 0, 1, 0], #2 ... [0, 1, 1, 1, 1, 0], #3 ... [0, 0, 0, 0, 0, 1], #4 ... ], dtype=bool)
>>> r1, r2 = (Coords(2, 1), Coords(2, 1)) >>> _expand_rect(states_matrix, r1, r2, 'U') (Coords(row=2, col=1), Coords(row=2, col=1))
>>> r1, r2 = (Coords(3, 1), Coords(2, 1)) >>> _expand_rect(states_matrix, r1, r2, 'R') (Coords(row=2, col=1), Coords(row=3, col=4))
>>> r1, r2 = (Coords(2, 1), Coords(6, 1)) >>> _expand_rect(states_matrix, r1, r2, 'r') (Coords(row=2, col=1), Coords(row=6, col=5))
>>> r1, r2 = (Coords(2, 3), Coords(2, 3)) >>> _expand_rect(states_matrix, r1, r2, 'LURD') (Coords(row=1, col=1), Coords(row=3, col=4))
"""
"L": np.array([0, 0, -1, 0]), "R": np.array([0, 0, 0, 1]), "U": np.array([-1, 0, 0, 0]), "D": np.array([0, 1, 0, 0]), } "L": [0, 1, 2, 2], "R": [0, 1, 3, 3], "U": [0, 0, 2, 3], "D": [1, 1, 2, 3], }
# Sort rect's vertices top-left/bottom-right. # # ``[r1, r2, c1, c2]`` to use slices, below slice(*exp_vect_i[:2]), slice(*exp_vect_i[2:]) ]
states_matrix, up_dn_margins, st_edge, nd_edge=None, exp_moves=None, base_coords=None, ): """ Performs :term:`targeting`, :term:`capturing` and :term:`expansions` based on the :term:`states-matrix`.
To get the margin_coords, use one of:
* :meth:`ABCSheet.get_margin_coords()` * :func:`.io.backend.margin_coords_from_states_matrix()`
Its results can be fed into :func:`read_capture_values()`.
:param np.ndarray states_matrix: A 2D-array with `False` wherever cell are blank or empty. Use :meth:`ABCSheet.get_states_matrix()` to derrive it. :param (Coords, Coords) up_dn_margins: the top-left/bottom-right coords with full-cells :param Edge st_edge: "uncooked" as matched by regex :param Edge nd_edge: "uncooked" as matched by regex :param list or none exp_moves: Just the parsed string, and not `None`. :param Coords base_coords: The base for a :term:`dependent` :term:`1st` edge.
:return: a ``(Coords, Coords)`` with the 1st and 2nd :term:`capture-cell` ordered from top-left --> bottom-right. :rtype: tuple
:raises EmptyCaptureException: When :term:`targeting` failed, and no :term:`target` cell identified.
Examples:: >>> from pandalone.xleash import Edge, margin_coords_from_states_matrix
>>> states_matrix = np.array([ ... [0, 0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0, 0], ... [0, 0, 0, 1, 1, 1], ... [0, 0, 1, 0, 0, 1], ... [0, 0, 1, 1, 1, 1] ... ], dtype=bool) >>> up, dn = margin_coords_from_states_matrix(states_matrix)
>>> st_edge = Edge(Cell('1', 'A'), 'DR') >>> nd_edge = Edge(Cell('.', '.'), 'DR') >>> resolve_capture_rect(states_matrix, (up, dn), st_edge, nd_edge) (Coords(row=3, col=2), Coords(row=4, col=2))
Using dependenent coordinates for the 2nd edge::
>>> st_edge = Edge(Cell('_', '_'), None) >>> nd_edge = Edge(Cell('.', '.'), 'UL') >>> rect = resolve_capture_rect(states_matrix, (up, dn), st_edge, nd_edge) >>> rect (Coords(row=2, col=2), Coords(row=4, col=5))
Using sheet's margins::
>>> st_edge = Edge(Cell('^', '_'), None) >>> nd_edge = Edge(Cell('_', '^'), None) >>> rect == resolve_capture_rect(states_matrix, (up, dn), st_edge, nd_edge) True
Walking backwards::
>>> st_edge = Edge(Cell('^', '_'), 'L') # Landing is full, so 'L' ignored. >>> nd_edge = Edge(Cell('_', '_'), 'L', '+') # '+' or would also stop. >>> rect == resolve_capture_rect(states_matrix, (up, dn), st_edge, nd_edge) True
"""
else:
else:
nd_edge.mod == "+" or nd_edge.land == Cell(".", ".") and nd_edge.mod != "?" ): else:
else:
|