layrz-sdk 3.1.11__py3-none-any.whl → 3.1.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of layrz-sdk might be problematic. Click here for more details.

Files changed (75) hide show
  1. layrz_sdk/__init__.py +1 -1
  2. layrz_sdk/constants.py +5 -5
  3. layrz_sdk/entities/__init__.py +129 -129
  4. layrz_sdk/entities/asset.py +60 -60
  5. layrz_sdk/entities/asset_operation_mode.py +31 -31
  6. layrz_sdk/entities/broadcast_request.py +12 -12
  7. layrz_sdk/entities/broadcast_response.py +12 -12
  8. layrz_sdk/entities/broadcast_result.py +20 -20
  9. layrz_sdk/entities/broadcast_status.py +28 -28
  10. layrz_sdk/entities/case.py +48 -47
  11. layrz_sdk/entities/case_ignored_status.py +26 -26
  12. layrz_sdk/entities/case_status.py +23 -23
  13. layrz_sdk/entities/charts/axis_config.py +15 -15
  14. layrz_sdk/entities/charts/bar_chart.py +175 -175
  15. layrz_sdk/entities/charts/chart_alignment.py +27 -27
  16. layrz_sdk/entities/charts/chart_color.py +44 -44
  17. layrz_sdk/entities/charts/chart_configuration.py +10 -10
  18. layrz_sdk/entities/charts/chart_data_serie.py +19 -19
  19. layrz_sdk/entities/charts/chart_data_serie_type.py +28 -28
  20. layrz_sdk/entities/charts/chart_data_type.py +27 -27
  21. layrz_sdk/entities/charts/chart_render_technology.py +30 -30
  22. layrz_sdk/entities/charts/column_chart.py +201 -201
  23. layrz_sdk/entities/charts/html_chart.py +38 -38
  24. layrz_sdk/entities/charts/line_chart.py +248 -248
  25. layrz_sdk/entities/charts/map_center_type.py +22 -22
  26. layrz_sdk/entities/charts/map_chart.py +108 -108
  27. layrz_sdk/entities/charts/map_point.py +22 -22
  28. layrz_sdk/entities/charts/number_chart.py +54 -54
  29. layrz_sdk/entities/charts/pie_chart.py +131 -131
  30. layrz_sdk/entities/charts/radar_chart.py +81 -81
  31. layrz_sdk/entities/charts/radial_bar_chart.py +131 -131
  32. layrz_sdk/entities/charts/scatter_chart.py +210 -210
  33. layrz_sdk/entities/charts/scatter_serie.py +13 -15
  34. layrz_sdk/entities/charts/scatter_serie_item.py +8 -8
  35. layrz_sdk/entities/charts/table_chart.py +54 -54
  36. layrz_sdk/entities/charts/table_header.py +8 -8
  37. layrz_sdk/entities/charts/table_row.py +9 -9
  38. layrz_sdk/entities/charts/timeline_chart.py +79 -79
  39. layrz_sdk/entities/charts/timeline_serie.py +10 -12
  40. layrz_sdk/entities/charts/timeline_serie_item.py +12 -12
  41. layrz_sdk/entities/checkpoint.py +17 -18
  42. layrz_sdk/entities/comment.py +16 -16
  43. layrz_sdk/entities/custom_field.py +10 -10
  44. layrz_sdk/entities/custom_report_page.py +40 -16
  45. layrz_sdk/entities/device.py +13 -13
  46. layrz_sdk/entities/event.py +23 -23
  47. layrz_sdk/entities/geofence.py +11 -11
  48. layrz_sdk/entities/last_message.py +12 -12
  49. layrz_sdk/entities/message.py +23 -23
  50. layrz_sdk/entities/outbound_service.py +10 -10
  51. layrz_sdk/entities/position.py +116 -116
  52. layrz_sdk/entities/presence_type.py +16 -16
  53. layrz_sdk/entities/report.py +289 -283
  54. layrz_sdk/entities/report_col.py +40 -40
  55. layrz_sdk/entities/report_configuration.py +8 -8
  56. layrz_sdk/entities/report_data_type.py +28 -28
  57. layrz_sdk/entities/report_format.py +27 -27
  58. layrz_sdk/entities/report_header.py +43 -43
  59. layrz_sdk/entities/report_page.py +15 -17
  60. layrz_sdk/entities/report_row.py +28 -28
  61. layrz_sdk/entities/sensor.py +11 -11
  62. layrz_sdk/entities/text_alignment.py +26 -26
  63. layrz_sdk/entities/trigger.py +11 -11
  64. layrz_sdk/entities/user.py +10 -10
  65. layrz_sdk/entities/waypoint.py +18 -18
  66. layrz_sdk/helpers/__init__.py +5 -8
  67. layrz_sdk/helpers/color.py +44 -44
  68. layrz_sdk/lcl/__init__.py +5 -7
  69. layrz_sdk/lcl/core.py +848 -848
  70. {layrz_sdk-3.1.11.dist-info → layrz_sdk-3.1.13.dist-info}/METADATA +49 -49
  71. layrz_sdk-3.1.13.dist-info/RECORD +75 -0
  72. {layrz_sdk-3.1.11.dist-info → layrz_sdk-3.1.13.dist-info}/WHEEL +1 -1
  73. {layrz_sdk-3.1.11.dist-info → layrz_sdk-3.1.13.dist-info}/licenses/LICENSE +6 -6
  74. layrz_sdk-3.1.11.dist-info/RECORD +0 -75
  75. {layrz_sdk-3.1.11.dist-info → layrz_sdk-3.1.13.dist-info}/top_level.txt +0 -0
@@ -1,283 +1,289 @@
1
- """Report class"""
2
-
3
- import logging
4
- import os
5
- import sys
6
- import time
7
- import warnings
8
- from pathlib import Path
9
- from typing import Any, Dict, List, Optional
10
-
11
- import xlsxwriter
12
- from pydantic import BaseModel, Field, field_validator
13
-
14
- from layrz_sdk.helpers.color import use_black
15
-
16
- from .custom_report_page import CustomReportPage
17
- from .report_data_type import ReportDataType
18
- from .report_format import ReportFormat
19
- from .report_page import ReportPage
20
-
21
- if sys.version_info >= (3, 11):
22
- from typing import Self
23
- else:
24
- from typing_extensions import Self
25
-
26
- log = logging.getLogger(__name__)
27
-
28
-
29
- class Report(BaseModel):
30
- """Report definition"""
31
-
32
- name: str = Field(description='Name of the report. Length should be less than 60 characters')
33
- pages: List[ReportPage | CustomReportPage] = Field(
34
- description='List of report pages',
35
- default_factory=list,
36
- )
37
- export_format: Optional[ReportFormat] = Field(description='Export format of the report', default=None)
38
-
39
- @field_validator('export_format', mode='before')
40
- def _validate_export_format(cls: 'Report', value: Any) -> Any:
41
- if value is not None:
42
- warnings.warn(
43
- 'export_format is deprecated, use the export method instead',
44
- DeprecationWarning,
45
- stacklevel=2,
46
- )
47
-
48
- return value
49
-
50
- @property
51
- def filename(self: Self) -> str:
52
- """Report filename"""
53
- return f'{self.name}_{int(time.time() * 1000)}.xlsx'
54
-
55
- def export(
56
- self: Self,
57
- path: str | Path,
58
- export_format: Optional[ReportFormat] = None,
59
- password: Optional[str] = None,
60
- msoffice_crypt_path: str = '/opt/msoffice/bin/msoffice-crypt.exe',
61
- ) -> Path | Dict[str, Any]:
62
- """
63
- Export report to file
64
-
65
- :param path: Path to save the report
66
- :type path: str | Path
67
- :param export_format: Format to export the report
68
- :type export_format: ReportFormat
69
- :param password: Password to protect the file (Only works with Microsoft Excel format)
70
- :type password: str
71
- :param msoffice_crypt_path: Path to the msoffice-crypt.exe executable, used to encrypt the file
72
- :type msoffice_crypt_path: str
73
- :return: Full path of the exported file or JSON representation of the report
74
- :rtype: Path | dict
75
- :raises AttributeError: If the export format is not supported
76
- """
77
- if export_format:
78
- if export_format == ReportFormat.MICROSOFT_EXCEL:
79
- return self._export_xlsx(path=path, password=password, msoffice_crypt_path=msoffice_crypt_path)
80
- elif export_format == ReportFormat.JSON:
81
- if password:
82
- return {'name': self.name, 'is_protected': True, 'pages': []}
83
- return self._export_json()
84
- else:
85
- raise AttributeError(f'Unsupported export format: {export_format}')
86
-
87
- if self.export_format == ReportFormat.MICROSOFT_EXCEL:
88
- return self._export_xlsx(path=path, password=password, msoffice_crypt_path=msoffice_crypt_path)
89
- elif self.export_format == ReportFormat.JSON:
90
- if password:
91
- return {'name': self.name, 'is_protected': True, 'pages': []}
92
- return self._export_json()
93
- else:
94
- raise AttributeError(f'Unsupported export format: {self.export_format}')
95
-
96
- def export_as_json(self: Self) -> Dict[str, Any]:
97
- """Returns the report as a JSON dict"""
98
- return self._export_json()
99
-
100
- def _export_json(self: Self) -> Dict[str, Any]:
101
- """Returns a JSON dict of the report"""
102
- json_pages = []
103
- for page in self.pages:
104
- if isinstance(page, CustomReportPage):
105
- continue
106
-
107
- headers = []
108
- for header in page.headers:
109
- headers.append(
110
- {
111
- 'content': header.content,
112
- 'text_color': '#000000' if use_black(header.color) else '#ffffff',
113
- 'color': header.color,
114
- }
115
- )
116
- rows = []
117
- for row in page.rows:
118
- cells = []
119
- for cell in row.content:
120
- cells.append(
121
- {
122
- 'content': cell.content,
123
- 'text_color': '#000000' if use_black(cell.color) else '#ffffff',
124
- 'color': cell.color,
125
- 'data_type': cell.data_type.value,
126
- }
127
- )
128
- rows.append(
129
- {
130
- 'content': cells,
131
- 'compact': row.compact,
132
- }
133
- )
134
- json_pages.append(
135
- {
136
- 'name': page.name,
137
- 'headers': headers,
138
- 'rows': rows,
139
- }
140
- )
141
-
142
- return {
143
- 'name': self.name,
144
- 'pages': json_pages,
145
- }
146
-
147
- def _export_xlsx(
148
- self: Self,
149
- path: str | Path,
150
- password: Optional[str] = None,
151
- msoffice_crypt_path: Optional[str] = None,
152
- ) -> Path:
153
- """
154
- Export to Microsoft Excel (.xslx)
155
-
156
- :param path: Path to save the report
157
- :type path: str | Path
158
- :param password: Password to protect the file
159
- :type password: str
160
- :param msoffice_crypt_path: Path to the msoffice-crypt.exe executable, used to encrypt the file
161
- :type msoffice_crypt_path: str
162
-
163
- :return: Full path of the exported file
164
- :rtype: Path
165
-
166
- :raises AttributeError: If the export format is not supported
167
- """
168
-
169
- if isinstance(path, str):
170
- path = Path(path).resolve()
171
-
172
- full_path = path / self.filename
173
- if full_path.exists():
174
- log.warning(f'File {full_path} already exists, overwriting it')
175
- os.remove(full_path)
176
-
177
- book = xlsxwriter.Workbook(full_path)
178
-
179
- pages_name: List[str] = []
180
-
181
- for page in self.pages:
182
- sheet_name = page.name[0:20]
183
-
184
- if sheet_name in pages_name:
185
- sheet_name = f'{sheet_name} ({pages_name.count(sheet_name) + 1})'
186
-
187
- # Allow only numbers, letters, spaces and _ or - characters
188
- # Other characters will be removed
189
- sheet_name = ''.join(e for e in sheet_name if e.isalnum() or e in [' ', '_', '-'])
190
- sheet = book.add_worksheet(sheet_name)
191
-
192
- if isinstance(page, CustomReportPage):
193
- page.builder(sheet)
194
- sheet.autofit()
195
- continue
196
-
197
- if page.freeze_header:
198
- sheet.freeze_panes(1, 0)
199
-
200
- for i, header in enumerate(page.headers):
201
- style = book.add_format(
202
- {
203
- 'align': header.align.value,
204
- 'font_color': '#000000' if use_black(header.color) else '#ffffff',
205
- 'bg_color': header.color,
206
- 'bold': header.bold,
207
- 'valign': 'vcenter',
208
- 'font_size': 11,
209
- 'top': 1,
210
- 'left': 1,
211
- 'right': 1,
212
- 'bottom': 1,
213
- 'font_name': 'Aptos Narrow',
214
- }
215
- )
216
- sheet.write(0, i, header.content, style)
217
-
218
- for i, row in enumerate(page.rows):
219
- for j, cell in enumerate(row.content):
220
- style = {
221
- 'align': cell.align.value,
222
- 'font_color': '#000000' if use_black(cell.color) else '#ffffff',
223
- 'bg_color': cell.color,
224
- 'bold': cell.bold,
225
- 'valign': 'vcenter',
226
- 'font_size': 11,
227
- 'top': 1,
228
- 'left': 1,
229
- 'right': 1,
230
- 'bottom': 1,
231
- 'font_name': 'Aptos Narrow',
232
- }
233
-
234
- value: Any = None
235
-
236
- if cell.data_type == ReportDataType.BOOL:
237
- value = 'Yes' if cell.content else 'No'
238
- elif cell.data_type == ReportDataType.DATETIME:
239
- value = cell.content.strftime(cell.datetime_format)
240
- elif cell.data_type == ReportDataType.INT:
241
- try:
242
- value = int(cell.content)
243
- except ValueError:
244
- value = cell.content
245
- log.warning(f'Invalid int value: {cell.content} in cell {i + 1}, {j}')
246
- elif cell.data_type == ReportDataType.FLOAT:
247
- try:
248
- value = float(cell.content)
249
- style.update({'num_format': '0.00'})
250
- except ValueError:
251
- value = cell.content
252
- log.warning(f'Invalid float value: {cell.content} in cell {i + 1}, {j}')
253
- elif cell.data_type == ReportDataType.CURRENCY:
254
- value = float(cell.content)
255
- style.update(
256
- {'num_format': f'"{cell.currency_symbol}" * #,##0.00;[Red]"{cell.currency_symbol}" * #,##0.00'}
257
- )
258
- else:
259
- value = cell.content
260
-
261
- sheet.write(i + 1, j, value, book.add_format(style))
262
-
263
- if row.compact:
264
- sheet.set_row(i + 1, None, None, {'level': 1, 'hidden': True})
265
- else:
266
- sheet.set_row(i + 1, None, None, {'collapsed': True})
267
-
268
- sheet.autofit()
269
- book.close()
270
-
271
- if password and msoffice_crypt_path:
272
- new_path = os.path.join(path, f'encrypted_{self.filename}')
273
- log.debug(f'Executing `{msoffice_crypt_path} -e -p "{password}" "{full_path}" "{new_path}"`')
274
- os.system(f'{msoffice_crypt_path} -e -p "{password}" "{full_path}" "{new_path}"')
275
- os.remove(full_path)
276
-
277
- with open(new_path, 'rb') as f:
278
- with open(full_path, 'wb') as f2:
279
- f2.write(f.read())
280
-
281
- os.remove(new_path)
282
-
283
- return full_path
1
+ """Report class"""
2
+
3
+ import logging
4
+ import os
5
+ import sys
6
+ import time
7
+ import warnings
8
+ from pathlib import Path
9
+ from typing import Any, Optional
10
+
11
+ import xlsxwriter
12
+ from pydantic import BaseModel, Field, field_validator
13
+
14
+ from layrz_sdk.helpers.color import use_black
15
+
16
+ from .custom_report_page import CustomReportPage
17
+ from .report_data_type import ReportDataType
18
+ from .report_format import ReportFormat
19
+ from .report_page import ReportPage
20
+
21
+ if sys.version_info >= (3, 11):
22
+ from typing import Self
23
+ else:
24
+ from typing_extensions import Self
25
+
26
+ log = logging.getLogger(__name__)
27
+
28
+
29
+ class Report(BaseModel):
30
+ """Report definition"""
31
+
32
+ name: str = Field(description='Name of the report. Length should be less than 60 characters')
33
+ pages: list[ReportPage | CustomReportPage] = Field(
34
+ description='List of report pages',
35
+ default_factory=list,
36
+ )
37
+ export_format: Optional[ReportFormat] = Field(description='Export format of the report', default=None)
38
+
39
+ @field_validator('export_format', mode='before')
40
+ def _validate_export_format(cls: 'Report', value: Any) -> Any:
41
+ if value is not None:
42
+ warnings.warn(
43
+ 'export_format is deprecated, use the export method instead',
44
+ DeprecationWarning,
45
+ stacklevel=2,
46
+ )
47
+
48
+ return value
49
+
50
+ @property
51
+ def filename(self: Self) -> str:
52
+ """Report filename"""
53
+ return f'{self.name}_{int(time.time() * 1000)}.xlsx'
54
+
55
+ def export(
56
+ self: Self,
57
+ path: str | Path,
58
+ export_format: Optional[ReportFormat] = None,
59
+ password: Optional[str] = None,
60
+ msoffice_crypt_path: str = '/opt/msoffice/bin/msoffice-crypt.exe',
61
+ ) -> Path | dict[str, Any]:
62
+ """
63
+ Export report to file
64
+
65
+ :param path: Path to save the report
66
+ :type path: str | Path
67
+ :param export_format: Format to export the report
68
+ :type export_format: ReportFormat
69
+ :param password: Password to protect the file (Only works with Microsoft Excel format)
70
+ :type password: str
71
+ :param msoffice_crypt_path: Path to the msoffice-crypt.exe executable, used to encrypt the file
72
+ :type msoffice_crypt_path: str
73
+ :return: Full path of the exported file or JSON representation of the report
74
+ :rtype: Path | dict
75
+ :raises AttributeError: If the export format is not supported
76
+ """
77
+ if export_format:
78
+ if export_format == ReportFormat.MICROSOFT_EXCEL:
79
+ return self._export_xlsx(path=path, password=password, msoffice_crypt_path=msoffice_crypt_path)
80
+ elif export_format == ReportFormat.JSON:
81
+ if password:
82
+ return {'name': self.name, 'is_protected': True, 'pages': []}
83
+ return self._export_json()
84
+ else:
85
+ raise AttributeError(f'Unsupported export format: {export_format}')
86
+
87
+ if self.export_format == ReportFormat.MICROSOFT_EXCEL:
88
+ return self._export_xlsx(path=path, password=password, msoffice_crypt_path=msoffice_crypt_path)
89
+ elif self.export_format == ReportFormat.JSON:
90
+ if password:
91
+ return {'name': self.name, 'is_protected': True, 'pages': []}
92
+ return self._export_json()
93
+ else:
94
+ raise AttributeError(f'Unsupported export format: {self.export_format}')
95
+
96
+ def export_as_json(self: Self) -> dict[str, Any]:
97
+ """Returns the report as a JSON dict"""
98
+ return self._export_json()
99
+
100
+ def _export_json(self: Self) -> dict[str, Any]:
101
+ """Returns a JSON dict of the report"""
102
+ json_pages = []
103
+ for page in self.pages:
104
+ if isinstance(page, CustomReportPage):
105
+ continue
106
+
107
+ headers = []
108
+ for header in page.headers:
109
+ headers.append(
110
+ {
111
+ 'content': header.content,
112
+ 'text_color': '#000000' if use_black(header.color) else '#ffffff',
113
+ 'color': header.color,
114
+ }
115
+ )
116
+ rows = []
117
+ for row in page.rows:
118
+ cells = []
119
+ for cell in row.content:
120
+ cells.append(
121
+ {
122
+ 'content': cell.content,
123
+ 'text_color': '#000000' if use_black(cell.color) else '#ffffff',
124
+ 'color': cell.color,
125
+ 'data_type': cell.data_type.value,
126
+ }
127
+ )
128
+ rows.append(
129
+ {
130
+ 'content': cells,
131
+ 'compact': row.compact,
132
+ }
133
+ )
134
+ json_pages.append(
135
+ {
136
+ 'name': page.name,
137
+ 'headers': headers,
138
+ 'rows': rows,
139
+ }
140
+ )
141
+
142
+ return {
143
+ 'name': self.name,
144
+ 'pages': json_pages,
145
+ }
146
+
147
+ def _export_xlsx(
148
+ self: Self,
149
+ path: str | Path,
150
+ password: Optional[str] = None,
151
+ msoffice_crypt_path: Optional[str] = None,
152
+ ) -> Path:
153
+ """
154
+ Export to Microsoft Excel (.xslx)
155
+
156
+ :param path: Path to save the report
157
+ :type path: str | Path
158
+ :param password: Password to protect the file
159
+ :type password: str
160
+ :param msoffice_crypt_path: Path to the msoffice-crypt.exe executable, used to encrypt the file
161
+ :type msoffice_crypt_path: str
162
+
163
+ :return: Full path of the exported file
164
+ :rtype: Path
165
+
166
+ :raises AttributeError: If the export format is not supported
167
+ """
168
+
169
+ if isinstance(path, str):
170
+ path = Path(path).resolve()
171
+
172
+ full_path = path / self.filename
173
+ if full_path.exists():
174
+ log.warning(f'File {full_path} already exists, overwriting it')
175
+ os.remove(full_path)
176
+
177
+ book = xlsxwriter.Workbook(full_path)
178
+
179
+ pages_name: list[str] = []
180
+
181
+ for page in self.pages:
182
+ sheet_name = page.name[0:20]
183
+
184
+ if sheet_name in pages_name:
185
+ sheet_name = f'{sheet_name} ({pages_name.count(sheet_name) + 1})'
186
+
187
+ # Allow only numbers, letters, spaces and _ or - characters
188
+ # Other characters will be removed
189
+ sheet_name = ''.join(e for e in sheet_name if e.isalnum() or e in [' ', '_', '-'])
190
+ sheet = book.add_worksheet(sheet_name)
191
+
192
+ if isinstance(page, CustomReportPage):
193
+ if page.extended_builder:
194
+ page.extended_builder(sheet=sheet, workbook=book)
195
+ elif page.builder:
196
+ page.builder(sheet)
197
+ else:
198
+ raise AttributeError('Custom report page must have a builder or extended_builder function')
199
+
200
+ sheet.autofit()
201
+ continue
202
+
203
+ if page.freeze_header:
204
+ sheet.freeze_panes(1, 0)
205
+
206
+ for i, header in enumerate(page.headers):
207
+ style = book.add_format(
208
+ {
209
+ 'align': header.align.value,
210
+ 'font_color': '#000000' if use_black(header.color) else '#ffffff',
211
+ 'bg_color': header.color,
212
+ 'bold': header.bold,
213
+ 'valign': 'vcenter',
214
+ 'font_size': 11,
215
+ 'top': 1,
216
+ 'left': 1,
217
+ 'right': 1,
218
+ 'bottom': 1,
219
+ 'font_name': 'Aptos Narrow',
220
+ }
221
+ )
222
+ sheet.write(0, i, header.content, style)
223
+
224
+ for i, row in enumerate(page.rows):
225
+ for j, cell in enumerate(row.content):
226
+ style = {
227
+ 'align': cell.align.value,
228
+ 'font_color': '#000000' if use_black(cell.color) else '#ffffff',
229
+ 'bg_color': cell.color,
230
+ 'bold': cell.bold,
231
+ 'valign': 'vcenter',
232
+ 'font_size': 11,
233
+ 'top': 1,
234
+ 'left': 1,
235
+ 'right': 1,
236
+ 'bottom': 1,
237
+ 'font_name': 'Aptos Narrow',
238
+ }
239
+
240
+ value: Any = None
241
+
242
+ if cell.data_type == ReportDataType.BOOL:
243
+ value = 'Yes' if cell.content else 'No'
244
+ elif cell.data_type == ReportDataType.DATETIME:
245
+ value = cell.content.strftime(cell.datetime_format)
246
+ elif cell.data_type == ReportDataType.INT:
247
+ try:
248
+ value = int(cell.content)
249
+ except ValueError:
250
+ value = cell.content
251
+ log.warning(f'Invalid int value: {cell.content} in cell {i + 1}, {j}')
252
+ elif cell.data_type == ReportDataType.FLOAT:
253
+ try:
254
+ value = float(cell.content)
255
+ style.update({'num_format': '0.00'})
256
+ except ValueError:
257
+ value = cell.content
258
+ log.warning(f'Invalid float value: {cell.content} in cell {i + 1}, {j}')
259
+ elif cell.data_type == ReportDataType.CURRENCY:
260
+ value = float(cell.content)
261
+ style.update(
262
+ {'num_format': f'"{cell.currency_symbol}" * #,##0.00;[Red]"{cell.currency_symbol}" * #,##0.00'}
263
+ )
264
+ else:
265
+ value = cell.content
266
+
267
+ sheet.write(i + 1, j, value, book.add_format(style))
268
+
269
+ if row.compact:
270
+ sheet.set_row(i + 1, None, None, {'level': 1, 'hidden': True})
271
+ else:
272
+ sheet.set_row(i + 1, None, None, {'collapsed': True})
273
+
274
+ sheet.autofit()
275
+ book.close()
276
+
277
+ if password and msoffice_crypt_path:
278
+ new_path = os.path.join(path, f'encrypted_{self.filename}')
279
+ log.debug(f'Executing `{msoffice_crypt_path} -e -p "{password}" "{full_path}" "{new_path}"`')
280
+ os.system(f'{msoffice_crypt_path} -e -p "{password}" "{full_path}" "{new_path}"')
281
+ os.remove(full_path)
282
+
283
+ with open(new_path, 'rb') as f:
284
+ with open(full_path, 'wb') as f2:
285
+ f2.write(f.read())
286
+
287
+ os.remove(new_path)
288
+
289
+ return full_path
@@ -1,40 +1,40 @@
1
- """Report col"""
2
-
3
- import sys
4
- import warnings
5
- from typing import Any, Optional
6
-
7
- from pydantic import BaseModel, Field, field_validator
8
-
9
- from .report_data_type import ReportDataType
10
- from .text_alignment import TextAlignment
11
-
12
- if sys.version_info >= (3, 11):
13
- from typing import Self
14
- else:
15
- from typing_extensions import Self
16
-
17
-
18
- class ReportCol(BaseModel):
19
- """Report column entity"""
20
-
21
- content: Any = Field(description='Column content')
22
- color: str = Field(description='Column color', default='#ffffff')
23
- text_color: Optional[str] = Field(description='Column text color', default=None)
24
- align: TextAlignment = Field(description='Column text alignment', default=TextAlignment.LEFT)
25
- data_type: ReportDataType = Field(description='Column data type', default=ReportDataType.STR)
26
- datetime_format: str = Field(description='Datetime format', default='%Y-%m-%d %H:%M:%S')
27
- currency_symbol: str = Field(description='Currency symbol', default='')
28
- bold: bool = Field(description='Bold text', default=False)
29
-
30
- @field_validator('text_color', mode='before')
31
- def _validate_text_color(cls: Self, value: Any) -> Any:
32
- """Validate text color"""
33
- if value is not None:
34
- warnings.warn(
35
- 'text_color is deprecated, the algorithm will calculate the rigth text color instead',
36
- DeprecationWarning,
37
- stacklevel=2,
38
- )
39
-
40
- return value
1
+ """Report col"""
2
+
3
+ import sys
4
+ import warnings
5
+ from typing import Any, Optional
6
+
7
+ from pydantic import BaseModel, Field, field_validator
8
+
9
+ from .report_data_type import ReportDataType
10
+ from .text_alignment import TextAlignment
11
+
12
+ if sys.version_info >= (3, 11):
13
+ from typing import Self
14
+ else:
15
+ from typing_extensions import Self
16
+
17
+
18
+ class ReportCol(BaseModel):
19
+ """Report column entity"""
20
+
21
+ content: Any = Field(description='Column content')
22
+ color: str = Field(description='Column color', default='#ffffff')
23
+ text_color: Optional[str] = Field(description='Column text color', default=None)
24
+ align: TextAlignment = Field(description='Column text alignment', default=TextAlignment.LEFT)
25
+ data_type: ReportDataType = Field(description='Column data type', default=ReportDataType.STR)
26
+ datetime_format: str = Field(description='Datetime format', default='%Y-%m-%d %H:%M:%S')
27
+ currency_symbol: str = Field(description='Currency symbol', default='')
28
+ bold: bool = Field(description='Bold text', default=False)
29
+
30
+ @field_validator('text_color', mode='before')
31
+ def _validate_text_color(cls: Self, value: Any) -> Any:
32
+ """Validate text color"""
33
+ if value is not None:
34
+ warnings.warn(
35
+ 'text_color is deprecated, the algorithm will calculate the rigth text color instead',
36
+ DeprecationWarning,
37
+ stacklevel=2,
38
+ )
39
+
40
+ return value