Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/openpyxl/worksheet/datavalidation.py: 43%
101 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1# Copyright (c) 2010-2022 openpyxl
3from collections import defaultdict
4from itertools import chain
5from operator import itemgetter
7from openpyxl.descriptors.serialisable import Serialisable
8from openpyxl.descriptors import (
9 Bool,
10 NoneSet,
11 String,
12 Sequence,
13 Alias,
14 Integer,
15 Convertible,
16)
17from openpyxl.descriptors.nested import NestedText
19from openpyxl.utils import (
20 rows_from_range,
21 coordinate_to_tuple,
22 get_column_letter,
23)
26def collapse_cell_addresses(cells, input_ranges=()):
27 """ Collapse a collection of cell co-ordinates down into an optimal
28 range or collection of ranges.
30 E.g. Cells A1, A2, A3, B1, B2 and B3 should have the data-validation
31 object applied, attempt to collapse down to a single range, A1:B3.
33 Currently only collapsing contiguous vertical ranges (i.e. above
34 example results in A1:A3 B1:B3).
35 """
37 ranges = list(input_ranges)
39 # convert cell into row, col tuple
40 raw_coords = (coordinate_to_tuple(cell) for cell in cells)
42 # group by column in order
43 grouped_coords = defaultdict(list)
44 for row, col in sorted(raw_coords, key=itemgetter(1)):
45 grouped_coords[col].append(row)
47 # create range string from first and last row in column
48 for col, cells in grouped_coords.items():
49 col = get_column_letter(col)
50 fmt = "{0}{1}:{2}{3}"
51 if len(cells) == 1:
52 fmt = "{0}{1}"
53 r = fmt.format(col, min(cells), col, max(cells))
54 ranges.append(r)
56 return " ".join(ranges)
59def expand_cell_ranges(range_string):
60 """
61 Expand cell ranges to a sequence of addresses.
62 Reverse of collapse_cell_addresses
63 Eg. converts "A1:A2 B1:B2" to (A1, A2, B1, B2)
64 """
65 # expand ranges to rows and then flatten
66 rows = (rows_from_range(rs) for rs in range_string.split()) # list of rows
67 cells = (chain(*row) for row in rows) # flatten rows
68 return set(chain(*cells))
71from .cell_range import MultiCellRange
74class DataValidation(Serialisable):
76 tagname = "dataValidation"
78 sqref = Convertible(expected_type=MultiCellRange)
79 cells = Alias("sqref")
80 ranges = Alias("sqref")
82 showErrorMessage = Bool()
83 showDropDown = Bool(allow_none=True)
84 hide_drop_down = Alias('showDropDown')
85 showInputMessage = Bool()
86 showErrorMessage = Bool()
87 allowBlank = Bool()
88 allow_blank = Alias('allowBlank')
90 errorTitle = String(allow_none = True)
91 error = String(allow_none = True)
92 promptTitle = String(allow_none = True)
93 prompt = String(allow_none = True)
94 formula1 = NestedText(allow_none=True, expected_type=str)
95 formula2 = NestedText(allow_none=True, expected_type=str)
97 type = NoneSet(values=("whole", "decimal", "list", "date", "time",
98 "textLength", "custom"))
99 errorStyle = NoneSet(values=("stop", "warning", "information"))
100 imeMode = NoneSet(values=("noControl", "off", "on", "disabled",
101 "hiragana", "fullKatakana", "halfKatakana", "fullAlpha","halfAlpha",
102 "fullHangul", "halfHangul"))
103 operator = NoneSet(values=("between", "notBetween", "equal", "notEqual",
104 "lessThan", "lessThanOrEqual", "greaterThan", "greaterThanOrEqual"))
105 validation_type = Alias('type')
107 def __init__(self,
108 type=None,
109 formula1=None,
110 formula2=None,
111 showErrorMessage=True,
112 showInputMessage=True,
113 showDropDown=None,
114 allowBlank=None,
115 sqref=(),
116 promptTitle=None,
117 errorStyle=None,
118 error=None,
119 prompt=None,
120 errorTitle=None,
121 imeMode=None,
122 operator=None,
123 allow_blank=None,
124 ):
125 self.sqref = sqref
126 self.showDropDown = showDropDown
127 self.imeMode = imeMode
128 self.operator = operator
129 self.formula1 = formula1
130 self.formula2 = formula2
131 if allow_blank is not None:
132 allowBlank = allow_blank
133 self.allowBlank = allowBlank
134 self.showErrorMessage = showErrorMessage
135 self.showInputMessage = showInputMessage
136 self.type = type
137 self.promptTitle = promptTitle
138 self.errorStyle = errorStyle
139 self.error = error
140 self.prompt = prompt
141 self.errorTitle = errorTitle
144 def add(self, cell):
145 """Adds a cell or cell coordinate to this validator"""
146 if hasattr(cell, "coordinate"):
147 cell = cell.coordinate
148 self.sqref += cell
151 def __contains__(self, cell):
152 if hasattr(cell, "coordinate"):
153 cell = cell.coordinate
154 return cell in self.sqref
157class DataValidationList(Serialisable):
159 tagname = "dataValidations"
161 disablePrompts = Bool(allow_none=True)
162 xWindow = Integer(allow_none=True)
163 yWindow = Integer(allow_none=True)
164 dataValidation = Sequence(expected_type=DataValidation)
166 __elements__ = ('dataValidation',)
167 __attrs__ = ('disablePrompts', 'xWindow', 'yWindow', 'count')
169 def __init__(self,
170 disablePrompts=None,
171 xWindow=None,
172 yWindow=None,
173 count=None,
174 dataValidation=(),
175 ):
176 self.disablePrompts = disablePrompts
177 self.xWindow = xWindow
178 self.yWindow = yWindow
179 self.dataValidation = dataValidation
182 @property
183 def count(self):
184 return len(self)
187 def __len__(self):
188 return len(self.dataValidation)
191 def append(self, dv):
192 self.dataValidation.append(dv)
195 def to_tree(self, tagname=None):
196 """
197 Need to skip validations that have no cell ranges
198 """
199 ranges = self.dataValidation # copy
200 self.dataValidation = [r for r in self.dataValidation if bool(r.sqref)]
201 xml = super(DataValidationList, self).to_tree(tagname)
202 self.dataValidation = ranges
203 return xml