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

1# Copyright (c) 2010-2022 openpyxl 

2 

3from collections import defaultdict 

4from itertools import chain 

5from operator import itemgetter 

6 

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 

18 

19from openpyxl.utils import ( 

20 rows_from_range, 

21 coordinate_to_tuple, 

22 get_column_letter, 

23) 

24 

25 

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. 

29 

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. 

32 

33 Currently only collapsing contiguous vertical ranges (i.e. above 

34 example results in A1:A3 B1:B3). 

35 """ 

36 

37 ranges = list(input_ranges) 

38 

39 # convert cell into row, col tuple 

40 raw_coords = (coordinate_to_tuple(cell) for cell in cells) 

41 

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) 

46 

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) 

55 

56 return " ".join(ranges) 

57 

58 

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)) 

69 

70 

71from .cell_range import MultiCellRange 

72 

73 

74class DataValidation(Serialisable): 

75 

76 tagname = "dataValidation" 

77 

78 sqref = Convertible(expected_type=MultiCellRange) 

79 cells = Alias("sqref") 

80 ranges = Alias("sqref") 

81 

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') 

89 

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) 

96 

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') 

106 

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 

142 

143 

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 

149 

150 

151 def __contains__(self, cell): 

152 if hasattr(cell, "coordinate"): 

153 cell = cell.coordinate 

154 return cell in self.sqref 

155 

156 

157class DataValidationList(Serialisable): 

158 

159 tagname = "dataValidations" 

160 

161 disablePrompts = Bool(allow_none=True) 

162 xWindow = Integer(allow_none=True) 

163 yWindow = Integer(allow_none=True) 

164 dataValidation = Sequence(expected_type=DataValidation) 

165 

166 __elements__ = ('dataValidation',) 

167 __attrs__ = ('disablePrompts', 'xWindow', 'yWindow', 'count') 

168 

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 

180 

181 

182 @property 

183 def count(self): 

184 return len(self) 

185 

186 

187 def __len__(self): 

188 return len(self.dataValidation) 

189 

190 

191 def append(self, dv): 

192 self.dataValidation.append(dv) 

193 

194 

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