This commit is contained in:
2022-11-15 07:33:28 -06:00
parent cd7b98e3f5
commit 54d6dc410e
7 changed files with 130 additions and 19 deletions

View File

@@ -4395,7 +4395,7 @@ var dataUser = new Data();
// 2. Improve the module's maintainability by reducing the storage // 2. Improve the module's maintainability by reducing the storage
// paths to a single mechanism. // paths to a single mechanism.
// 3. Use the same single mechanism to support "private" and "user" data. // 3. Use the same single mechanism to support "private" and "user" data.
// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) // 4. _Never_ expose "private" data to user code (TODO: Drop _internal_data_dict, _removeData)
// 5. Avoid exposing implementation details on user objects (eg. expando properties) // 5. Avoid exposing implementation details on user objects (eg. expando properties)
// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 // 6. Provide a clear path for implementation upgrade to WeakMap in 2014
@@ -4463,7 +4463,7 @@ jQuery.extend( {
dataUser.remove( elem, name ); dataUser.remove( elem, name );
}, },
// TODO: Now that all calls to _data and _removeData have been replaced // TODO: Now that all calls to _internal_data_dict and _removeData have been replaced
// with direct calls to dataPriv methods, these can be deprecated. // with direct calls to dataPriv methods, these can be deprecated.
_data: function( elem, name, data ) { _data: function( elem, name, data ) {
return dataPriv.access( elem, name, data ); return dataPriv.access( elem, name, data );

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -4,6 +4,7 @@ from apiclient import (
APIClient, APIClient,
) )
import typing as T import typing as T
import copy
# Import "preview" of Self typing # Import "preview" of Self typing
# https://stackoverflow.com/a/70932112 # https://stackoverflow.com/a/70932112
@@ -23,25 +24,31 @@ class BaseApiObject:
:param data: Data to instantiate instance, defaults to empty dict. :param data: Data to instantiate instance, defaults to empty dict.
""" """
self.client = client self.client = client
self._data = data for k, v in data.items():
if k == 'type': continue
if k == 'id': continue
setattr(self, k, v)
self._internal_data_dict = copy.deepcopy(data)
self.rel = self.__class__.rel self.rel = self.__class__.rel
"""rel: Relationship between a linked resource and the current document""" """rel: Relationship between a linked resource and the current document"""
def __repr__(self): def __repr__(self):
return f'TeamSnap<{self.__class__.__name__}:{self.id}> "{self.__str__()}"' return f'TeamSnap<{self.__class__.__name__}:{self.id}> "{self.data.get("name")}"'
def __getitem__(self, key): def __getitem__(self, key):
return self._data.__getitem__(key) return self._internal_data_dict.__getitem__(key)
# return getattr(self, key)
def __setitem__(self, key, newvalue): def __setitem__(self, key, newvalue):
return self._data.__setitem__(key, newvalue) return self._internal_data_dict.__setitem__(key, newvalue)
# return setattr(self, key, newvalue)
def __iter__(self): def __iter__(self):
return iter(self._data.items()) return iter(self._internal_data_dict.items())
@property @property
def id(self) -> int: def id(self) -> int:
return self._data["id"] return self._internal_data_dict["id"]
@property @property
def data(self) -> T.Dict[str, T.Union[str, list]]: def data(self) -> T.Dict[str, T.Union[str, list]]:
@@ -49,7 +56,7 @@ class BaseApiObject:
:return: dict: dict with keys: :return: dict: dict with keys:
""" """
return self._data return self._internal_data_dict
@classmethod @classmethod
def search(cls, client: APIClient, **kwargs): def search(cls, client: APIClient, **kwargs):
@@ -62,11 +69,11 @@ class BaseApiObject:
@classmethod @classmethod
def get(cls, client: APIClient, id: T.Union[int, str]) -> Self: def get(cls, client: APIClient, id: T.Union[int, str]) -> Self:
r = client.get(f"{client.link(cls.rel)}/{id}") r = client.get(f"{client.link(cls.rel)}/{id}")
return cls(client, cls.rel, client.parse_response(r)[0]) return cls(client, client.parse_response(r)[0])
@classmethod @classmethod
def new(cls, client: Self) -> Self: def new(cls, client: Self) -> Self:
return cls(client, cls.rel) return cls(client)
def post(self) -> Self: def post(self) -> Self:
data = { data = {
@@ -75,7 +82,7 @@ class BaseApiObject:
} }
} }
r = self.client.post_item(self.rel, data=data) r = self.client.post_item(self.rel, data=data)
self._data = r self._internal_data_dict = r
return self return self
def put(self) -> Self: def put(self) -> Self:
@@ -86,7 +93,7 @@ class BaseApiObject:
} }
id = self.data.get("id") id = self.data.get("id")
r = self.client.put_item(self.rel, id=id, data=data) r = self.client.put_item(self.rel, id=id, data=data)
self._data = r self._internal_data_dict = r
return self return self
def delete(self): def delete(self):

View File

@@ -11,6 +11,51 @@ class Event(BaseApiObject):
type = "event" type = "event"
version = "3.866.0" version = "3.866.0"
__slots__ = (
# "type",
"additional_location_details",
"browser_time_zone",
"division_location_id",
"doesnt_count_towards_record",
"duration_in_minutes",
"game_type_code",
"icon_color",
"is_canceled",
"is_game",
"is_overtime",
"is_shootout",
"is_tbd",
"label",
"location_id",
"minutes_to_arrive_early",
"name",
"notes",
"notify_opponent",
"notify_opponent_contacts_email",
"notify_opponent_contacts_name",
"notify_opponent_notes",
"notify_team",
"notify_team_as_member_id",
"opponent_id",
"points_for_opponent",
"points_for_team",
"repeating_include",
"repeating_type_code",
"repeating_until",
"results",
"results_url",
"shootout_points_for_opponent",
"shootout_points_for_team",
"start_date",
"team_id",
"time_zone",
"tracks_availability",
"uniform",
)
def __str__(self):
return f'{self["formatted_title"]}'
@property @property
def data(self): def data(self):
"""Data dictionary for object """Data dictionary for object

View File

@@ -23,5 +23,5 @@ class EventLineupEntry(BaseApiObject):
# this is a workaround # this is a workaround
r = client.get(f"{client.link(cls.rel)}/search", params=kwargs) r = client.get(f"{client.link(cls.rel)}/search", params=kwargs)
results = client.parse_response(r) results = client.parse_response(r)
[cls(client, rel=cls.rel, data=r) for r in results] [cls(client, data=r) for r in results]
return [cls(client, rel=cls.rel, data=r) for r in results] return [cls(client, data=r) for r in results]

View File

@@ -5,3 +5,4 @@ black==22.10
vcrpy==4.2 vcrpy==4.2
jinja2==3.1 jinja2==3.1
python-dotenv==0.21 python-dotenv==0.21
bump==1.32

55
tests/client.py Normal file
View File

@@ -0,0 +1,55 @@
#!/usr/bin/env python
"""Tests for `pyteamsnap` package."""
import unittest
from unittest import TestCase
from pyteamsnap import client
from os import getenv
from pyteamsnap.models.base import BaseApiObject
import vcr
TEAMSNAP_TOKEN = getenv('TEAMSNAP_TOKEN')
TEAMSNAP_TEAM = getenv('TEAMSNAP_TEAM')
TEAMSNAP_EVENT = getenv('TEAMSNAP_EVENT')
vcr_options = {
'decode_compressed_response': True,
'cassette_library_dir':'tests/fixtures/cassettes',
'filter_headers':['authorization']
}
class ClientTestCase(TestCase):
"""Tests for `pyteamsnap` package."""
@classmethod
def setUpClass(cls) -> None:
"""Set up test fixtures, if any."""
cls.TEAMSNAP_TOKEN = getenv('TEAMSNAP_TOKEN')
cls.TEAMSNAP_TEAM = int(getenv('TEAMSNAP_TEAM'))
cls.TEAMSNAP_EVENT = getenv('TEAMSNAP_EVENT')
with vcr.use_cassette("client.yml", **vcr_options):
cls.client = client.TeamSnap(token=TEAMSNAP_TOKEN)
cls.cassette = vcr.use_cassette(f"{cls.__name__}.yml", **vcr_options)
super().setUpClass()
def test_bulkload(self):
from pyteamsnap.models import Event, EventLineup, EventLineupEntry, AvailabilitySummary, Member
with self.cassette:
bulk_load = self.client.bulk_load(
team_id=self.TEAMSNAP_TEAM,
event__id=self.TEAMSNAP_EVENT,
types=[
Event,
EventLineup,
EventLineupEntry,
AvailabilitySummary,
Member
])
self.assertIsInstance(bulk_load, list)
return bulk_load