• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

andy-goellner / pco-python-sdk / 14284405228

05 Apr 2025 06:00PM UTC coverage: 92.701% (-1.7%) from 94.432%
14284405228

Pull #13

github

web-flow
Merge 220dc311c into e9345420a
Pull Request #13: patch: Bug bash, fixing random bugs

105 of 122 new or added lines in 20 files covered. (86.07%)

508 of 548 relevant lines covered (92.7%)

2.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.74
/src/planning_center_python/models/pco_object.py
1
from collections.abc import Mapping
3✔
2
from typing import Any, ClassVar, Optional, cast
3✔
3

4
from planning_center_python.errors import (
3✔
5
    InvalidParamsError,
6
    InvalidRequestError,
7
    NoAttributesDefinedError,
8
)
9
from planning_center_python.types.abstract_pco_object import AbstractPCOObject
3✔
10
from planning_center_python.types.inclusions import Inclusion, InclusionDefinition
3✔
11
from planning_center_python.types.relationships import (
3✔
12
    Relationship,
13
    RelationshipDefinition,
14
)
15

16
# TODO
17
# 1. Determine if relationships get the method name, or we prepend it
18
# 2. add support for many to one relationships and inclusions
19
# 3. ensure we add support for many to one in fetchers
20
# 4. tests for included_keys
21
# 5. remove print statements and move to logging
22

23

24
class PCOObject(AbstractPCOObject):
3✔
25
    """Base class object from which all Planning Center objects should inherit from"""
26

27
    OBJECT_TYPE: ClassVar[str] = "BaseClass"
3✔
28
    OBJECT_URL: ClassVar[str] = ""
3✔
29
    RELATIONSHIPS: ClassVar[list[RelationshipDefinition]] = []
3✔
30
    INCLUSION_DEFINITIONS: ClassVar[list[InclusionDefinition]] = []
3✔
31

32
    def __call__(
3✔
33
        self,
34
        data: Mapping[str, Any] = {},
35
        id: Optional[str] = None,
36
        included_data: Optional[list[Mapping[str, Any]]] = [],
37
    ) -> Any:
NEW
38
        self.__init__(data=data, id=id, included_data=included_data)
×
39

40
    def __init__(
3✔
41
        self,
42
        data: Mapping[str, Any] = {},
43
        id: Optional[str] = None,
44
        included_data: Optional[list[Mapping[str, Any]]] = [],
45
    ):
46
        print(f"INTITIALIZING OBJECT {self.OBJECT_TYPE}")
3✔
47
        self._data = data
3✔
48
        self._included_data = included_data
3✔
49
        self._id = id or data.get("id")
3✔
50
        self._type = data.get("type") or self.OBJECT_TYPE
3✔
51
        self._validate()
3✔
52
        self.attributes = data.get("attributes")
3✔
53
        self.relationships = self._init_relationships(
3✔
54
            cast(Mapping[str, Any], data.get("relationships"))
55
        )
56
        print(f"{self.OBJECT_TYPE} included_data {included_data}")
3✔
57
        self.included = self._init_inclusions(included_data)
3✔
58

59
    def get_attribute(self, name: str) -> Any:
3✔
60
        if not self.attributes:
3✔
61
            raise NoAttributesDefinedError
3✔
62
        return self.attributes.get(name)
3✔
63

64
    def get_relationship(self, key: str) -> AbstractPCOObject | None:
3✔
65
        return next(
3✔
66
            (r.class_instance for r in self.relationships if r.key == key),
67
            None,
68
        )
69

70
    def get_inclusion(self, key: str) -> AbstractPCOObject | None:
3✔
71
        return next(
3✔
72
            (r.class_instance for r in self.included if r.key == key),
73
            None,
74
        )
75

76
    def _instance_url(self) -> str:
3✔
77
        if not self.id:
3✔
78
            raise InvalidRequestError(
×
79
                "Cannot determine instance url without a valid id"
80
            )
81
        return "%s/%s" % (self._object_url(), self.id)
3✔
82

83
    def _validate(self):
3✔
84
        if not self.id:
3✔
85
            raise InvalidParamsError(self, "id")
3✔
86
        if not self.type:
3✔
87
            raise InvalidParamsError(self, "type")
×
88
        if self.type != self.OBJECT_TYPE:
3✔
89
            raise InvalidParamsError(self, "type", "Class types do not match")
3✔
90

91
    def _init_relationships(
3✔
92
        self, object_data: Mapping[str, Any] | None
93
    ) -> list[Relationship]:
94
        built_relationships: list[Relationship] = []
3✔
95
        if object_data:
3✔
96
            for definition in self.RELATIONSHIPS:
3✔
97
                relation = object_data.get(definition["key"])
3✔
98
                if relation and relation["data"]:
3✔
99
                    relation_id = relation["data"]["id"]
3✔
100
                    klass_instance = definition["klass"](id=relation_id)
3✔
101
                    built_relationships.append(
3✔
102
                        Relationship(definition["key"], klass_instance)
103
                    )
104
                    self.__setattr__(
3✔
105
                        definition["method"], klass_instance
106
                    )  # TODO see note at top
107
        return built_relationships
3✔
108

109
    def _init_inclusions(
3✔
110
        self, included_data: list[Mapping[str, Any]] | None
111
    ) -> list[Inclusion]:
112
        built_inclusions: list[Inclusion] = []
3✔
113
        if included_data:
3✔
114
            for inclusion in included_data:
3✔
115
                definition = self._find_definition_from_type(
3✔
116
                    self.INCLUSION_DEFINITIONS, inclusion["type"]
117
                )
118
                if definition:
3✔
119
                    klass_instance = definition["klass"](data=inclusion)
3✔
120
                    built_inclusions.append(
3✔
121
                        Inclusion(definition["key"], klass_instance)
122
                    )
123
                    self.__setattr__(
3✔
124
                        definition["method"], klass_instance
125
                    )  # TODO see note at top
126

127
        return built_inclusions
3✔
128

129
    @staticmethod
3✔
130
    def _find_definition_from_type(
3✔
131
        definitions: list[RelationshipDefinition | InclusionDefinition], def_type: str
132
    ) -> InclusionDefinition | RelationshipDefinition | None:
133
        return next((dfn for dfn in definitions if dfn["type"] == def_type), None)
3✔
134

135
    @property
3✔
136
    def id(self) -> str | None:
3✔
137
        return self._id
3✔
138

139
    @property
3✔
140
    def type(self) -> str | None:
3✔
141
        return self._type
3✔
142

143
    @property
3✔
144
    def included_keys(self):
3✔
NEW
145
        return set([inc.key for inc in self.included])
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc