python-google-sheets 0.1.1__tar.gz → 1.0.0__tar.gz

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.
Files changed (31) hide show
  1. python_google_sheets-1.0.0/PKG-INFO +491 -0
  2. python_google_sheets-1.0.0/README.md +475 -0
  3. python_google_sheets-1.0.0/google_sheets/__init__.py +40 -0
  4. {python_google_sheets-0.1.1 → python_google_sheets-1.0.0}/google_sheets/api_request.py +174 -49
  5. {python_google_sheets-0.1.1 → python_google_sheets-1.0.0}/google_sheets/google_sheets.py +36 -45
  6. {python_google_sheets-0.1.1 → python_google_sheets-1.0.0}/google_sheets/spreadsheet_requests/__init__.py +6 -0
  7. python_google_sheets-1.0.0/google_sheets/spreadsheet_requests/conditional_format_rule.py +172 -0
  8. {python_google_sheets-0.1.1 → python_google_sheets-1.0.0}/google_sheets/spreadsheet_requests/general_models.py +3 -0
  9. python_google_sheets-1.0.0/google_sheets/spreadsheet_requests/spreadsheet.py +91 -0
  10. {python_google_sheets-0.1.1 → python_google_sheets-1.0.0}/google_sheets/spreadsheet_requests/update_cells.py +51 -48
  11. python_google_sheets-1.0.0/google_sheets/spreadsheet_requests/update_sheet_properties.py +47 -0
  12. {python_google_sheets-0.1.1 → python_google_sheets-1.0.0}/google_sheets/styles.py +14 -0
  13. python_google_sheets-1.0.0/google_sheets/utils.py +64 -0
  14. {python_google_sheets-0.1.1 → python_google_sheets-1.0.0}/pyproject.toml +2 -2
  15. python_google_sheets-1.0.0/python_google_sheets.egg-info/PKG-INFO +491 -0
  16. python_google_sheets-0.1.1/PKG-INFO +0 -53
  17. python_google_sheets-0.1.1/README.md +0 -37
  18. python_google_sheets-0.1.1/google_sheets/__init__.py +0 -22
  19. python_google_sheets-0.1.1/google_sheets/spreadsheet_requests/conditional_format_rule.py +0 -95
  20. python_google_sheets-0.1.1/google_sheets/spreadsheet_requests/spreadsheet.py +0 -91
  21. python_google_sheets-0.1.1/google_sheets/spreadsheet_requests/update_sheet_properties.py +0 -48
  22. python_google_sheets-0.1.1/google_sheets/utils.py +0 -41
  23. python_google_sheets-0.1.1/python_google_sheets.egg-info/PKG-INFO +0 -53
  24. {python_google_sheets-0.1.1 → python_google_sheets-1.0.0}/LICENSE +0 -0
  25. {python_google_sheets-0.1.1 → python_google_sheets-1.0.0}/google_sheets/spreadsheet_requests/dimension.py +0 -0
  26. {python_google_sheets-0.1.1 → python_google_sheets-1.0.0}/google_sheets/spreadsheet_requests/merge_cells.py +0 -0
  27. {python_google_sheets-0.1.1 → python_google_sheets-1.0.0}/python_google_sheets.egg-info/SOURCES.txt +0 -0
  28. {python_google_sheets-0.1.1 → python_google_sheets-1.0.0}/python_google_sheets.egg-info/dependency_links.txt +0 -0
  29. {python_google_sheets-0.1.1 → python_google_sheets-1.0.0}/python_google_sheets.egg-info/requires.txt +0 -0
  30. {python_google_sheets-0.1.1 → python_google_sheets-1.0.0}/python_google_sheets.egg-info/top_level.txt +0 -0
  31. {python_google_sheets-0.1.1 → python_google_sheets-1.0.0}/setup.cfg +0 -0
@@ -0,0 +1,491 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-google-sheets
3
+ Version: 1.0.0
4
+ Summary: A lightweight and efficient Python wrapper for the Google Sheets API v4
5
+ Author-email: Timofey Egorov <timegorr@gmail.com>
6
+ License-Expression: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Operating System :: OS Independent
9
+ Requires-Python: >=3.11
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: google-auth
13
+ Requires-Dist: google-api-python-client
14
+ Requires-Dist: pydantic>=2.0.0
15
+ Dynamic: license-file
16
+
17
+ # python-google-sheets
18
+
19
+ A lightweight and efficient Python wrapper for the Google Sheets API v4, leveraging `service account` credentials for seamless authentication.
20
+
21
+ ## Features
22
+
23
+ - **Batch Operations:** Read multiple ranges or write values and formatting in a single `batchUpdate` request to optimize API quotas.
24
+ - **Conditional Formatting:** Full support for boolean rules, gradient rules, presets, updating and deleting rules.
25
+ - **Rich Styling:** Built-in `ColorStyle` objects for all standard Google Sheets web interface colors.
26
+ - **Developer Friendly:** Designed to simplify complex API interactions into intuitive Pythonic calls.
27
+
28
+ ## Table of Contents
29
+ - [Features](#features)
30
+ - [Installation](#installation)
31
+ - [Quick Start](#quickstart)
32
+ - [GoogleSheets methods](#googlesheets-methods)
33
+ - [ApiRequest methods for batchUpdate](#apirequest-methods-for-batchupdate)
34
+ - [Cell updates](#cell-updates)
35
+ - [Conditional formatting](#conditional-formatting)
36
+ - [Sheet-level operations](#sheet-level-operations)
37
+ - [Merge and freeze](#merge-and-freeze)
38
+ - [Rows and columns](#rows-and-columns)
39
+ - [Utility helpers](#utility-helpers)
40
+ - [Full example: send multiple requests at once](#full-example-send-multiple-requests-at-once)
41
+ - [Support & Feedback](#support--feedback)
42
+
43
+ ## Installation
44
+
45
+ ```sh
46
+ pip install python-google-sheets
47
+ ```
48
+
49
+ ## Quickstart
50
+
51
+ ```python
52
+ from google_sheets import GoogleSheets
53
+
54
+ service = GoogleSheets.build_service('service_account.json')
55
+ spreadsheet_id = '1abc...'
56
+ sheet_id = 0
57
+ ```
58
+
59
+ ---
60
+
61
+ ## `GoogleSheets` methods
62
+
63
+ ### `build_service(path_to_creds: str = 'service_account.json') -> 'Resource'`
64
+ Create an authorized Google Sheets service client.
65
+
66
+ ```python
67
+ service = GoogleSheets.build_service('service_account.json')
68
+ ```
69
+
70
+ ### `create_spreadsheet(title: str, folder_id: str = None, editing_permissions_for_everyone: bool = False, emails: list[str] = None, email: str = None, path_to_creds: str = 'service_account.json') -> tuple[str, str]`
71
+ Create a new spreadsheet and optionally share it. `editing_permissions_for_everyone=True` will allow anyone with the link to edit, while `emails` and `email` parameters can be used to share with specific users or groups. You can also specify a `folder_id` to create the spreadsheet in a specific Google Drive folder.
72
+
73
+ > **Important:** this method may not work with `service accounts` created after May 2024 due to Google's updated security policies.
74
+
75
+ ```python
76
+ spreadsheet_id, url = GoogleSheets.create_spreadsheet(
77
+ title='Monthly Sales Report',
78
+ folder_id='1a2b3c4d5e6f7g8h9i0j',
79
+ emails=['analyst@company.com', 'lead@company.com'],
80
+ path_to_creds='service_account.json',
81
+ )
82
+ ```
83
+
84
+ ### `update_spreadsheet(spreadsheet_id: str, api_requests: list[dict], service: 'Resource') -> None`
85
+ Execute a `batchUpdate` with one or more request objects.
86
+
87
+ ```python
88
+ from google_sheets import ApiRequest
89
+
90
+ requests = [
91
+ ApiRequest.update_cells(sheet_id=0, range_='A1:C1', values=['Product', 'Qty', 'Revenue']),
92
+ ]
93
+ GoogleSheets.update_spreadsheet(spreadsheet_id, requests, service)
94
+ ```
95
+
96
+ ### `copy_sheet(source_spreadsheet_id: str, source_sheet_id: str, destination_spreadsheet_id: str, service: 'Resource') -> SheetProperties`
97
+ Copy a sheet from one spreadsheet into another.
98
+
99
+ ```python
100
+ GoogleSheets.copy_sheet(
101
+ source_spreadsheet_id='1source...',
102
+ source_sheet_id=0,
103
+ destination_spreadsheet_id='1destination...',
104
+ service=service,
105
+ )
106
+ ```
107
+
108
+ ### `get_spreadsheet(spreadsheet_id: str, service: 'Resource') -> Spreadsheet`
109
+ Fetch spreadsheet metadata (sheets, properties, etc.).
110
+
111
+ ```python
112
+ spreadsheet = GoogleSheets.get_spreadsheet(spreadsheet_id, service)
113
+ print([sheet.properties.title for sheet in spreadsheet.sheets])
114
+ ```
115
+
116
+ ### `get_spreadsheet_range_values(spreadsheet_id: str, sheets: list[str | int], ranges: list[list[str]], service: 'Resource') -> list[list[SimpleType] | list[list[SimpleType]]]`
117
+ Read values from multiple ranges across one or more sheets.
118
+
119
+ ```python
120
+ values = GoogleSheets.get_spreadsheet_range_values(
121
+ spreadsheet_id=spreadsheet_id,
122
+ sheets=[0, 'Summary'],
123
+ ranges=[['A2:C8'], ['B2:B12']],
124
+ service=service,
125
+ )
126
+ print(values)
127
+ ```
128
+
129
+ ---
130
+
131
+ ## `ApiRequest` methods for `batchUpdate`
132
+
133
+ Each method below returns a request `dict` that you can pass to `GoogleSheets.update_spreadsheet(...)`.
134
+
135
+ ### Cell updates
136
+
137
+ ### `update_cells(sheet_id: int, range_: str, values: list[list[int | float | bool | str]] | list[int | float | bool | str] = None, cell_formats: list[list[CellFormat]] | list[CellFormat] = None) -> dict`
138
+ Write values, formats, or both.
139
+
140
+ ```python
141
+ from google_sheets import ApiRequest, CellFormat, TextFormat
142
+
143
+ req = ApiRequest.update_cells(
144
+ sheet_id=0,
145
+ range_='A1:B2',
146
+ values=[['Plan', 120], ['Actual', 135]],
147
+ cell_formats=[
148
+ [CellFormat(text_format=TextFormat(bold=True)), None],
149
+ [CellFormat(text_format=TextFormat(italic=True)), None],
150
+ ],
151
+ )
152
+ ```
153
+
154
+ ### Conditional formatting
155
+
156
+ ### `add_boolean_format_rule(*, sheet_id: int, ranges: list[str], condition_type: ConditionType, condition_values: list[ConditionValue] = None, cell_format: CellFormat) -> dict`
157
+ Add a rule (for example, highlight values greater than `100`).
158
+
159
+ ```python
160
+ from google_sheets import ConditionType, ConditionValue, CellFormat, Color_, TextFormat
161
+
162
+ req = ApiRequest.add_boolean_format_rule(
163
+ sheet_id=0,
164
+ ranges=['C2:C100'],
165
+ condition_type=ConditionType.NUMBER_GREATER,
166
+ condition_values=[ConditionValue(user_entered_value=100)],
167
+ cell_format=CellFormat(
168
+ background_color_style=Color_.ConditionalFormatting.GREEN,
169
+ text_format=TextFormat(bold=True),
170
+ ),
171
+ )
172
+ ```
173
+
174
+ ### `GradientRule.add(*, sheet_id: int, ranges: list[str], interpolation_points: tuple[IPTypeAndValue, IPTypeAndValue] | tuple[IPTypeAndValue, IPTypeAndValue, IPTypeAndValue], interpolation_point_colors: tuple[ColorStyle, ColorStyle] | tuple[ColorStyle, ColorStyle, ColorStyle]) -> dict`
175
+ Add a custom color scale with 2 or 3 interpolation points.
176
+
177
+ ```python
178
+ from google_sheets import InterpolationPointType, Color_
179
+
180
+ req = ApiRequest.GradientRule.add(
181
+ sheet_id=0,
182
+ ranges=['D2:D100'],
183
+ interpolation_points=(
184
+ (InterpolationPointType.MIN, None),
185
+ (InterpolationPointType.NUMBER, 150),
186
+ (InterpolationPointType.MAX, None),
187
+ ),
188
+ interpolation_point_colors=(
189
+ Color_('#2ca958'),
190
+ Color_.Basic.LIGHT_YELLOW_3,
191
+ Color_.ConditionalFormatting.RED,
192
+ )
193
+ )
194
+ ```
195
+
196
+ ### `GradientRule.add_preset(*, sheet_id: int, ranges: list[str], preset: Preset) -> dict`
197
+ Add a gradient rule from a built-in preset.
198
+
199
+ ```python
200
+ req = ApiRequest.GradientRule.add_preset(
201
+ sheet_id=0,
202
+ ranges=['E2:E100'],
203
+ preset=ApiRequest.GradientRule.Preset.RED_YELLOW_GREEN_PERCENTILE,
204
+ )
205
+ ```
206
+
207
+ ### `delete_conditional_format_rule(*, sheet_id: int, index: int) -> dict`
208
+ Delete a conditional format rule by index.
209
+
210
+ ```python
211
+ req = ApiRequest.delete_conditional_format_rule(sheet_id=0, index=0)
212
+ ```
213
+
214
+ ### `update_conditional_format_rule(*, sheet_id: int, index: int, rule: ConditionalFormatRule) -> dict`
215
+ Replace an existing conditional format rule by index.
216
+
217
+ ```python
218
+ from google_sheets import GoogleSheets, ApiRequest
219
+ from google_sheets import (
220
+ ConditionalFormatRule,
221
+ GradientRule,
222
+ InterpolationPoint,
223
+ InterpolationPointType,
224
+ BooleanRule,
225
+ BooleanCondition,
226
+ ConditionType,
227
+ ConditionValue,
228
+ GridRange,
229
+ CellFormat,
230
+ TextFormat,
231
+ Color_,
232
+ )
233
+
234
+ rule1 = ConditionalFormatRule(
235
+ ranges=[GridRange(sheet_id=0, **ApiRequest._split_excel_range('A1:A1000',return_as_dict=True))],
236
+ boolean_rule=BooleanRule(
237
+ condition=BooleanCondition(
238
+ type=ConditionType.NUMBER_NOT_BETWEEN,
239
+ values=[ConditionValue(user_entered_value=10), ConditionValue(user_entered_value=30)]
240
+ ),
241
+ format=CellFormat(
242
+ background_color_style=Color_.ConditionalFormatting.GREEN,
243
+ text_format=TextFormat(bold=True)
244
+ ),
245
+ )
246
+ )
247
+ rule2 = ConditionalFormatRule(
248
+ ranges=[GridRange(sheet_id=0, **ApiRequest._split_excel_range('A1:A1000',return_as_dict=True))],
249
+ gradient_rule=GradientRule(
250
+ minpoint=InterpolationPoint(
251
+ color_style=Color_.ConditionalFormatting.GREEN,
252
+ type=InterpolationPointType.MIN,
253
+ ),
254
+ midpoint=InterpolationPoint(
255
+ color_style=Color_.ConditionalFormatting.YELLOW,
256
+ type=InterpolationPointType.PERCENTILE,
257
+ value=50,
258
+ ),
259
+ maxpoint=InterpolationPoint(
260
+ color_style=Color_.ConditionalFormatting.RED,
261
+ type=InterpolationPointType.MAX,
262
+ ),
263
+ )
264
+ )
265
+
266
+ req1 = ApiRequest.update_conditional_format_rule(sheet_id=0, index=0, rule=rule1)
267
+ req2 = ApiRequest.update_conditional_format_rule(sheet_id=0, index=1, rule=rule2)
268
+
269
+ GoogleSheets.update_spreadsheet(SPREADSHEET_ID, api_requests=[req1, req2], service=service)
270
+ ```
271
+
272
+ ### Sheet-level operations
273
+
274
+ ### `update_sheet_title(sheet_id: int, title: str) -> dict`
275
+ Rename a sheet.
276
+
277
+ ```python
278
+ req = ApiRequest.update_sheet_title(sheet_id=0, title='June 2026')
279
+ ```
280
+
281
+ ### `remove_grid(sheet_id: int) -> dict`
282
+ Hide grid lines on a sheet.
283
+
284
+ ```python
285
+ req = ApiRequest.remove_grid(sheet_id=0)
286
+ ```
287
+
288
+ ### `set_sheet_size(sheet_id: int, rows: int = None, columns: int = None) -> dict`
289
+ Resize a sheet.
290
+
291
+ ```python
292
+ req = ApiRequest.set_sheet_size(sheet_id=0, rows=200, columns=12)
293
+ ```
294
+
295
+ ### `delete_sheet(sheet_id: int) -> dict`
296
+ Delete a sheet.
297
+
298
+ ```python
299
+ req = ApiRequest.delete_sheet(sheet_id=3)
300
+ ```
301
+
302
+ ### `add_sheet(*, sheet_id: int = None, title: str = None, index: int = None, hidden: bool = None, row_count: int = None, column_count: int = None, frozen_row_count: int = None, frozen_column_count: int = None, hide_grid_lines: bool = None) -> dict`
303
+ Create a new sheet with optional properties.
304
+
305
+ ```python
306
+ req = ApiRequest.add_sheet(
307
+ title='Archive',
308
+ row_count=100,
309
+ column_count=8,
310
+ hide_grid_lines=True,
311
+ )
312
+ ```
313
+
314
+ ### Merge and freeze
315
+
316
+ ### `merge_cells(sheet_id: int, range_: str, merge_type: MergeType = MergeType.MERGE_ALL) -> dict`
317
+ Merge cells in a range.
318
+
319
+ ```python
320
+ req = ApiRequest.merge_cells(sheet_id=0, range_='A1:C1')
321
+ ```
322
+
323
+ ### `unmerge_cells(sheet_id: int, range_: str = None, start_row: int = None, end_row: int = None, start_column: int | str = None, end_column: int | str = None) -> dict`
324
+ Unmerge cells in a range (or by explicit indexes).
325
+
326
+ ```python
327
+ req = ApiRequest.unmerge_cells(sheet_id=0, range_='A1:C1')
328
+ ```
329
+
330
+ ### `freeze(sheet_id: int, rows: int = 0, columns: int = 0) -> dict`
331
+ Freeze top rows and/or left columns.
332
+
333
+ ```python
334
+ req = ApiRequest.freeze(sheet_id=0, rows=1, columns=1)
335
+ ```
336
+
337
+ ### Rows and columns
338
+
339
+ ### `insert_rows(sheet_id: int, start_index: int, end_index: int = None, inherit_from_before: bool = True) -> dict`
340
+ Insert one or more rows (zero-based indexes).
341
+
342
+ ```python
343
+ req = ApiRequest.insert_rows(sheet_id=0, start_index=5, end_index=9)
344
+ ```
345
+
346
+ ### `insert_columns(sheet_id: int, start_index: int, end_index: int = None, inherit_from_before: bool = True) -> dict`
347
+ Insert columns (zero-based indexes).
348
+
349
+ ```python
350
+ req = ApiRequest.insert_columns(sheet_id=0, start_index=2, end_index=3)
351
+ ```
352
+
353
+ ### `clear_columns(sheet_id: int, rows_count: int, start_index: int, end_index: int = None) -> dict`
354
+ Clear values and formatting in one or more columns.
355
+
356
+ ```python
357
+ req = ApiRequest.clear_columns(sheet_id=0, rows_count=500, start_index=4, end_index=5)
358
+ ```
359
+
360
+ ### `set_column_width(sheet_id: int, col_no_or_letter: int | str, width: int) -> dict`
361
+ Set a specific column width in pixels.
362
+
363
+ ```python
364
+ req = ApiRequest.set_column_width(sheet_id=0, col_no_or_letter='B', width=220)
365
+ ```
366
+
367
+ ### `set_row_height(sheet_id: int, row_no: int, height: int) -> dict`
368
+ Set a specific row height in pixels.
369
+
370
+ ```python
371
+ req = ApiRequest.set_row_height(sheet_id=0, row_no=1, height=36)
372
+ ```
373
+
374
+ ### `set_standard_cell_dimensions(sheet_id: int, rows: int, columns: int) -> tuple[dict, dict]`
375
+ Apply default row/column dimensions to a full area.
376
+
377
+ ```python
378
+ row_req, col_req = ApiRequest.set_standard_cell_dimensions(
379
+ sheet_id=0,
380
+ rows=1000,
381
+ columns=26,
382
+ )
383
+ ```
384
+
385
+ ### `add_dimension_group(sheet_id: int, dimension: Dimension, start_index: int, end_index: int) -> dict`
386
+ Create an outline group for rows or columns.
387
+
388
+ ```python
389
+ from google_sheets import Dimension
390
+
391
+ req = ApiRequest.add_dimension_group(
392
+ sheet_id=0,
393
+ dimension=Dimension.ROWS,
394
+ start_index=10,
395
+ end_index=30,
396
+ )
397
+ ```
398
+
399
+ ### `delete_dimension_group(sheet_id: int, dimension: Dimension, start_index: int, end_index: int) -> dict`
400
+ Remove an existing outline group.
401
+
402
+ ```python
403
+ req = ApiRequest.delete_dimension_group(
404
+ sheet_id=0,
405
+ dimension=Dimension.ROWS,
406
+ start_index=10,
407
+ end_index=30,
408
+ )
409
+ ```
410
+
411
+ ---
412
+
413
+ ## Utility helpers
414
+
415
+ ### `get_spreadsheet_id_from_url(url: str) -> str`
416
+ Extract a spreadsheet ID from a full Google Sheets URL.
417
+
418
+ ```python
419
+ from google_sheets import get_spreadsheet_id_from_url
420
+
421
+ spreadsheet_id = get_spreadsheet_id_from_url(
422
+ "https://docs.google.com/spreadsheets/d/1abcDEFghiJKLmnoPQRstuVWXyz/edit?gid=0#gid=0"
423
+ )
424
+ ```
425
+
426
+ ### `col_num_to_letter(col_num: int) -> str`
427
+ Convert a 1-based column number to letters.
428
+
429
+ ```python
430
+ from google_sheets import col_num_to_letter
431
+
432
+ print(col_num_to_letter(28)) # AB
433
+ ```
434
+
435
+ ### `col_letter_to_num(col_letter: str) -> int`
436
+ Convert column letters to a 1-based number.
437
+
438
+ ```python
439
+ from google_sheets import col_letter_to_num
440
+
441
+ print(col_letter_to_num('AB')) # 28
442
+ ```
443
+
444
+ ### `rowcol_to_a1(row: int, col: int) -> str`
445
+ Convert `(row, col)` into A1 notation.
446
+
447
+ ```python
448
+ from google_sheets import rowcol_to_a1
449
+
450
+ print(rowcol_to_a1(7, 3)) # C7
451
+ ```
452
+
453
+ ### `a1_to_rowcol(a1: str) -> tuple[int, int]`
454
+ Convert A1 notation into `(row, col)`.
455
+
456
+ ```python
457
+ from google_sheets import a1_to_rowcol
458
+
459
+ print(a1_to_rowcol('C7')) # (7, 3)
460
+ ```
461
+
462
+ ### `float_sum(*floats: float) -> float`
463
+ Sum floating-point numbers with high precision using the Decimal class.
464
+
465
+ ```python
466
+ from google_sheets import float_sum
467
+
468
+ print(float_sum(0.1, 0.2, 0.3)) # 0.6
469
+ ```
470
+
471
+ ---
472
+
473
+ ## Full example: send multiple requests at once
474
+
475
+ ```python
476
+ requests = [
477
+ ApiRequest.update_cells(sheet_id=0, range_='A1', values=['KPI']),
478
+ ApiRequest.freeze(sheet_id=0, rows=1),
479
+ ApiRequest.set_column_width(sheet_id=0, col_no_or_letter=1, width=240),
480
+ ]
481
+ GoogleSheets.update_spreadsheet(spreadsheet_id, requests, service)
482
+ ```
483
+
484
+ ---
485
+
486
+ ## Support & Feedback
487
+
488
+ If you have any questions, encounter issues, or want to suggest improvements:
489
+
490
+ - **Bug Reports & Feature Requests:** Please use [GitHub Issues](https://github.com/Timofey28/python-google-sheets/issues) to report bugs or submit ideas.
491
+ - **Questions:** For general inquiries or help with integration, feel free to reach out via email at [timegorr@gmail.com](mailto:timegorr@gmail.com).