spond_classes
Introduction
Spond is a team/group-oriented events system.
The unofficial Python spond package gets data from
the Spond API and returns dict objects.
This unofficial Python spond-classes
package parses those dicts to Pydantic class instances.
Key classes
These are the classes intended for direct user instantiation:
1""" 2# Introduction 3 4[Spond](https://spond.com/welcome) is a team/group-oriented events system. 5 6The unofficial Python [`spond`](https://github.com/Olen/Spond/) package gets data from 7the Spond API and returns `dict` objects. 8 9This unofficial Python [`spond-classes`](https://github.com/elliot-100/Spond-classes) 10package parses those `dict`s to [Pydantic](https://docs.pydantic.dev/) class instances. 11 12# Key classes 13 14These are the classes intended for direct user instantiation: 15 16- `Event` via `Event.list_from_data()` 17- `Group` via `Group.list_from_data()` 18""" 19 20# Explicitly import classes and functions into the package namespace to define the API. 21 22from . import typing 23from .event import Event, Responses 24from .group import FieldDef, Group 25from .member import Member 26from .profile_ import Profile 27from .role import Role 28from .subgroup import Subgroup 29 30__all__ = [ 31 "Event", 32 "Responses", 33 "FieldDef", 34 "Group", 35 "Member", 36 "Profile", 37 "Role", 38 "Subgroup", 39 "typing", 40]
42class Event(BaseModel): 43 """Represents an event in the Spond system.""" 44 45 uid: str = Field(alias="id") 46 """`id` in Spond API; aliased as that's a Python built-in, and the Spond package 47 uses `uid`.""" 48 heading: str 49 """Same name in Spond API.""" 50 responses: Responses 51 """Same name in Spond API.""" 52 type: Literal["AVAILABILITY", "EVENT", "RECURRING"] 53 """Same name in Spond API. 54 55 'AVAILABILITY': availability request. 56 'EVENT': regular event. 57 'RECURRING': instance of recurring event.""" 58 created_time: datetime = Field(alias="createdTime") 59 """Derived from `createdTime` in Spond API.""" 60 end_time: datetime = Field(alias="endTimestamp") 61 """Datetime at which the `Event` ends. 62 Derived from `endTimestamp` in Spond API.""" 63 start_time: datetime = Field(alias="startTimestamp") 64 """Datetime at which the `Event` starts. 65 Derived from `startTimestamp` in Spond API.""" 66 67 # Optional in API data 68 cancelled: bool | None = Field(default=None) 69 """Same name in Spond API. Not always present. Use `Event.is_cancelled` instead to 70 always return a `bool`.""" 71 hidden: bool | None = Field(default=None) 72 """Same name in Spond API. Not always present. Use `Event.is_hidden` instead to 73 always return a `bool`.""" 74 invite_time: datetime | None = Field(alias="inviteTime", default=None) 75 """Derived from `inviteTime` in Spond API. 76 Not always present.""" 77 78 def __str__(self) -> str: 79 """Return simple human-readable description. 80 81 Includes only key fields in custom order. 82 """ 83 return ( 84 f"Event(uid='{self.uid}', " 85 f"heading='{self.heading}', " 86 f"start_time: {self.start_time}, " 87 "…)" 88 ) 89 90 @property 91 def url(self) -> str: 92 """Return the URL of the `Event`, for convenience.""" 93 return f"https://spond.com/client/sponds/{self.uid}/" 94 95 @property 96 def is_cancelled(self) -> bool: 97 """Return whether the `Event` is cancelled.""" 98 return getattr(self, "cancelled", False) 99 100 @property 101 def is_hidden(self) -> bool: 102 """Return whether the `Event` is hidden.""" 103 return getattr(self, "hidden", False) 104 105 @classmethod 106 def list_from_data(cls, data: Iterable[DictFromJSON]) -> list[Self]: 107 """Construct a list of `Event`s from the list returned by `Spond.get_events()`. 108 109 Parameters 110 ---------- 111 data 112 as returned by `spond.spond.Spond.get_events()`. 113 114 Returns 115 ------- 116 `list[Event]` 117 118 Raises 119 ------ 120 `TypeError` 121 if an item in `data` is not a `dict`. 122 """ 123 return [cls.from_dict(item) for item in data] 124 125 @classmethod 126 def from_dict(cls, dict_: DictFromJSON) -> Self: 127 """Construct an `Event`. 128 129 Parameters 130 ---------- 131 dict_ 132 as returned by `spond.spond.Spond.get_event()` 133 or from the list returned by `spond.spond.Spond.get_events()`. 134 135 Returns 136 ------- 137 `Event` 138 139 Raises 140 ------ 141 `TypeError` 142 if `dict_` is not a `dict`. 143 """ 144 _ensure_dict(dict_) 145 return cls(**dict_)
Represents an event in the Spond system.
id in Spond API; aliased as that's a Python built-in, and the Spond package
uses uid.
Same name in Spond API.
'AVAILABILITY': availability request. 'EVENT': regular event. 'RECURRING': instance of recurring event.
Datetime at which the Event ends.
Derived from endTimestamp in Spond API.
Datetime at which the Event starts.
Derived from startTimestamp in Spond API.
Same name in Spond API. Not always present. Use Event.is_cancelled instead to
always return a bool.
Derived from inviteTime in Spond API.
Not always present.
90 @property 91 def url(self) -> str: 92 """Return the URL of the `Event`, for convenience.""" 93 return f"https://spond.com/client/sponds/{self.uid}/"
Return the URL of the Event, for convenience.
95 @property 96 def is_cancelled(self) -> bool: 97 """Return whether the `Event` is cancelled.""" 98 return getattr(self, "cancelled", False)
Return whether the Event is cancelled.
105 @classmethod 106 def list_from_data(cls, data: Iterable[DictFromJSON]) -> list[Self]: 107 """Construct a list of `Event`s from the list returned by `Spond.get_events()`. 108 109 Parameters 110 ---------- 111 data 112 as returned by `spond.spond.Spond.get_events()`. 113 114 Returns 115 ------- 116 `list[Event]` 117 118 Raises 119 ------ 120 `TypeError` 121 if an item in `data` is not a `dict`. 122 """ 123 return [cls.from_dict(item) for item in data]
Construct a list of Events from the list returned by Spond.get_events().
Parameters
- data: as returned by
spond.spond.Spond.get_events().
Returns
list[Event]
Raises
TypeError: if an item indatais not adict.
125 @classmethod 126 def from_dict(cls, dict_: DictFromJSON) -> Self: 127 """Construct an `Event`. 128 129 Parameters 130 ---------- 131 dict_ 132 as returned by `spond.spond.Spond.get_event()` 133 or from the list returned by `spond.spond.Spond.get_events()`. 134 135 Returns 136 ------- 137 `Event` 138 139 Raises 140 ------ 141 `TypeError` 142 if `dict_` is not a `dict`. 143 """ 144 _ensure_dict(dict_) 145 return cls(**dict_)
21class Responses(BaseModel): 22 """Represents the responses to an `Event`.""" 23 24 # Lists which always exist in API data, but may be empty 25 accepted_uids: list[str] = Field(alias="acceptedIds") 26 """`acceptedIds` in Spond API. 27 May be empty.""" 28 declined_uids: list[str] = Field(alias="declinedIds") 29 """`declinedIds` in Spond API. 30 May be empty.""" 31 unanswered_uids: list[str] = Field(alias="unansweredIds") 32 """`unansweredIds` in Spond API. 33 May be empty.""" 34 waiting_list_uids: list[str] = Field(alias="waitinglistIds") 35 """`waitinglistIds` in Spond API. 36 May be empty.""" 37 unconfirmed_uids: list[str] = Field(alias="unconfirmedIds") 38 """`unconfirmedIds` in Spond API. 39 May be empty."""
Represents the responses to an Event.
20class FieldDef(BaseModel): 21 """Custom field definition.""" 22 23 uid: str = Field(alias="id") 24 """`id` in Spond API; aliased as that's a Python built-in, and the Spond package 25 uses `uid`.""" 26 name: str
Custom field definition.
id in Spond API; aliased as that's a Python built-in, and the Spond package
uses uid.
29class Group(BaseModel): 30 """Represents a group in the Spond system. 31 32 A `Group` has: 33 - zero, one or more `Member`s 34 - zero, one or more `Role`s 35 - zero, one or more `Subgroup`s 36 """ 37 38 uid: str = Field(alias="id") 39 """`id` in Spond API; aliased as that's a Python built-in, and the Spond package 40 uses `uid`.""" 41 name: str 42 """Same name in Spond API.""" 43 44 # Mutables which always exist in Spond API data, but may be empty 45 members: list[Member] 46 """`Member`s belonging to the `Group`. 47 Derived from `members` in Spond API. 48 May be empty.""" 49 roles: list[Role] 50 """`Role`s belonging to the `Group`. 51 Derived from `roles` in Spond API. 52 May be empty.""" 53 subgroups: list[Subgroup] = Field(alias="subGroups") 54 """`Subgroup`s belonging to the `Group`. 55 Derived from `subGroups` in Spond API. 56 May be empty.""" 57 field_defs: list[FieldDef] = Field(alias="fieldDefs") 58 """Custom field definitions. 59 Derived from `fieldDefs` in Spond API. 60 May be empty.""" 61 62 def __str__(self) -> str: 63 """Return simple human-readable description. 64 65 Includes only key fields in custom order. 66 """ 67 return f"Group(uid='{self.uid}', name='{self.name}', …)" 68 69 @classmethod 70 def list_from_data(cls, data: Iterable[DictFromJSON]) -> list[Self]: 71 """Construct a list of `Group`s from the list returned by `Spond.get_groups()`. 72 73 Parameters 74 ---------- 75 data 76 as returned by `spond.spond.Spond.get_groups()`. 77 78 Returns 79 ------- 80 `list[Group]` 81 82 Raises 83 ------ 84 `TypeError` 85 if an item in `data` is not a `dict`. 86 """ 87 return [cls.from_dict(item) for item in data] 88 89 @classmethod 90 def from_dict(cls, dict_: DictFromJSON) -> Self: 91 """Construct a `Group`. 92 93 Parameters 94 ---------- 95 dict_ 96 as returned by `spond.spond.Spond.get_group()` 97 or from the list returned by `spond.spond.Spond.get_groups()`. 98 99 Returns 100 ------- 101 `Group` 102 103 Raises 104 ------ 105 `TypeError` 106 if `dict_` is not a `dict`. 107 """ 108 _ensure_dict(dict_) 109 return cls(**dict_) 110 111 def member_by_uid(self, uid: str) -> Member: 112 """Return the `Member` with matching `uid`. 113 114 Parameters 115 ---------- 116 uid 117 118 Returns 119 ------- 120 `Member` 121 122 Raises 123 ------ 124 LookupError 125 If `uid` is not found. 126 """ 127 for member in self.members: 128 if member.uid == uid: 129 return member 130 err_msg = f"No Member found with id='{uid}'." 131 raise LookupError(err_msg) 132 133 def role_by_uid(self, uid: str) -> Role: 134 """Return the `Role` with matching `uid`. 135 136 Parameters 137 ---------- 138 uid 139 140 Returns 141 ------- 142 `Role` 143 144 Raises 145 ------ 146 LookupError 147 If `uid` is not found. 148 """ 149 for role in self.roles: 150 if role.uid == uid: 151 return role 152 err_msg = f"No Role found with id='{uid}'." 153 raise LookupError(err_msg) 154 155 def subgroup_by_uid(self, uid: str) -> Subgroup: 156 """Return the `Subgroup` with matching `uid`. 157 158 Parameters 159 ---------- 160 uid 161 162 Returns 163 ------- 164 `Subgroup` 165 166 Raises 167 ------ 168 LookupError 169 If `uid` is not found. 170 """ 171 for subgroup in self.subgroups: 172 if subgroup.uid == uid: 173 return subgroup 174 err_msg = f"No Subgroup found with id='{uid}'." 175 raise LookupError(err_msg) 176 177 def members_by_subgroup(self, subgroup: Subgroup) -> list[Member]: 178 """Return `Member`s in the `Subgroup`. 179 180 Parameters 181 ---------- 182 subgroup 183 `Subgroup` from which to return `Member`s. 184 185 Returns 186 ------- 187 list[`Member`] 188 189 Raises 190 ------ 191 TypeError 192 If `subgroup` is not a `Subgroup` instance. 193 """ 194 if not isinstance(subgroup, Subgroup): 195 err_msg = "`subgroup` must be a Subgroup." 196 raise TypeError(err_msg) 197 return [ 198 member for member in self.members if subgroup.uid in member.subgroup_uids 199 ] 200 201 def members_by_role(self, role: Role) -> list[Member]: 202 """Return `Member`s with the `Role`. 203 204 Parameters 205 ---------- 206 role 207 `Role` from which to return `Member`s. 208 209 Returns 210 ------- 211 list[`Member`] 212 213 Raises 214 ------ 215 TypeError 216 If `role` is not a `Role` instance. 217 """ 218 if not isinstance(role, Role): 219 err_msg = "`role` must be a Role." 220 raise TypeError(err_msg) 221 return [ 222 member 223 for member in self.members 224 if member.role_uids and role.uid in member.role_uids 225 ]
Represents a group in the Spond system.
A Group has:
id in Spond API; aliased as that's a Python built-in, and the Spond package
uses uid.
69 @classmethod 70 def list_from_data(cls, data: Iterable[DictFromJSON]) -> list[Self]: 71 """Construct a list of `Group`s from the list returned by `Spond.get_groups()`. 72 73 Parameters 74 ---------- 75 data 76 as returned by `spond.spond.Spond.get_groups()`. 77 78 Returns 79 ------- 80 `list[Group]` 81 82 Raises 83 ------ 84 `TypeError` 85 if an item in `data` is not a `dict`. 86 """ 87 return [cls.from_dict(item) for item in data]
Construct a list of Groups from the list returned by Spond.get_groups().
Parameters
- data: as returned by
spond.spond.Spond.get_groups().
Returns
list[Group]
Raises
TypeError: if an item indatais not adict.
89 @classmethod 90 def from_dict(cls, dict_: DictFromJSON) -> Self: 91 """Construct a `Group`. 92 93 Parameters 94 ---------- 95 dict_ 96 as returned by `spond.spond.Spond.get_group()` 97 or from the list returned by `spond.spond.Spond.get_groups()`. 98 99 Returns 100 ------- 101 `Group` 102 103 Raises 104 ------ 105 `TypeError` 106 if `dict_` is not a `dict`. 107 """ 108 _ensure_dict(dict_) 109 return cls(**dict_)
111 def member_by_uid(self, uid: str) -> Member: 112 """Return the `Member` with matching `uid`. 113 114 Parameters 115 ---------- 116 uid 117 118 Returns 119 ------- 120 `Member` 121 122 Raises 123 ------ 124 LookupError 125 If `uid` is not found. 126 """ 127 for member in self.members: 128 if member.uid == uid: 129 return member 130 err_msg = f"No Member found with id='{uid}'." 131 raise LookupError(err_msg)
133 def role_by_uid(self, uid: str) -> Role: 134 """Return the `Role` with matching `uid`. 135 136 Parameters 137 ---------- 138 uid 139 140 Returns 141 ------- 142 `Role` 143 144 Raises 145 ------ 146 LookupError 147 If `uid` is not found. 148 """ 149 for role in self.roles: 150 if role.uid == uid: 151 return role 152 err_msg = f"No Role found with id='{uid}'." 153 raise LookupError(err_msg)
155 def subgroup_by_uid(self, uid: str) -> Subgroup: 156 """Return the `Subgroup` with matching `uid`. 157 158 Parameters 159 ---------- 160 uid 161 162 Returns 163 ------- 164 `Subgroup` 165 166 Raises 167 ------ 168 LookupError 169 If `uid` is not found. 170 """ 171 for subgroup in self.subgroups: 172 if subgroup.uid == uid: 173 return subgroup 174 err_msg = f"No Subgroup found with id='{uid}'." 175 raise LookupError(err_msg)
177 def members_by_subgroup(self, subgroup: Subgroup) -> list[Member]: 178 """Return `Member`s in the `Subgroup`. 179 180 Parameters 181 ---------- 182 subgroup 183 `Subgroup` from which to return `Member`s. 184 185 Returns 186 ------- 187 list[`Member`] 188 189 Raises 190 ------ 191 TypeError 192 If `subgroup` is not a `Subgroup` instance. 193 """ 194 if not isinstance(subgroup, Subgroup): 195 err_msg = "`subgroup` must be a Subgroup." 196 raise TypeError(err_msg) 197 return [ 198 member for member in self.members if subgroup.uid in member.subgroup_uids 199 ]
201 def members_by_role(self, role: Role) -> list[Member]: 202 """Return `Member`s with the `Role`. 203 204 Parameters 205 ---------- 206 role 207 `Role` from which to return `Member`s. 208 209 Returns 210 ------- 211 list[`Member`] 212 213 Raises 214 ------ 215 TypeError 216 If `role` is not a `Role` instance. 217 """ 218 if not isinstance(role, Role): 219 err_msg = "`role` must be a Role." 220 raise TypeError(err_msg) 221 return [ 222 member 223 for member in self.members 224 if member.role_uids and role.uid in member.role_uids 225 ]
11class Member(BaseModel): 12 """Represents a member in the Spond system. 13 14 A `Member` is an individual's `Group`-specific record. 15 16 A `Member` may have a `Profile`. 17 """ 18 19 uid: str = Field(alias="id") 20 """`id` in Spond API; aliased as that's a Python built-in, and the Spond package 21 uses `uid`.""" 22 created_time: datetime = Field(alias="createdTime") 23 """Derived from `createdTime` in Spond API.""" 24 first_name: str = Field(alias="firstName") 25 """`firstName` in Spond API.""" 26 last_name: str = Field(alias="lastName") 27 """`lastName` in Spond API.""" 28 29 # Mutables which always exist in Spond API data, but may be empty 30 subgroup_uids: list[str] = Field(alias="subGroups") 31 """`subGroups` in Spond API; aliased to avoid confusion with `Subgroup` instances. 32 May be empty.""" 33 fields: dict[str, int | str] = Field(alias="fields") 34 """Custom fields. Same name in Spond API. 35 May be empty.""" 36 37 # Optional in Spond API data 38 email: EmailStr | None = Field(default=None) 39 """Same name in Spond API. Not always present.""" 40 phone_number: str | None = Field(alias="phoneNumber", default=None) 41 """`phoneNumber` in Spond API. 42 Not always present.""" 43 profile: Profile | None = None # Availability may depend on permissions 44 """Derived from `profile` in Spond API. 45 Not always present.""" 46 role_uids: list[str] | None = Field(alias="roles", default=None) 47 """`roles` in Spond API; aliased to avoid confusion with `Role` instances. 48 Not always present.""" 49 50 def __str__(self) -> str: 51 """Return simple human-readable description. 52 53 Includes only key fields in custom order. 54 """ 55 return f"Member(uid='{self.uid}', full_name='{self.full_name}', …)" 56 57 @property 58 def full_name(self) -> str: 59 """Return the `Member`'s full name, for convenience.""" 60 return f"{self.first_name} {self.last_name}"
id in Spond API; aliased as that's a Python built-in, and the Spond package
uses uid.
subGroups in Spond API; aliased to avoid confusion with Subgroup instances.
May be empty.
Custom fields. Same name in Spond API. May be empty.
roles in Spond API; aliased to avoid confusion with Role instances.
Not always present.
7class Profile(BaseModel): 8 """Represents a profile in the Spond system. 9 10 A `Profile` is an individual's account-specific record. 11 12 A `Profile` belongs to a `Member`. 13 """ 14 15 uid: str = Field(alias="id") 16 """`id` in Spond API; aliased as that's a Python built-in, and the Spond package 17 uses `uid`.""" 18 first_name: str = Field(alias="firstName") 19 """`firstName` in Spond API.""" 20 last_name: str = Field(alias="lastName") 21 """`lastName` in Spond API.""" 22 23 # Optional in Spond API data 24 email: EmailStr | None = Field(default=None) 25 """Same name in Spond API. Not always present.""" 26 phone_number: str | None = Field(alias="phoneNumber", default=None) 27 """`phoneNumber` in Spond API. 28 Not always present.""" 29 30 def __str__(self) -> str: 31 """Return simple human-readable description. 32 33 Includes only key fields in custom order. 34 """ 35 return f"Profile(uid='{self.uid}', full_name='{self.full_name}', …)" 36 37 @property 38 def full_name(self) -> str: 39 """Return the `Profile`'s full name, for convenience.""" 40 return f"{self.first_name} {self.last_name}"
Represents a profile in the Spond system.
A Profile is an individual's account-specific record.
id in Spond API; aliased as that's a Python built-in, and the Spond package
uses uid.
7class Role(BaseModel): 8 """Represents a role in the Spond system. 9 10 A `Role` belongs to a `Group`. 11 12 Use `Group.members_by_role()` to get subordinate `Member`s. 13 """ 14 15 uid: str = Field(alias="id") 16 """`id` in Spond API; aliased as that's a Python built-in, and the Spond package 17 uses `uid`.""" 18 name: str 19 """Same name in Spond API.""" 20 21 def __str__(self) -> str: 22 """Return simple human-readable description.""" 23 return f"Role(uid='{self.uid}', name='{self.name}')"
Represents a role in the Spond system.
Use Group.members_by_role() to get subordinate Members.
id in Spond API; aliased as that's a Python built-in, and the Spond package
uses uid.
7class Subgroup(BaseModel): 8 """Represents a subgroup in the Spond system. 9 10 A `Subgroup` belongs to a `Group`. 11 12 Use `Group.members_by_subgroup()` to get subordinate `Member`s. 13 """ 14 15 uid: str = Field(alias="id") 16 """`id` in Spond API; aliased as that's a Python built-in, and the Spond package 17 uses `uid`.""" 18 name: str 19 """Same name in Spond API.""" 20 21 def __str__(self) -> str: 22 """Return simple human-readable description.""" 23 return f"Subgroup(uid='{self.uid}', name='{self.name}')"
Represents a subgroup in the Spond system.
A Subgroup belongs to a Group.
Use Group.members_by_subgroup() to get subordinate Members.
id in Spond API; aliased as that's a Python built-in, and the Spond package
uses uid.