diff --git a/specification/talk/call.json b/specification/talk/call.json new file mode 100644 index 00000000..dd0eaa61 --- /dev/null +++ b/specification/talk/call.json @@ -0,0 +1,43 @@ +{ + "agent_id": 1, + "call_charge": "0.025", + "call_group_id": 1, + "call_recording_consent": "always", + "call_recording_consent_action": "caller_opted_in", + "call_recording_consent_keypress": "3", + "callback": false, + "callback_source": null, + "completion_status": "completed", + "consultation_time": 0, + "created_at": "2020-10-15T13:19:09Z", + "customer_id": 1, + "customer_requested_voicemail": false, + "default_group": true, + "direction": "inbound", + "duration": 782, + "exceeded_queue_wait_time": false, + "hold_time": 0, + "id": 123, + "ivr_action": null, + "ivr_destination_group_name": null, + "ivr_hops": null, + "ivr_routed_to": null, + "ivr_time_spent": null, + "minutes_billed": 14, + "not_recording_time": 5, + "outside_business_hours": false, + "overflowed": false, + "overflowed_to": null, + "phone_number": "+1234567890", + "phone_number_id": 123, + "quality_issues": ["high_latency"], + "recording_control_interactions": 5, + "recording_time": 15, + "talk_time": 751, + "ticket_id": 1059354, + "time_to_answer": 31, + "updated_at": "2020-10-15T14:12:22Z", + "voicemail": false, + "wait_time": 16, + "wrap_up_time": 66 + } \ No newline at end of file diff --git a/zenpy/lib/api.py b/zenpy/lib/api.py index bce229f6..454f7e36 100644 --- a/zenpy/lib/api.py +++ b/zenpy/lib/api.py @@ -12,7 +12,7 @@ from zenpy.lib.api_objects.help_centre_objects import ( Section, Article, Comment, ArticleAttachment, Label, Category, Translation, Topic, Post, Subscription) -from zenpy.lib.api_objects.talk_objects import (CurrentQueueActivity, +from zenpy.lib.api_objects.talk_objects import (Call, CurrentQueueActivity, PhoneNumbers, ShowAvailability, AgentsOverview, AccountOverview, @@ -2408,6 +2408,9 @@ def __init__(self, config): endpoint=EndpointFactory('talk'), object_type='talk') + self.calls = CallApi(config, + self.endpoint.calls, + object_type='call') self.current_queue_activity = StatsApi( config, self.endpoint.current_queue_activity, @@ -2432,6 +2435,12 @@ def __call__(self, *args, **kwargs): raise NotImplementedError("Cannot directly call the TalkApi!") +class CallApi(TalkApiBase, IncrementalApi): + def __init__(self, config, endpoint, object_type): + super(CallApi, self).__init__(config, + object_type=object_type, + endpoint=endpoint) + class StatsApi(TalkApiBase): def __init__(self, config, endpoint, object_type): super(StatsApi, self).__init__(config, diff --git a/zenpy/lib/api_objects/__init__.py b/zenpy/lib/api_objects/__init__.py index e2d7fbc4..2a8e4e7e 100644 --- a/zenpy/lib/api_objects/__init__.py +++ b/zenpy/lib/api_objects/__init__.py @@ -1,3 +1,4 @@ + ###################################################################### # Do not modify, these classes are autogenerated by gen_classes.py # ###################################################################### @@ -11,6 +12,7 @@ class BaseObject(object): """ Base for all Zenpy objects. Keeps track of which attributes have been modified. """ + def __new__(cls, *args, **kwargs): instance = super(BaseObject, cls).__new__(cls) instance.__dict__['_dirty_attributes'] = set() @@ -42,17 +44,14 @@ def _set_dirty(self, obj=None): """ Recursively set self and all child objects _dirty flag. """ obj = obj or self for key, value in vars(obj).items(): - if key not in ('api', '_dirty_attributes', '_always_dirty', - '_dirty_callback', '_dirty'): + if key not in ('api', '_dirty_attributes', '_always_dirty', '_dirty_callback', '_dirty'): setattr(obj, key, value) if isinstance(value, BaseObject): self._set_dirty(value) def to_json(self, indent=2): """ Return self formatted as JSON. """ - return json.dumps(self, - default=json_encode_for_printing, - indent=indent) + return json.dumps(self, default=json_encode_for_printing, indent=indent) def to_dict(self, serialize=False): """ @@ -63,9 +62,7 @@ def to_dict(self, serialize=False): encode_method = json_encode_for_zendesk else: encode_method = json_encode_for_printing - return json.loads( - json.dumps(self._to_dict(serialize=serialize), - default=encode_method)) + return json.loads(json.dumps(self._to_dict(serialize=serialize), default=encode_method)) def _to_dict(self, serialize=False): """ @@ -90,8 +87,7 @@ def _to_dict(self, serialize=False): continue # These are for internal tracking, so just delete. - elif key in ('api', '_dirty_attributes', '_always_dirty', - '_dirty_callback', '_dirty'): + elif key in ('api', '_dirty_attributes', '_always_dirty', '_dirty_callback', '_dirty'): del copy_dict[key] # If the attribute has not been modified, do not send it. @@ -106,17 +102,15 @@ def _to_dict(self, serialize=False): def __repr__(self): class_name = type(self).__name__ - if class_name in ('UserField', ): + if class_name in ('UserField',): return "{}()".format(class_name) def formatted(item): - return item if (isinstance(item, int) - or item is None) else "'{}'".format(item) + return item if (isinstance(item, int) or item is None) else "'{}'".format(item) for identifier in ('id', 'token', 'key', 'name', 'account_key'): if hasattr(self, identifier): - return "{}({}={})".format(class_name, identifier, - formatted(getattr(self, identifier))) + return "{}({}={})".format(class_name, identifier, formatted(getattr(self, identifier))) return "{}()".format(class_name) diff --git a/zenpy/lib/api_objects/talk_objects.py b/zenpy/lib/api_objects/talk_objects.py index 3f5134e1..ef6a0cbf 100644 --- a/zenpy/lib/api_objects/talk_objects.py +++ b/zenpy/lib/api_objects/talk_objects.py @@ -211,6 +211,193 @@ def __init__(self, continue +class Call(BaseObject): + """ + ###################################################################### + # Do not modify, this class is autogenerated by gen_classes.py # + ###################################################################### + """ + def __init__(self, + api=None, + agent_id=None, + call_charge=None, + call_group_id=None, + call_recording_consent=None, + call_recording_consent_action=None, + call_recording_consent_keypress=None, + callback=None, + callback_source=None, + completion_status=None, + consultation_time=None, + created_at=None, + customer_id=None, + customer_requested_voicemail=None, + default_group=None, + direction=None, + duration=None, + exceeded_queue_wait_time=None, + hold_time=None, + id=None, + ivr_action=None, + ivr_destination_group_name=None, + ivr_hops=None, + ivr_routed_to=None, + ivr_time_spent=None, + minutes_billed=None, + not_recording_time=None, + outside_business_hours=None, + overflowed=None, + overflowed_to=None, + phone_number=None, + phone_number_id=None, + quality_issues=None, + recording_control_interactions=None, + recording_time=None, + talk_time=None, + ticket_id=None, + time_to_answer=None, + updated_at=None, + voicemail=None, + wait_time=None, + wrap_up_time=None, + **kwargs): + + self.api = api + self.agent_id = agent_id + self.call_charge = call_charge + self.call_group_id = call_group_id + self.call_recording_consent = call_recording_consent + self.call_recording_consent_action = call_recording_consent_action + self.call_recording_consent_keypress = call_recording_consent_keypress + self.callback = callback + self.callback_source = callback_source + self.completion_status = completion_status + self.consultation_time = consultation_time + self.created_at = created_at + self.customer_id = customer_id + self.customer_requested_voicemail = customer_requested_voicemail + self.default_group = default_group + self.direction = direction + self.duration = duration + self.exceeded_queue_wait_time = exceeded_queue_wait_time + self.hold_time = hold_time + self.id = id + self.ivr_action = ivr_action + self.ivr_destination_group_name = ivr_destination_group_name + self.ivr_hops = ivr_hops + self.ivr_routed_to = ivr_routed_to + self.ivr_time_spent = ivr_time_spent + self.minutes_billed = minutes_billed + self.not_recording_time = not_recording_time + self.outside_business_hours = outside_business_hours + self.overflowed = overflowed + self.overflowed_to = overflowed_to + self.phone_number = phone_number + self.phone_number_id = phone_number_id + self.quality_issues = quality_issues + self.recording_control_interactions = recording_control_interactions + self.recording_time = recording_time + self.talk_time = talk_time + self.ticket_id = ticket_id + self.time_to_answer = time_to_answer + self.updated_at = updated_at + self.voicemail = voicemail + self.wait_time = wait_time + self.wrap_up_time = wrap_up_time + + for key, value in kwargs.items(): + setattr(self, key, value) + + for key in self.to_dict(): + if getattr(self, key) is None: + try: + self._dirty_attributes.remove(key) + except KeyError: + continue + + @property + def agent(self): + + if self.api and self.agent_id: + return self.api._get_agent(self.agent_id) + + @agent.setter + def agent(self, agent): + if agent: + self.agent_id = agent.id + self._agent = agent + + @property + def call_group(self): + + if self.api and self.call_group_id: + return self.api._get_call_group(self.call_group_id) + + @call_group.setter + def call_group(self, call_group): + if call_group: + self.call_group_id = call_group.id + self._call_group = call_group + + @property + def created(self): + + if self.created_at: + return dateutil.parser.parse(self.created_at) + + @created.setter + def created(self, created): + if created: + self.created_at = created + + @property + def customer(self): + + if self.api and self.customer_id: + return self.api._get_customer(self.customer_id) + + @customer.setter + def customer(self, customer): + if customer: + self.customer_id = customer.id + self._customer = customer + + @property + def phone_number(self): + + if self.api and self.phone_number_id: + return self.api._get_phone_number(self.phone_number_id) + + @phone_number.setter + def phone_number(self, phone_number): + if phone_number: + self.phone_number_id = phone_number.id + self._phone_number = phone_number + + @property + def ticket(self): + + if self.api and self.ticket_id: + return self.api._get_ticket(self.ticket_id) + + @ticket.setter + def ticket(self, ticket): + if ticket: + self.ticket_id = ticket.id + self._ticket = ticket + + @property + def updated(self): + + if self.updated_at: + return dateutil.parser.parse(self.updated_at) + + @updated.setter + def updated(self, updated): + if updated: + self.updated_at = updated + + class CurrentQueueActivity(BaseObject): """ ###################################################################### diff --git a/zenpy/lib/endpoint.py b/zenpy/lib/endpoint.py index 05aeb82c..529d9cfa 100644 --- a/zenpy/lib/endpoint.py +++ b/zenpy/lib/endpoint.py @@ -650,6 +650,9 @@ class Dummy(object): pass talk = Dummy() + talk.calls = Dummy() + talk.calls.incremental = IncrementalEndpoint( + 'channels/voice/stats/incremental/calls.json') talk.current_queue_activity = PrimaryEndpoint( 'channels/voice/stats/current_queue_activity') talk.agents_activity = PrimaryEndpoint( diff --git a/zenpy/lib/mapping.py b/zenpy/lib/mapping.py index c78b32da..fed9a2c3 100644 --- a/zenpy/lib/mapping.py +++ b/zenpy/lib/mapping.py @@ -254,6 +254,7 @@ class TalkObjectMapping(ZendeskObjectMapping): to prevent namespace collisions between APIs. """ class_mapping = { + 'call': Call, 'account_overview': AccountOverview, 'agents_activity': AgentsActivity, 'agents_overview': AgentsOverview,