Exemple #1
0
    def _emit(self, value=None):
        # resolve the range into cells
        sheet = self.cell and self.cell.sheet or ''
        value = value is not None and value or self.value
        if '!' in value:
            sheet = ''
        try:
            addr_str = value.replace('$', '')
            address = AddressRange.create(addr_str,
                                          sheet=sheet,
                                          cell=self.cell)
        except ValueError:
            # check for table relative address
            table_name = None
            if self.cell:
                excel = self.cell.excel
                if excel and '[' in addr_str:
                    table_name = excel.table_name_containing(self.cell.address)

            if not table_name:
                logging.getLogger('pycel').warning(
                    'Table Name not found: {}'.format(addr_str))
                return '"{}"'.format(NAME_ERROR)

            addr_str = '{}{}'.format(table_name, addr_str)
            address = AddressRange.create(addr_str,
                                          sheet=self.cell.address.sheet,
                                          cell=self.cell)

        if isinstance(address, AddressMultiAreaRange):
            return ', '.join(self._emit(value=str(addr)) for addr in address)
        else:
            template = '_R_("{}")' if address.is_range else '_C_("{}")'
            return template.format(address)
Exemple #2
0
    def emit(self):
        # resolve the range into cells
        sheet = self.cell and self.cell.sheet or ''
        if '!' in self.value:
            sheet = ''
        try:
            addr_str = self.value.replace('$', '')
            address = AddressRange.create(addr_str,
                                          sheet=sheet,
                                          cell=self.cell)
        except ValueError:
            # check for table relative address
            table_name = None
            if self.cell:
                excel = self.cell.excel
                if excel and '[' in addr_str:
                    table_name = excel.table_name_containing(self.cell.address)

            if not table_name:
                return '"{}"'.format(NAME_ERROR)

            addr_str = '{}{}'.format(table_name, addr_str)
            address = AddressRange.create(addr_str,
                                          sheet=self.cell.address.sheet,
                                          cell=self.cell)

        template = '_R_("{}")' if address.is_range else '_C_("{}")'
        return template.format(address)
Exemple #3
0
    def emit(self):
        # resolve the range into cells
        sheet = self.cell and self.cell.sheet or ''
        if '!' in self.value:
            sheet = ''
        try:
            addr_str = self.value.replace('$', '')
            address = AddressRange.create(addr_str, sheet=sheet, cell=self.cell)
        except ValueError:
            # check for table relative address
            table_name = None
            if self.cell:
                excel = self.cell.excel
                if excel and '[' in addr_str:
                    table_name = excel.table_name_containing(self.cell.address)

            if not table_name:
                logging.getLogger('pycel').warning(
                    'Table Name not found: {}'.format(addr_str))
                return '"{}"'.format(NAME_ERROR)

            addr_str = '{}{}'.format(table_name, addr_str)
            address = AddressRange.create(
                addr_str, sheet=self.cell.address.sheet, cell=self.cell)

        template = '_R_("{}")' if address.is_range else '_C_("{}")'
        return template.format(address)
Exemple #4
0
def indirect(ref_text, a1=True, sheet=''):
    # Excel reference: https://support.microsoft.com/en-us/office/
    #   indirect-function-474b3a3a-8a26-4f44-b491-92b6306fa261
    try:
        address = AddressRange.create(ref_text)
    except ValueError:
        return REF_ERROR
    if address.row > MAX_ROW or address.col_idx > MAX_COL:
        return REF_ERROR
    if not address.has_sheet:
        address = AddressRange.create(address, sheet=sheet)
    return address
Exemple #5
0
def test_address_range():
    a = AddressRange('a1:b2')
    b = AddressRange('A1:B2')
    c = AddressRange(a)

    assert a == b
    assert b == c

    assert b == AddressRange(b)
    assert b == AddressRange.create(b)

    assert AddressRange('sh!a1:b2') == AddressRange(a, sheet='sh')
    assert AddressCell('C13') == AddressCell('R13C3')

    with pytest.raises(ValueError):
        AddressRange(AddressRange('sh!a1:b2'), sheet='sheet')

    a = AddressRange('A:A')
    assert 'A' == a.start.column
    assert 'A' == a.end.column
    assert 0 == a.start.row
    assert 0 == a.end.row

    b = AddressRange('1:1')
    assert '' == b.start.column
    assert '' == b.end.column
    assert 1 == b.start.row
    assert 1 == b.end.row
Exemple #6
0
    def evaluate(self, address):
        """ evaluate a cell or cells in the spreadsheet

        :param address: str, AddressRange, AddressCell or a tuple or list
            or iterable of these three
        :return: evaluted value/values
        """

        try:
            not_in_cell_map = address not in self.cell_map
        except TypeError:
            not_in_cell_map = True

        if not_in_cell_map:
            if (not isinstance(address, (str, AddressRange, AddressCell))
                    and isinstance(address, collections.Iterable)):

                if not isinstance(address, (tuple, list)):
                    address = tuple(address)

                # process a tuple or list of addresses
                return type(address)(self.evaluate(c) for c in address)

            address = AddressRange.create(address).address
            if address not in self.cell_map:
                self._gen_graph(address)

        return self._evaluate(address)
Exemple #7
0
    def set_value(self, address, value):
        """ Set the value of one or more cells or ranges

        :param address: `str`, `AddressRange`, `AddressCell` or a tuple, list
            or an iterable of these three
        :param value: value to set.  This can be a value or a tuple/list
            which matches the shapes needed for the given address/addresses
        """

        if (not isinstance(address, (AddressRange, AddressCell))
                and isinstance(address, (tuple, list))):
            assert isinstance(value, (tuple, list))
            assert len(address) == len(value)
            for addr, val in zip(address, value):
                self.set_value(addr, val)
            return

        elif address not in self.cell_map:
            address = AddressRange.create(address).address
            assert address in self.cell_map

        cell_or_range = self.cell_map[address]

        if cell_or_range.value != value:  # pragma: no branch
            # need to be able to 'set' an empty cell
            if cell_or_range.value is None:
                cell_or_range.value = value

            # reset the node + its dependencies
            self._reset(cell_or_range)

            # set the value
            cell_or_range.value = value
Exemple #8
0
def offset(reference, row_inc, col_inc, height=None, width=None):
    # Excel reference: https://support.microsoft.com/en-us/office/
    #   offset-function-c8de19ae-dd79-4b9b-a14e-b4d906d11b66
    """
    Returns a reference to a range that is a specified number of rows and
    columns from a cell or range of cells.
    """
    base_addr = AddressRange.create(reference)

    if height is None:
        height = base_addr.size.height
    if width is None:
        width = base_addr.size.width

    new_row = base_addr.row + row_inc
    end_row = new_row + height - 1
    new_col = base_addr.col_idx + col_inc
    end_col = new_col + width - 1

    if new_row <= 0 or end_row > MAX_ROW or new_col <= 0 or end_col > MAX_COL:
        return REF_ERROR

    top_left = AddressCell((new_col, new_row, new_col, new_row),
                           sheet=base_addr.sheet)
    if height == width == 1:
        return top_left
    else:
        bottom_right = AddressCell((end_col, end_row, end_col, end_row),
                                   sheet=base_addr.sheet)

        return AddressRange(f'{top_left.coordinate}:{bottom_right.coordinate}',
                            sheet=top_left.sheet)
Exemple #9
0
    def evaluate(self, address):
        """ evaluate a cell or cells in the spreadsheet

        :param address: str, AddressRange, AddressCell or a tuple or list
            or iterable of these three
        :return: evaluated value/values
        """

        if str(address) not in self.cell_map:
            if list_like(address):
                if not isinstance(address, (tuple, list)):
                    address = tuple(address)

                # process a tuple or list of addresses
                return type(address)(self.evaluate(c) for c in address)

            address = AddressRange.create(address)

            # get the sheet if not specified
            if not address.has_sheet:
                address = AddressRange(
                    address, sheet=self.excel.get_active_sheet_name())

            if address.address not in self.cell_map:
                self._gen_graph(address.address)

        return self._evaluate(str(address))
Exemple #10
0
def test_address_range():
    a = AddressRange('a1:b2')
    b = AddressRange('A1:B2')
    c = AddressRange(a)

    assert a == b
    assert b == c

    assert b == AddressRange(b)
    assert b == AddressRange.create(b)

    assert AddressRange('sh!a1:b2') == AddressRange(a, sheet='sh')
    assert AddressCell('C13') == AddressCell('R13C3')

    with pytest.raises(ValueError):
        AddressRange(AddressRange('sh!a1:b2'), sheet='sheet')

    a = AddressRange('A:A')
    assert 'A' == a.start.column
    assert 'A' == a.end.column
    assert 0 == a.start.row
    assert 0 == a.end.row

    b = AddressRange('1:1')
    assert '' == b.start.column
    assert '' == b.end.column
    assert 1 == b.start.row
    assert 1 == b.end.row
Exemple #11
0
    def evaluate(self, address):
        """ evaluate a cell or cells in the spreadsheet

        :param address: str, AddressRange, AddressCell or a tuple or list
            or iterable of these three
        :return: evaluated value/values
        """

        if str(address) not in self.cell_map:
            if list_like(address):
                if not isinstance(address, (tuple, list)):
                    address = tuple(address)

                # process a tuple or list of addresses
                return type(address)(self.evaluate(c) for c in address)

            address = AddressRange.create(address)

            # get the sheet if not specified
            if not address.has_sheet:
                address = AddressRange(
                    address, sheet=self.excel.get_active_sheet_name())

            if address.address not in self.cell_map:
                self._gen_graph(address.address)

        result = self._evaluate(str(address))
        if isinstance(result, tuple):
            # trim excess dimensions
            if len(result[0]) == 1:
                result = tuple(row[0] for row in result)
            if len(result) == 1:
                result = result[0]
        return result
Exemple #12
0
def test_offset(crwh, refer, rows, cols, height, width):
    expected = crwh
    if isinstance(crwh, tuple):
        start = AddressCell((crwh[0], crwh[1], crwh[0], crwh[1]))
        end = AddressCell((crwh[0] + crwh[2] - 1, crwh[1] + crwh[3] - 1,
                           crwh[0] + crwh[2] - 1, crwh[1] + crwh[3] - 1))

        expected = AddressRange.create(f'{start.coordinate}:{end.coordinate}')

    result = offset(refer, rows, cols, height, width)
    assert result == expected

    refer_addr = AddressRange.create(refer)
    if height == refer_addr.size.height:
        height = None
    if width == refer_addr.size.width:
        width = None
    assert offset(refer_addr, rows, cols, height, width) == expected
Exemple #13
0
 def emit(self):
     # resolve the range into cells
     sheet = self.cell and self.cell.sheet or ''
     if '!' in self.value:
         sheet = ''
     address = AddressRange.create(
         self.value.replace('$', ''), sheet=sheet, cell=self.cell)
     template = '_R_("{}")' if address.is_range else '_C_("{}")'
     return template.format(address)
Exemple #14
0
def indirect(ref_text, a1=True):
    # Excel reference: https://support.office.com/en-us/article/
    #   indirect-function-474b3a3a-8a26-4f44-b491-92b6306fa261
    try:
        address = AddressRange.create(ref_text)
    except ValueError:
        return REF_ERROR
    if address.row > MAX_ROW or address.col_idx > MAX_COL:
        return REF_ERROR
    return address
Exemple #15
0
def test_row(address, expected):
    try:
        address = AddressRange.create(address)
    except ValueError:
        pass

    result = row(address)
    if expected is None:
        assert 1 == next(iter(result))
    else:
        assert expected == result
Exemple #16
0
def test_row(address, expected):
    try:
        address = AddressRange.create(address)
    except ValueError:
        pass

    result = row(address)
    if expected is None:
        assert 1 == next(iter(result))
    else:
        assert expected == result
Exemple #17
0
    def eval_conditional_formats(self, address):
        """Evaluate the conditional format (formulas) for a cell or cells

        returns the conditional format id which is the key for the dict:
          ExcelCompiler.conditional_formats

        NOTE: conditional_formats are not saved in the persistent formats.
              If needed they can be hand serialized into "extra_data"

        :param address: str, AddressRange, AddressCell or a tuple or list
            or iterable of these three
        :return: evaluated objects ids
        """
        if list_like(address):
            if not isinstance(address, (tuple, list)):
                address = tuple(address)

            # process a tuple or list of addresses
            return type(address)(self.eval_conditional_formats(c)
                                 for c in address)

        address = AddressRange.create(address)

        # get the sheet if not specified
        if not address.has_sheet:
            address = AddressRange(address,
                                   sheet=self.excel.get_active_sheet_name())

        if address.is_range:
            return tuple(
                tuple(self.eval_conditional_formats(addr) for addr in row)
                for row in address.rows)

        cf_addr = str(address).replace('!', '.cf!')

        if cf_addr not in self.cell_map:
            phony_cell = _Cell(address)
            formats = self.excel.conditional_format(address)
            format_strs = []
            for f in formats:
                excel_formula = ExcelFormula(f.formula, cell=phony_cell)
                python_code = excel_formula.python_code
                format_strs.append(
                    f'({python_code}, {f.dxf_id}, {int(bool(f.stop_if_true))})'
                )
                self.conditional_formats[f.dxf_id] = f.dxf

            python_code = f"=conditional_format_ids({', '.join(format_strs)})"
            a_cell = _Cell(address, formula=python_code)
            self.cell_map[cf_addr] = a_cell
            self._gen_graph(a_cell.formula.needed_addresses)

        return self.eval(self.cell_map[cf_addr])
Exemple #18
0
def test_multi_area_ranges(excel, ATestCell):
    cell = ATestCell('A', 1, excel=excel)
    from unittest import mock
    with mock.patch.object(excel, '_defined_names',
                           {'dname': (('$A$1', 's1'), ('$A$3:$A$4', 's2'))}):

        multi_area_range = AddressMultiAreaRange(
            tuple(AddressRange(addr, sheet=sh))
            for addr, sh in excel._defined_names['dname'])

        assert (multi_area_range, None) == range_boundaries('dname', cell)
        assert multi_area_range == AddressRange.create('dname', cell=cell)
Exemple #19
0
    def set_value(self, address, value, set_as_range=False):
        """ Set the value of one or more cells or ranges

        :param address: `str`, `AddressRange`, `AddressCell` or a tuple, list
            or an iterable of these three
        :param value: value to set.  This can be a value or a tuple/list
            which matches the shapes needed for the given address/addresses
        :param set_as_range: With a single range address and a list like value,
            set to true to set the entire rnage to the inserted list.
        """

        if list_like(value) and not set_as_range:
            value = tuple(flatten(value))
            if list_like(address):
                address = (AddressCell(addr) for addr in flatten(address))
            else:
                address = flatten(AddressRange(address).resolve_range)
            address = tuple(address)
            assert len(address) == len(value)
            for addr, val in zip(address, value):
                self.set_value(addr, val)
            return

        elif address not in self.cell_map:
            address = AddressRange.create(address).address
            assert address in self.cell_map, (
                f'Address "{address}" not found in the cell map. Evaluate the '
                'address, or an address that references it, to place it in the cell map.'
            )

        if set_as_range and list_like(value) and not (value
                                                      and list_like(value[0])):
            value = (value, )

        cell_or_range = self.cell_map[address]

        if cell_or_range.value != value:  # pragma: no branch
            # need to be able to 'set' an empty cell, set to not None
            cell_or_range.value = value

            # reset the node + its dependencies
            if not self.cycles:
                self._reset(cell_or_range)

            # set the value
            cell_or_range.value = value
Exemple #20
0
    def set_value(self, address, value, set_as_range=False):
        """ Set the value of one or more cells or ranges

        :param address: `str`, `AddressRange`, `AddressCell` or a tuple, list
            or an iterable of these three
        :param value: value to set.  This can be a value or a tuple/list
            which matches the shapes needed for the given address/addresses
        :param set_as_range: With a single range address and a list like value,
            set to true to set the entire rnage to the inserted list.
        """

        if list_like(value) and not set_as_range:
            value = tuple(flatten(value))
            if list_like(address):
                address = (AddressCell(addr) for addr in flatten(address))
            else:
                address = flatten(AddressRange(address).resolve_range)
            address = tuple(address)
            assert len(address) == len(value)
            for addr, val in zip(address, value):
                self.set_value(addr, val)
            return

        elif address not in self.cell_map:
            address = AddressRange.create(address).address
            assert address in self.cell_map

        if set_as_range and list_like(value) and not (
                value and list_like(value[0])):
            value = (value, )

        cell_or_range = self.cell_map[address]

        if cell_or_range.value != value:  # pragma: no branch
            # need to be able to 'set' an empty cell
            if cell_or_range.value is None:
                cell_or_range.value = value

            # reset the node + its dependencies
            self._reset(cell_or_range)

            # set the value
            cell_or_range.value = value
Exemple #21
0
    def evaluate(self, address, _recursed=False):
        """ evaluate a cell or cells in the spreadsheet

        :param address: str, AddressRange, AddressCell or a tuple or list
            or iterable of these three
        :return: evaluated value/values
        """

        if str(address) not in self.cell_map:
            if list_like(address):
                if not isinstance(address, (tuple, list)):
                    address = tuple(address)

                # process a tuple or list of addresses
                return type(address)(self.evaluate(c, True) for c in address)

            address = AddressRange.create(address)

            # get the sheet if not specified
            if not address.has_sheet:
                address = AddressRange(
                    address, sheet=self.excel.get_active_sheet_name())

            if address.address not in self.cell_map:
                self._gen_graph(address.address)

        if not _recursed:
            for cell in self.cell_map.values():
                if isinstance(cell, _CellRange) or cell.formula:
                    cell.iterations = 0

        result = self._evaluate(str(address))
        if isinstance(result, tuple):
            # trim excess dimensions
            if len(result[0]) == 1:
                result = tuple(row[0] for row in result)
            if len(result) == 1:
                result = result[0]
        return result
Exemple #22
0
def test_column(address, result):
    try:
        address = AddressRange.create(address)
    except ValueError:
        pass
    assert result == column(address)
Exemple #23
0
 def get_formula_from_range(self, address):
     addr = AddressRange.create(address)
     found = addr.column in self.columns and str(addr.row) in self.rows
     return '=linest()' if found else ''
Exemple #24
0
 def get_formula_from_range(self, address):
     addr = AddressRange.create(address)
     found = addr.column in self.columns and str(addr.row) in self.rows
     return '=linest()' if found else ''
Exemple #25
0
def test_address_absolute(address, expected):
    assert AddressRange.create(address).abs_address == expected