Coverage for src\airtable_to_sqlite\schema.py: 100%
76 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-05 17:44 +0100
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-05 17:44 +0100
1import logging
2from copy import copy
3from dataclasses import dataclass
4from typing import Any, Generator, Optional
6from pyairtable.api.base import Base as AirtableBase
8from airtable_to_sqlite.constants import (
9 NUMBER_FIELD_TYPES,
10 OPTION_FIELDS,
11 PreferedNamingMethod,
12)
14logger = logging.getLogger(__name__)
17@dataclass
18class BaseRecord:
19 id: str # noqa: A003
20 name: str
21 permissionLevel: str # noqa: N815
24@dataclass
25class FieldSchema:
26 id: str # noqa: A003
27 name: str
28 type: str # noqa: A003
29 options: Optional[dict[str, Any]] = None
31 def db_name(self, prefers_ids: PreferedNamingMethod = PreferedNamingMethod.NAME) -> str:
32 if prefers_ids == PreferedNamingMethod.ID:
33 return self.id
34 return self.name
36 @property
37 def column_type(self) -> Any:
38 field_type = self.type
39 if field_type == "formula":
40 if self.options is not None:
41 field_type = self.options.get("result", {}).get("type", field_type)
43 if field_type == "multipleRecordLinks":
44 return None
45 if field_type in NUMBER_FIELD_TYPES:
46 return float
47 if field_type in ("checkbox"):
48 return bool
49 return str
51 @property
52 def choices(self) -> Optional[list[dict[str, Any]]]:
53 options = self.options
54 if options is None:
55 return None
57 return [
58 {
59 "id": choice.get("id"),
60 "name": choice.get("name"),
61 "color": choice.get("color"),
62 "fieldId": self.id,
63 }
64 for choice in options.get("choices", [])
65 ]
67 def for_insertion(self, table: "TableSchema") -> dict[str, Any]:
68 options = copy(self.options)
69 field_to_insert: dict[str, Any] = {
70 "id": self.id,
71 "name": self.name,
72 "type": self.type,
73 "tableId": table.id,
74 }
75 if options is None:
76 for option_field in OPTION_FIELDS.keys():
77 field_to_insert[option_field] = None
78 else:
79 for option_field in OPTION_FIELDS.keys():
80 field_to_insert[option_field] = options.pop(option_field, None)
81 field_to_insert["options"] = options
82 return field_to_insert
85@dataclass
86class ViewSchema:
87 id: str # noqa: A003
88 name: str
89 type: str # noqa: A003
91 def db_name(self, prefers_ids: PreferedNamingMethod = PreferedNamingMethod.NAME) -> str:
92 if prefers_ids == PreferedNamingMethod.ID:
93 return self.id
94 return self.name
97@dataclass
98class TableSchema:
99 id: str # noqa: A003
100 name: str
101 primaryFieldId: str # noqa: N815
102 fields: list[FieldSchema]
103 views: list[ViewSchema]
105 def db_name(self, prefers_ids: PreferedNamingMethod = PreferedNamingMethod.NAME) -> str:
106 if prefers_ids == PreferedNamingMethod.ID:
107 return self.id
108 return self.name
110 def get_table_data(self, api: AirtableBase) -> Generator[dict[str, Any], None, None]:
111 logger.info(f"Fetching table data for {self.name} from Airtable...")
112 for page in api.iterate(self.name):
113 for record in page:
114 yield record