about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/openpyxl/worksheet/datavalidation.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/openpyxl/worksheet/datavalidation.py')
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/worksheet/datavalidation.py202
1 files changed, 202 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/worksheet/datavalidation.py b/.venv/lib/python3.12/site-packages/openpyxl/worksheet/datavalidation.py
new file mode 100644
index 00000000..f5077d97
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/worksheet/datavalidation.py
@@ -0,0 +1,202 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from collections import defaultdict
+from itertools import chain
+from operator import itemgetter
+
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors import (
+    Bool,
+    NoneSet,
+    String,
+    Sequence,
+    Alias,
+    Integer,
+    Convertible,
+)
+from openpyxl.descriptors.nested import NestedText
+
+from openpyxl.utils import (
+    rows_from_range,
+    coordinate_to_tuple,
+    get_column_letter,
+)
+
+
+def collapse_cell_addresses(cells, input_ranges=()):
+    """ Collapse a collection of cell co-ordinates down into an optimal
+        range or collection of ranges.
+
+        E.g. Cells A1, A2, A3, B1, B2 and B3 should have the data-validation
+        object applied, attempt to collapse down to a single range, A1:B3.
+
+        Currently only collapsing contiguous vertical ranges (i.e. above
+        example results in A1:A3 B1:B3).
+    """
+
+    ranges = list(input_ranges)
+
+    # convert cell into row, col tuple
+    raw_coords = (coordinate_to_tuple(cell) for cell in cells)
+
+    # group by column in order
+    grouped_coords = defaultdict(list)
+    for row, col in sorted(raw_coords, key=itemgetter(1)):
+        grouped_coords[col].append(row)
+
+    # create range string from first and last row in column
+    for col, cells in grouped_coords.items():
+        col = get_column_letter(col)
+        fmt = "{0}{1}:{2}{3}"
+        if len(cells) == 1:
+            fmt = "{0}{1}"
+        r = fmt.format(col, min(cells), col, max(cells))
+        ranges.append(r)
+
+    return " ".join(ranges)
+
+
+def expand_cell_ranges(range_string):
+    """
+    Expand cell ranges to a sequence of addresses.
+    Reverse of collapse_cell_addresses
+    Eg. converts "A1:A2 B1:B2" to (A1, A2, B1, B2)
+    """
+    # expand ranges to rows and then flatten
+    rows = (rows_from_range(rs) for rs in range_string.split()) # list of rows
+    cells = (chain(*row) for row in rows) # flatten rows
+    return set(chain(*cells))
+
+
+from .cell_range import MultiCellRange
+
+
+class DataValidation(Serialisable):
+
+    tagname = "dataValidation"
+
+    sqref = Convertible(expected_type=MultiCellRange)
+    cells = Alias("sqref")
+    ranges = Alias("sqref")
+
+    showDropDown = Bool(allow_none=True)
+    hide_drop_down = Alias('showDropDown')
+    showInputMessage = Bool(allow_none=True)
+    showErrorMessage = Bool(allow_none=True)
+    allowBlank = Bool(allow_none=True)
+    allow_blank = Alias('allowBlank')
+
+    errorTitle = String(allow_none = True)
+    error = String(allow_none = True)
+    promptTitle = String(allow_none = True)
+    prompt = String(allow_none = True)
+    formula1 = NestedText(allow_none=True, expected_type=str)
+    formula2 = NestedText(allow_none=True, expected_type=str)
+
+    type = NoneSet(values=("whole", "decimal", "list", "date", "time",
+                           "textLength", "custom"))
+    errorStyle = NoneSet(values=("stop", "warning", "information"))
+    imeMode = NoneSet(values=("noControl", "off", "on", "disabled",
+                              "hiragana", "fullKatakana", "halfKatakana", "fullAlpha","halfAlpha",
+                              "fullHangul", "halfHangul"))
+    operator = NoneSet(values=("between", "notBetween", "equal", "notEqual",
+                               "lessThan", "lessThanOrEqual", "greaterThan", "greaterThanOrEqual"))
+    validation_type = Alias('type')
+
+    def __init__(self,
+                 type=None,
+                 formula1=None,
+                 formula2=None,
+                 showErrorMessage=False,
+                 showInputMessage=False,
+                 showDropDown=False,
+                 allowBlank=False,
+                 sqref=(),
+                 promptTitle=None,
+                 errorStyle=None,
+                 error=None,
+                 prompt=None,
+                 errorTitle=None,
+                 imeMode=None,
+                 operator=None,
+                 allow_blank=None,
+                 ):
+        self.sqref = sqref
+        self.showDropDown = showDropDown
+        self.imeMode = imeMode
+        self.operator = operator
+        self.formula1 = formula1
+        self.formula2 = formula2
+        if allow_blank is not None:
+            allowBlank = allow_blank
+        self.allowBlank = allowBlank
+        self.showErrorMessage = showErrorMessage
+        self.showInputMessage = showInputMessage
+        self.type = type
+        self.promptTitle = promptTitle
+        self.errorStyle = errorStyle
+        self.error = error
+        self.prompt = prompt
+        self.errorTitle = errorTitle
+
+
+    def add(self, cell):
+        """Adds a cell or cell coordinate to this validator"""
+        if hasattr(cell, "coordinate"):
+            cell = cell.coordinate
+        self.sqref += cell
+
+
+    def __contains__(self, cell):
+        if hasattr(cell, "coordinate"):
+            cell = cell.coordinate
+        return cell in self.sqref
+
+
+class DataValidationList(Serialisable):
+
+    tagname = "dataValidations"
+
+    disablePrompts = Bool(allow_none=True)
+    xWindow = Integer(allow_none=True)
+    yWindow = Integer(allow_none=True)
+    dataValidation = Sequence(expected_type=DataValidation)
+
+    __elements__ = ('dataValidation',)
+    __attrs__ = ('disablePrompts', 'xWindow', 'yWindow', 'count')
+
+    def __init__(self,
+                 disablePrompts=None,
+                 xWindow=None,
+                 yWindow=None,
+                 count=None,
+                 dataValidation=(),
+                ):
+        self.disablePrompts = disablePrompts
+        self.xWindow = xWindow
+        self.yWindow = yWindow
+        self.dataValidation = dataValidation
+
+
+    @property
+    def count(self):
+        return len(self)
+
+
+    def __len__(self):
+        return len(self.dataValidation)
+
+
+    def append(self, dv):
+        self.dataValidation.append(dv)
+
+
+    def to_tree(self, tagname=None):
+        """
+        Need to skip validations that have no cell ranges
+        """
+        ranges = self.dataValidation # copy
+        self.dataValidation = [r for r in self.dataValidation if bool(r.sqref)]
+        xml = super().to_tree(tagname)
+        self.dataValidation = ranges
+        return xml