python-google-sheets 0.1.0__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.
- google_sheets/__init__.py +22 -0
- google_sheets/api_request.py +453 -0
- google_sheets/google_sheets.py +181 -0
- google_sheets/spreadsheet_requests/__init__.py +60 -0
- google_sheets/spreadsheet_requests/conditional_format_rule.py +95 -0
- google_sheets/spreadsheet_requests/dimension.py +77 -0
- google_sheets/spreadsheet_requests/general_models.py +79 -0
- google_sheets/spreadsheet_requests/merge_cells.py +35 -0
- google_sheets/spreadsheet_requests/spreadsheet.py +91 -0
- google_sheets/spreadsheet_requests/update_cells.py +242 -0
- google_sheets/spreadsheet_requests/update_sheet_properties.py +48 -0
- google_sheets/styles.py +146 -0
- google_sheets/utils.py +41 -0
- python_google_sheets-0.1.0.dist-info/METADATA +20 -0
- python_google_sheets-0.1.0.dist-info/RECORD +18 -0
- python_google_sheets-0.1.0.dist-info/WHEEL +5 -0
- python_google_sheets-0.1.0.dist-info/licenses/LICENSE +21 -0
- python_google_sheets-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from .conditional_format_rule import (
|
|
2
|
+
AddConditionalFormatRule,
|
|
3
|
+
DeleteConditionalFormatRule,
|
|
4
|
+
UpdateConditionalFormatRule,
|
|
5
|
+
ConditionalFormatRule,
|
|
6
|
+
GradientRule,
|
|
7
|
+
InterpolationPoint,
|
|
8
|
+
InterpolationPointType,
|
|
9
|
+
)
|
|
10
|
+
from .update_sheet_properties import (
|
|
11
|
+
UpdateSheetProperties,
|
|
12
|
+
SheetProperties,
|
|
13
|
+
GridProperties,
|
|
14
|
+
)
|
|
15
|
+
from .merge_cells import (
|
|
16
|
+
MergeType,
|
|
17
|
+
MergeCells,
|
|
18
|
+
UnmergeCells,
|
|
19
|
+
)
|
|
20
|
+
from .dimension import (
|
|
21
|
+
InsertDimension,
|
|
22
|
+
UpdateDimensionProperties,
|
|
23
|
+
AddDimensionGroup,
|
|
24
|
+
DeleteDimensionGroup,
|
|
25
|
+
DimensionProperties,
|
|
26
|
+
DimensionRange,
|
|
27
|
+
Dimension,
|
|
28
|
+
)
|
|
29
|
+
from .update_cells import (
|
|
30
|
+
UpdateCells,
|
|
31
|
+
RowData,
|
|
32
|
+
CellData,
|
|
33
|
+
ExtendedValue,
|
|
34
|
+
CellFormat,
|
|
35
|
+
NumberFormat,
|
|
36
|
+
NumberFormatType,
|
|
37
|
+
TextFormat,
|
|
38
|
+
TextDirection,
|
|
39
|
+
TextRotation,
|
|
40
|
+
Borders,
|
|
41
|
+
Border,
|
|
42
|
+
Style as BorderStyle,
|
|
43
|
+
HorizontalAlignment,
|
|
44
|
+
VerticalAlignment,
|
|
45
|
+
WrapStrategy,
|
|
46
|
+
)
|
|
47
|
+
from .spreadsheet import (
|
|
48
|
+
Spreadsheet,
|
|
49
|
+
SpreadsheetProperties,
|
|
50
|
+
Sheet,
|
|
51
|
+
AddSheet,
|
|
52
|
+
DeleteSheet,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
from .general_models import (
|
|
56
|
+
Color,
|
|
57
|
+
ColorStyle,
|
|
58
|
+
GridRange,
|
|
59
|
+
FieldMask,
|
|
60
|
+
)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains the models for the following Google Sheets API requests:
|
|
3
|
+
|
|
4
|
+
- AddConditionalFormatRule
|
|
5
|
+
- DeleteConditionalFormatRule
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from enum import StrEnum
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel, Field, model_validator
|
|
12
|
+
|
|
13
|
+
from .general_models import ColorStyle, GridRange
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class InterpolationPointType(StrEnum):
|
|
17
|
+
MIN = 'MIN'
|
|
18
|
+
MAX = 'MAX'
|
|
19
|
+
NUMBER = 'NUMBER'
|
|
20
|
+
PERCENT = 'PERCENT'
|
|
21
|
+
PERCENTILE = 'PERCENTILE'
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class InterpolationPoint(BaseModel):
|
|
25
|
+
color_style: ColorStyle = Field(..., alias='colorStyle')
|
|
26
|
+
type: InterpolationPointType
|
|
27
|
+
value: str = None
|
|
28
|
+
|
|
29
|
+
class Config:
|
|
30
|
+
populate_by_name = True
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class BooleanRule(BaseModel):
|
|
34
|
+
condition: dict
|
|
35
|
+
format: dict
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class GradientRule(BaseModel):
|
|
39
|
+
minpoint: InterpolationPoint
|
|
40
|
+
midpoint: InterpolationPoint = None
|
|
41
|
+
maxpoint: InterpolationPoint
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ConditionalFormatRule(BaseModel):
|
|
45
|
+
ranges: list[GridRange]
|
|
46
|
+
boolean_rule: BooleanRule = Field(None, alias='booleanRule')
|
|
47
|
+
gradient_rule: GradientRule = Field(None, alias='gradientRule')
|
|
48
|
+
|
|
49
|
+
@model_validator(mode='before')
|
|
50
|
+
def init_before(cls, values: dict):
|
|
51
|
+
bool_rule = values.get('boolean_rule', values.get('booleanRule'))
|
|
52
|
+
grad_rule = values.get('gradient_rule', values.get('gradientRule'))
|
|
53
|
+
if (bool_rule is None) == (grad_rule is None):
|
|
54
|
+
raise ValueError('either boolean_rule or gradient_rule must be set, but not both')
|
|
55
|
+
return values
|
|
56
|
+
|
|
57
|
+
class Config:
|
|
58
|
+
populate_by_name = True
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class AddConditionalFormatRule(BaseModel):
|
|
62
|
+
rule: ConditionalFormatRule
|
|
63
|
+
index: int = 0
|
|
64
|
+
|
|
65
|
+
def dict(self, *args, **kwargs):
|
|
66
|
+
class_name = self.__class__.__name__[0].lower() + self.__class__.__name__[1:]
|
|
67
|
+
return {class_name: json.loads(super().json(*args, **kwargs, by_alias=True, exclude_none=True))}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class DeleteConditionalFormatRule(BaseModel):
|
|
71
|
+
index: int
|
|
72
|
+
sheet_id: int
|
|
73
|
+
|
|
74
|
+
def dict(self, *args, **kwargs):
|
|
75
|
+
class_name = self.__class__.__name__[0].lower() + self.__class__.__name__[1:]
|
|
76
|
+
return {class_name: json.loads(super().json(*args, **kwargs, by_alias=True, exclude_none=True))}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class UpdateConditionalFormatRule(BaseModel):
|
|
80
|
+
index: int
|
|
81
|
+
sheet_id: int = Field(..., serialization_alias='sheetId')
|
|
82
|
+
|
|
83
|
+
# Union field instruction can be only one of the following:
|
|
84
|
+
rule: ConditionalFormatRule = None
|
|
85
|
+
new_index: int = Field(None, serialization_alias='newIndex')
|
|
86
|
+
|
|
87
|
+
@model_validator(mode='before')
|
|
88
|
+
def init_before(cls, values: dict):
|
|
89
|
+
if ('rule' in values) == ('new_index' in values):
|
|
90
|
+
raise ValueError('either rule or new_index must be set, but not both')
|
|
91
|
+
return values
|
|
92
|
+
|
|
93
|
+
def dict(self, *args, **kwargs):
|
|
94
|
+
class_name = self.__class__.__name__[0].lower() + self.__class__.__name__[1:]
|
|
95
|
+
return {class_name: json.loads(super().json(*args, **kwargs, by_alias=True, exclude_none=True))}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains the models for the following Google Sheets API requests:
|
|
3
|
+
|
|
4
|
+
- InsertDimension
|
|
5
|
+
- UpdateDimensionProperties
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from enum import StrEnum
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel, Field, model_validator
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Dimension(StrEnum):
|
|
15
|
+
ROWS = 'ROWS'
|
|
16
|
+
COLUMNS = 'COLUMNS'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DimensionRange(BaseModel):
|
|
20
|
+
sheet_id: int = Field(..., serialization_alias='sheetId', ge=0)
|
|
21
|
+
dimension: Dimension
|
|
22
|
+
start_index: int = Field(..., serialization_alias='startIndex', ge=0)
|
|
23
|
+
end_index: int = Field(..., serialization_alias='endIndex', gt=0)
|
|
24
|
+
|
|
25
|
+
@model_validator(mode='after')
|
|
26
|
+
def check_indexes(self):
|
|
27
|
+
if self.start_index >= self.end_index:
|
|
28
|
+
raise ValueError(f'start_index ({self.start_index}) must be less than end_index ({self.end_index})')
|
|
29
|
+
return self
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class DimensionProperties(BaseModel):
|
|
33
|
+
hidden_by_user: bool = Field(None, alias='hiddenByUser')
|
|
34
|
+
pixel_size: int = Field(None, alias='pixelSize')
|
|
35
|
+
developer_metadata: dict = Field(None, alias='developerMetadata')
|
|
36
|
+
data_source_column_reference: dict = Field(None, alias='dataSourceColumnReference')
|
|
37
|
+
|
|
38
|
+
# Read-only
|
|
39
|
+
hidden_by_filter: bool = Field(None, alias='hiddenByFilter')
|
|
40
|
+
|
|
41
|
+
class Config:
|
|
42
|
+
populate_by_name = True
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class InsertDimension(BaseModel):
|
|
46
|
+
range: DimensionRange
|
|
47
|
+
inherit_from_before: bool = Field(..., serialization_alias='inheritFromBefore')
|
|
48
|
+
|
|
49
|
+
def dict(self, *args, **kwargs):
|
|
50
|
+
class_name = self.__class__.__name__[0].lower() + self.__class__.__name__[1:]
|
|
51
|
+
return {class_name: json.loads(super().json(*args, **kwargs, by_alias=True, exclude_none=True))}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class UpdateDimensionProperties(BaseModel):
|
|
55
|
+
range: DimensionRange
|
|
56
|
+
properties: DimensionProperties
|
|
57
|
+
fields: str
|
|
58
|
+
|
|
59
|
+
def dict(self, *args, **kwargs):
|
|
60
|
+
class_name = self.__class__.__name__[0].lower() + self.__class__.__name__[1:]
|
|
61
|
+
return {class_name: json.loads(super().json(*args, **kwargs, by_alias=True, exclude_none=True))}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class AddDimensionGroup(BaseModel):
|
|
65
|
+
range: DimensionRange
|
|
66
|
+
|
|
67
|
+
def dict(self, *args, **kwargs):
|
|
68
|
+
class_name = self.__class__.__name__[0].lower() + self.__class__.__name__[1:]
|
|
69
|
+
return {class_name: json.loads(super().json(*args, **kwargs, by_alias=True, exclude_none=True))}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class DeleteDimensionGroup(BaseModel):
|
|
73
|
+
range: DimensionRange
|
|
74
|
+
|
|
75
|
+
def dict(self, *args, **kwargs):
|
|
76
|
+
class_name = self.__class__.__name__[0].lower() + self.__class__.__name__[1:]
|
|
77
|
+
return {class_name: json.loads(super().json(*args, **kwargs, by_alias=True, exclude_none=True))}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field, model_validator
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ThemeColorType(StrEnum):
|
|
7
|
+
TEXT = 'TEXT'
|
|
8
|
+
BACKGROUND = 'BACKGROUND'
|
|
9
|
+
ACCENT1 = 'ACCENT1'
|
|
10
|
+
ACCENT2 = 'ACCENT2'
|
|
11
|
+
ACCENT3 = 'ACCENT3'
|
|
12
|
+
ACCENT4 = 'ACCENT4'
|
|
13
|
+
ACCENT5 = 'ACCENT5'
|
|
14
|
+
ACCENT6 = 'ACCENT6'
|
|
15
|
+
LINK = 'LINK'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Color(BaseModel):
|
|
19
|
+
red: float = Field(1, ge=0, le=1)
|
|
20
|
+
green: float = Field(1, ge=0, le=1)
|
|
21
|
+
blue: float = Field(1, ge=0, le=1)
|
|
22
|
+
alpha: float = Field(1, ge=0, le=1)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ColorStyle(BaseModel):
|
|
26
|
+
rgb_color: Color = Field(..., alias='rgbColor')
|
|
27
|
+
theme_color: ThemeColorType = Field(None, alias='themeColor')
|
|
28
|
+
|
|
29
|
+
class Config:
|
|
30
|
+
populate_by_name = True
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class GridRange(BaseModel):
|
|
34
|
+
"""
|
|
35
|
+
A range on a sheet. All indexes are zero-based. Indexes are half open, i.e. the start index is inclusive and the
|
|
36
|
+
end index is exclusive -- [startIndex, endIndex). Missing indexes indicate the range is unbounded on that side.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
sheet_id: int = Field(None, alias='sheetId', ge=0)
|
|
40
|
+
start_row_index: int = Field(..., alias='startRowIndex', ge=0)
|
|
41
|
+
end_row_index: int = Field(..., alias='endRowIndex', gt=0)
|
|
42
|
+
start_column_index: int = Field(..., alias='startColumnIndex', ge=0)
|
|
43
|
+
end_column_index: int = Field(..., alias='endColumnIndex', gt=0)
|
|
44
|
+
|
|
45
|
+
@model_validator(mode='after')
|
|
46
|
+
def check_indexes(self):
|
|
47
|
+
if self.start_row_index >= self.end_row_index:
|
|
48
|
+
raise ValueError(f'start_row_index ({self.start_row_index}) must be less than end_row_index ({self.end_row_index})')
|
|
49
|
+
if self.start_column_index >= self.end_column_index:
|
|
50
|
+
raise ValueError(f'start_column_index ({self.start_column_index}) must be less than end_column_index ({self.end_column_index})')
|
|
51
|
+
return self
|
|
52
|
+
|
|
53
|
+
class Config:
|
|
54
|
+
populate_by_name = True
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class FieldMask:
|
|
58
|
+
TITLE = 'title'
|
|
59
|
+
PIXEL_SIZE = 'pixelSize'
|
|
60
|
+
|
|
61
|
+
class GridProperties:
|
|
62
|
+
ALL = 'gridProperties'
|
|
63
|
+
ROW_COUNT = 'gridProperties.rowCount'
|
|
64
|
+
COLUMN_COUNT = 'gridProperties.columnCount'
|
|
65
|
+
FROZEN_ROW_COUNT = 'gridProperties.frozenRowCount'
|
|
66
|
+
FROZEN_COLUMN_COUNT = 'gridProperties.frozenColumnCount'
|
|
67
|
+
HIDE_GRID_LINES = 'gridProperties.hideGridlines'
|
|
68
|
+
ROW_GROUP_CONTROL_AFTER = 'gridProperties.rowGroupControlAfter'
|
|
69
|
+
COLUMN_GROUP_CONTROL_AFTER = 'gridProperties.columnGroupControlAfter'
|
|
70
|
+
|
|
71
|
+
class CellData:
|
|
72
|
+
USER_ENTERED_VALUE = 'userEnteredValue'
|
|
73
|
+
USER_ENTERED_FORMAT = 'userEnteredFormat'
|
|
74
|
+
|
|
75
|
+
class Spreadsheet:
|
|
76
|
+
ID = 'spreadsheetId'
|
|
77
|
+
URL = 'spreadsheetUrl'
|
|
78
|
+
PROPERTIES = 'properties'
|
|
79
|
+
SHEETS = 'sheets'
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains the models for the following Google Sheets API requests:
|
|
3
|
+
|
|
4
|
+
- MergeCells
|
|
5
|
+
- UnmergeCells
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from enum import StrEnum
|
|
10
|
+
from pydantic import BaseModel, Field
|
|
11
|
+
|
|
12
|
+
from .general_models import GridRange
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MergeType(StrEnum):
|
|
16
|
+
MERGE_ALL = 'MERGE_ALL'
|
|
17
|
+
MERGE_COLUMNS = 'MERGE_COLUMNS'
|
|
18
|
+
MERGE_ROWS = 'MERGE_ROWS'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MergeCells(BaseModel):
|
|
22
|
+
range: GridRange
|
|
23
|
+
merge_type: MergeType = Field(..., serialization_alias='mergeType')
|
|
24
|
+
|
|
25
|
+
def dict(self, *args, **kwargs):
|
|
26
|
+
class_name = self.__class__.__name__[0].lower() + self.__class__.__name__[1:]
|
|
27
|
+
return {class_name: json.loads(super().json(*args, **kwargs, by_alias=True, exclude_none=True))}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class UnmergeCells(BaseModel):
|
|
31
|
+
range: GridRange
|
|
32
|
+
|
|
33
|
+
def dict(self, *args, **kwargs):
|
|
34
|
+
class_name = self.__class__.__name__[0].lower() + self.__class__.__name__[1:]
|
|
35
|
+
return {class_name: json.loads(super().json(*args, **kwargs, by_alias=True, exclude_none=True))}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from enum import StrEnum
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
|
|
6
|
+
from .update_sheet_properties import SheetProperties
|
|
7
|
+
from .update_cells import RowData
|
|
8
|
+
from .dimension import DimensionProperties
|
|
9
|
+
from .conditional_format_rule import ConditionalFormatRule
|
|
10
|
+
from .general_models import GridRange
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RecalculationInterval(StrEnum):
|
|
14
|
+
ON_CHANGE = 'ON_CHANGE'
|
|
15
|
+
MINUTE = 'MINUTE'
|
|
16
|
+
HOUR = 'HOUR'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SpreadsheetProperties(BaseModel):
|
|
20
|
+
title: str
|
|
21
|
+
locale: str = 'ru_RU'
|
|
22
|
+
auto_recalc: RecalculationInterval = Field(None, alias='autoRecalc')
|
|
23
|
+
time_zone: str = Field('Europe/Moscow', alias='timeZone')
|
|
24
|
+
iterative_calculation_settings: dict = Field(None, alias='iterativeCalculationSettings')
|
|
25
|
+
spreadsheet_theme: dict = Field(None, alias='spreadsheetTheme')
|
|
26
|
+
import_functions_external_url_access_allowed: bool = Field(None, alias='importFunctionsExternalUrlAccessAllowed')
|
|
27
|
+
|
|
28
|
+
# Read-only
|
|
29
|
+
default_format: dict = Field(None, alias='defaultFormat')
|
|
30
|
+
|
|
31
|
+
class Config:
|
|
32
|
+
populate_by_name = True
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class GridData(BaseModel):
|
|
36
|
+
start_row: int = Field(None, alias='startRow')
|
|
37
|
+
start_column: int = Field(None, alias='startColumn')
|
|
38
|
+
row_data: list[RowData] = Field(None, alias='rowData')
|
|
39
|
+
row_metadata: list[DimensionProperties] = Field(None, alias='rowMetadata')
|
|
40
|
+
column_metadata: list[DimensionProperties] = Field(None, alias='columnMetadata')
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Sheet(BaseModel):
|
|
44
|
+
properties: SheetProperties = None
|
|
45
|
+
data: list[GridData] = None
|
|
46
|
+
merges: list[GridRange] = None
|
|
47
|
+
conditional_formats: list[ConditionalFormatRule] = Field(None, alias='conditionalFormats')
|
|
48
|
+
filter_views: list[dict] = Field(None, alias='filterViews')
|
|
49
|
+
protected_ranges: list[dict] = Field(None, alias='protectedRanges')
|
|
50
|
+
basic_filter: dict = Field(None, alias='basicFilter')
|
|
51
|
+
charts: list[dict] = None
|
|
52
|
+
banded_ranges: list[dict] = Field(None, alias='bandedRanges')
|
|
53
|
+
developer_metadata: list[dict] = Field(None, alias='developerMetadata')
|
|
54
|
+
row_groups: list[dict] = Field(None, alias='rowGroups')
|
|
55
|
+
column_groups: list[dict] = Field(None, alias='columnGroups')
|
|
56
|
+
slicers: list[dict] = None
|
|
57
|
+
|
|
58
|
+
class Config:
|
|
59
|
+
populate_by_name = True
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class Spreadsheet(BaseModel):
|
|
63
|
+
properties: SpreadsheetProperties = None
|
|
64
|
+
sheets: list[Sheet] = None
|
|
65
|
+
named_ranges: list[dict] = Field(None, alias='namedRanges')
|
|
66
|
+
developer_metadata: list[dict] = Field(None, alias='developerMetadata')
|
|
67
|
+
data_sources: list[dict] = Field(None, alias='dataSources')
|
|
68
|
+
|
|
69
|
+
# Read-only
|
|
70
|
+
spreadsheet_id: str = Field(None, alias='spreadsheetId')
|
|
71
|
+
spreadsheet_url: str = Field(None, alias='spreadsheetUrl')
|
|
72
|
+
data_source_schedules: list[dict] = Field(None, alias='dataSourceSchedules')
|
|
73
|
+
|
|
74
|
+
class Config:
|
|
75
|
+
populate_by_name = True
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class DeleteSheet(BaseModel):
|
|
79
|
+
sheet_id: int = Field(..., serialization_alias='sheetId')
|
|
80
|
+
|
|
81
|
+
def dict(self, *args, **kwargs):
|
|
82
|
+
class_name = self.__class__.__name__[0].lower() + self.__class__.__name__[1:]
|
|
83
|
+
return {class_name: json.loads(super().json(*args, **kwargs, by_alias=True, exclude_none=True))}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class AddSheet(BaseModel):
|
|
87
|
+
properties: SheetProperties = None
|
|
88
|
+
|
|
89
|
+
def dict(self, *args, **kwargs):
|
|
90
|
+
class_name = self.__class__.__name__[0].lower() + self.__class__.__name__[1:]
|
|
91
|
+
return {class_name: json.loads(super().json(*args, **kwargs, by_alias=True, exclude_none=True))}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains models for the following Google Sheets API requests:
|
|
3
|
+
- UpdateCells
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from enum import StrEnum
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel, Field, model_validator
|
|
13
|
+
|
|
14
|
+
from .general_models import GridRange, ColorStyle
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class NumberFormatType(StrEnum):
|
|
18
|
+
TEXT = 'TEXT'
|
|
19
|
+
NUMBER = 'NUMBER'
|
|
20
|
+
PERCENT = 'PERCENT'
|
|
21
|
+
CURRENCY = 'CURRENCY'
|
|
22
|
+
DATE = 'DATE'
|
|
23
|
+
TIME = 'TIME'
|
|
24
|
+
DATE_TIME = 'DATE_TIME'
|
|
25
|
+
SCIENTIFIC = 'SCIENTIFIC'
|
|
26
|
+
|
|
27
|
+
class NumberFormat(BaseModel):
|
|
28
|
+
type_: NumberFormatType = Field(..., alias='type')
|
|
29
|
+
pattern: str = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Style(StrEnum):
|
|
33
|
+
DOTTED = 'DOTTED'
|
|
34
|
+
DASHED = 'DASHED'
|
|
35
|
+
SOLID = THIN = 'SOLID'
|
|
36
|
+
SOLID_MEDIUM = MEDIUM = 'SOLID_MEDIUM'
|
|
37
|
+
SOLID_THICK = THICK = 'SOLID_THICK'
|
|
38
|
+
NONE = 'NONE'
|
|
39
|
+
DOUBLE = 'DOUBLE'
|
|
40
|
+
|
|
41
|
+
class Border(BaseModel):
|
|
42
|
+
style: Style
|
|
43
|
+
color_style: ColorStyle = Field(None, serialization_alias='colorStyle')
|
|
44
|
+
|
|
45
|
+
class Borders(BaseModel):
|
|
46
|
+
top: Optional[Border] = None
|
|
47
|
+
bottom: Optional[Border] = None
|
|
48
|
+
left: Optional[Border] = None
|
|
49
|
+
right: Optional[Border] = None
|
|
50
|
+
|
|
51
|
+
@model_validator(mode='before')
|
|
52
|
+
def check_exclusive_fields(cls, values):
|
|
53
|
+
filled_fields = [key for key, value in values.items() if value is not None]
|
|
54
|
+
if len(filled_fields) == 0:
|
|
55
|
+
raise ValueError('at least one border must be set')
|
|
56
|
+
return values
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class Padding(BaseModel):
|
|
60
|
+
top: Optional[int]
|
|
61
|
+
right: Optional[int]
|
|
62
|
+
bottom: Optional[int]
|
|
63
|
+
left: Optional[int]
|
|
64
|
+
|
|
65
|
+
@model_validator(mode='before')
|
|
66
|
+
def check_exclusive_fields(cls, values):
|
|
67
|
+
filled_fields = [key for key, value in values.items() if value is not None]
|
|
68
|
+
if len(filled_fields) == 0:
|
|
69
|
+
raise ValueError('at least one padding must be set')
|
|
70
|
+
return values
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class HorizontalAlignment(StrEnum):
|
|
74
|
+
LEFT = 'LEFT'
|
|
75
|
+
CENTER = 'CENTER'
|
|
76
|
+
RIGHT = 'RIGHT'
|
|
77
|
+
|
|
78
|
+
class VerticalAlignment(StrEnum):
|
|
79
|
+
TOP = 'TOP'
|
|
80
|
+
MIDDLE = 'MIDDLE'
|
|
81
|
+
BOTTOM = 'BOTTOM'
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class WrapStrategy(StrEnum):
|
|
85
|
+
OVERFLOW_CELL = 'OVERFLOW_CELL'
|
|
86
|
+
# LEGACY_WRAP = 'LEGACY_WRAP' # Not supported on all platforms
|
|
87
|
+
CLIP = 'CLIP'
|
|
88
|
+
WRAP = 'WRAP'
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class TextDirection(StrEnum):
|
|
92
|
+
LEFT_TO_RIGHT = 'LEFT_TO_RIGHT'
|
|
93
|
+
RIGHT_TO_LEFT = 'RIGHT_TO_LEFT'
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class Link:
|
|
97
|
+
uri: str = None
|
|
98
|
+
|
|
99
|
+
class TextFormat(BaseModel):
|
|
100
|
+
foreground_color_style: Optional[ColorStyle] = Field(None, serialization_alias='foregroundColorStyle')
|
|
101
|
+
font_family: Optional[str] = Field(None, serialization_alias='fontFamily')
|
|
102
|
+
font_size: Optional[int] = Field(None, serialization_alias='fontSize')
|
|
103
|
+
bold: Optional[bool] = None
|
|
104
|
+
italic: Optional[bool] = None
|
|
105
|
+
strikethrough: Optional[bool] = None
|
|
106
|
+
underline: Optional[bool] = None
|
|
107
|
+
link: Optional[Link] = None
|
|
108
|
+
|
|
109
|
+
class Config:
|
|
110
|
+
arbitrary_types_allowed = True
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class HyperlinkDisplayType(StrEnum):
|
|
114
|
+
LINKED = 'LINKED'
|
|
115
|
+
PLAIN_TEXT = 'PLAIN_TEXT'
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class TextRotation(BaseModel):
|
|
119
|
+
angle: int = Field(None, ge=-90, le=90) # Angle between the standard orientation and the desired orientation, direction is counterclockwise
|
|
120
|
+
vertical: bool = None # Text reads top to bottom, but the orientation of individual characters is unchanged
|
|
121
|
+
|
|
122
|
+
@model_validator(mode='before')
|
|
123
|
+
def check_exclusive_fields(cls, values):
|
|
124
|
+
filled_fields = [key for key, value in values.items() if value is not None]
|
|
125
|
+
if len(filled_fields) != 1:
|
|
126
|
+
raise ValueError(f'only one field must be set, but got {filled_fields}')
|
|
127
|
+
return values
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class CellFormat(BaseModel):
|
|
131
|
+
number_format: Optional[NumberFormat] = Field(None, alias='numberFormat')
|
|
132
|
+
background_color_style: Optional[ColorStyle] = Field(None, alias='backgroundColorStyle')
|
|
133
|
+
borders: Optional[Borders] = None
|
|
134
|
+
padding: Optional[Padding] = None
|
|
135
|
+
horizontal_alignment: Optional[HorizontalAlignment] = Field(None, alias='horizontalAlignment')
|
|
136
|
+
vertical_alignment: Optional[VerticalAlignment] = Field(None, alias='verticalAlignment')
|
|
137
|
+
wrap_strategy: Optional[WrapStrategy] = Field(None, alias='wrapStrategy')
|
|
138
|
+
text_direction: Optional[TextDirection] = Field(None, alias='textDirection')
|
|
139
|
+
text_format: Optional[TextFormat] = Field(None, alias='textFormat')
|
|
140
|
+
hyperlink_display_type: Optional[HyperlinkDisplayType] = Field(None, alias='hyperlinkDisplayType')
|
|
141
|
+
text_rotation: Optional[TextRotation] = Field(None, alias='textRotation')
|
|
142
|
+
|
|
143
|
+
def __add__(self, other) -> CellFormat:
|
|
144
|
+
number_format = other.number_format or self.number_format
|
|
145
|
+
if other.number_format and self.number_format:
|
|
146
|
+
number_format = NumberFormat(
|
|
147
|
+
type=other.number_format.type_ or self.number_format.type_,
|
|
148
|
+
pattern=other.number_format.pattern or self.number_format.pattern,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
borders = other.borders or self.borders
|
|
152
|
+
if other.borders and self.borders:
|
|
153
|
+
borders = Borders(
|
|
154
|
+
top=other.borders.top or self.borders.top,
|
|
155
|
+
bottom=other.borders.bottom or self.borders.bottom,
|
|
156
|
+
left=other.borders.left or self.borders.left,
|
|
157
|
+
right=other.borders.right or self.borders.right,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
text_format = other.text_format or self.text_format
|
|
161
|
+
if other.text_format and self.text_format:
|
|
162
|
+
text_format = TextFormat(
|
|
163
|
+
foreground_color_style=other.text_format.foreground_color_style or self.text_format.foreground_color_style,
|
|
164
|
+
font_family=other.text_format.font_family or self.text_format.font_family,
|
|
165
|
+
font_size=other.text_format.font_size or self.text_format.font_size,
|
|
166
|
+
bold=other.text_format.bold or self.text_format.bold,
|
|
167
|
+
italic=other.text_format.italic or self.text_format.italic,
|
|
168
|
+
strikethrough=other.text_format.strikethrough or self.text_format.strikethrough,
|
|
169
|
+
underline=other.text_format.underline or self.text_format.underline,
|
|
170
|
+
link=other.text_format.link or self.text_format.link,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
return CellFormat(
|
|
174
|
+
number_format=number_format,
|
|
175
|
+
background_color_style=other.background_color_style or self.background_color_style,
|
|
176
|
+
borders=borders,
|
|
177
|
+
padding=other.padding or self.padding,
|
|
178
|
+
horizontal_alignment=other.horizontal_alignment or self.horizontal_alignment,
|
|
179
|
+
vertical_alignment=other.vertical_alignment or self.vertical_alignment,
|
|
180
|
+
wrap_strategy=other.wrap_strategy or self.wrap_strategy,
|
|
181
|
+
text_direction=other.text_direction or self.text_direction,
|
|
182
|
+
text_format=text_format,
|
|
183
|
+
hyperlink_display_type=other.hyperlink_display_type or self.hyperlink_display_type,
|
|
184
|
+
text_rotation=other.text_rotation or self.text_rotation,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
class Config:
|
|
188
|
+
populate_by_name = True
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class ExtendedValue(BaseModel):
|
|
192
|
+
number_value: Optional[float] = Field(None, alias='numberValue')
|
|
193
|
+
string_value: Optional[str] = Field(None, alias='stringValue')
|
|
194
|
+
bool_value: Optional[bool] = Field(None, alias='boolValue')
|
|
195
|
+
formula_value: Optional[str] = Field(None, alias='formulaValue')
|
|
196
|
+
|
|
197
|
+
# Read-only
|
|
198
|
+
error_value: dict = Field(None, alias='errorValue')
|
|
199
|
+
|
|
200
|
+
@model_validator(mode='before')
|
|
201
|
+
def check_exclusive_fields(cls, values):
|
|
202
|
+
filled_fields = [key for key, value in values.items() if value is not None]
|
|
203
|
+
if len(filled_fields) != 1:
|
|
204
|
+
raise ValueError(f'only one field must be set, but got {filled_fields}')
|
|
205
|
+
return values
|
|
206
|
+
|
|
207
|
+
class Config:
|
|
208
|
+
populate_by_name = True
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class CellData(BaseModel):
|
|
212
|
+
user_entered_value: Optional[ExtendedValue] = Field(None, alias='userEnteredValue')
|
|
213
|
+
user_entered_format: Optional[CellFormat] = Field(None, alias='userEnteredFormat')
|
|
214
|
+
note: str = None
|
|
215
|
+
text_format_runs: list[dict] = Field(None, alias='textFormatRuns')
|
|
216
|
+
data_validation: dict = Field(None, alias='dataValidation')
|
|
217
|
+
pivot_table: dict = Field(None, alias='pivotTable')
|
|
218
|
+
data_source_table: dict = Field(None, alias='dataSourceTable')
|
|
219
|
+
data_source_formula: dict = Field(None, alias='dataSourceFormula')
|
|
220
|
+
|
|
221
|
+
# Read-only
|
|
222
|
+
effective_value: ExtendedValue = Field(None, alias='effectiveValue')
|
|
223
|
+
formatted_value: str = Field(None, alias='formattedValue')
|
|
224
|
+
effective_format: CellFormat = Field(None, alias='effectiveFormat')
|
|
225
|
+
hyperlink: str = None
|
|
226
|
+
|
|
227
|
+
class Config:
|
|
228
|
+
populate_by_name = True
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class RowData(BaseModel):
|
|
232
|
+
values: list[CellData]
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class UpdateCells(BaseModel):
|
|
236
|
+
range: GridRange
|
|
237
|
+
rows: list[RowData] = []
|
|
238
|
+
fields: str
|
|
239
|
+
|
|
240
|
+
def dict(self, *args, **kwargs):
|
|
241
|
+
class_name = self.__class__.__name__[0].lower() + self.__class__.__name__[1:]
|
|
242
|
+
return {class_name: json.loads(super().json(*args, **kwargs, by_alias=True, exclude_none=True))}
|