Skip to content

Commit c94ef38

Browse files
Merge pull request #43 from JaviCerveraIngram/CPS-22-conversation
CPS-22-conversation
2 parents 4df3da4 + ddd1c43 commit c94ef38

File tree

8 files changed

+335
-12
lines changed

8 files changed

+335
-12
lines changed

connect/models/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from .company import Company, User
1010
from .connection import Connection
1111
from .contact import Contact, ContactInfo, PhoneNumber
12+
from .conversation import Conversation, ConversationMessage
1213
from .event import EventInfo, Events
1314
from .fulfillment import Fulfillment
1415
from .hub import Hub, HubInstance, ExtIdHub, HubStats
@@ -34,6 +35,8 @@
3435
'Contact',
3536
'ContactInfo',
3637
'Contract',
38+
'Conversation',
39+
'ConversationMessage',
3740
'CustomerUiSettings',
3841
'Document',
3942
'DownloadLink',

connect/models/conversation.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# -*- coding: utf-8 -*-
2+
3+
# This file is part of the Ingram Micro Cloud Blue Connect SDK.
4+
# Copyright (c) 2019 Ingram Micro. All Rights Reserved.
5+
6+
import datetime
7+
8+
from typing import List
9+
10+
from .base import BaseModel
11+
from .company import User
12+
from connect.models.schemas import ConversationMessageSchema, ConversationSchema
13+
14+
15+
class ConversationMessage(BaseModel):
16+
""" Message in a :py:class:`.Conversation`. """
17+
18+
_schema = ConversationMessageSchema()
19+
20+
conversation = None # type: str
21+
""" (str) Primary ID of Conversation object. """
22+
23+
created = None # type: datetime.datetime
24+
""" (datetime.datetime) Date of the Message creation. """
25+
26+
creator = None # type: User
27+
""" (:py:class:`.User`) :py:class:`.User` that created the message. """
28+
29+
text = None # type: str
30+
""" (str) Actual message. """
31+
32+
33+
class Conversation(BaseModel):
34+
""" Conversation. """
35+
36+
_schema = ConversationSchema()
37+
38+
instance_id = None # type: str
39+
""" (str) The id of object based on which discussion is made, e.g. listing request.
40+
It can be any object.
41+
"""
42+
43+
created = None # type: datetime.datetime
44+
""" (datetime.datetime) Date of the Conversation creation. """
45+
46+
topic = None # type: str
47+
""" (str) Conversation topic. """
48+
49+
messages = None # type: List[ConversationMessage]
50+
""" (List[:py:class:`.ConversationMessage`]) List of :py:class:`.ConversationMessage`
51+
objects.
52+
"""
53+
54+
creator = None # type: User
55+
""" (:py:class:`.User`) Creator of the conversation. """
56+
57+
def add_message(self, message, config=None):
58+
""" Adds a message to the conversation.
59+
60+
:param str message: Message to add.
61+
:param Config config: Configuration, or ``None`` to use the environment config (default).
62+
:return: The added message.
63+
:rtype: ConversationMessage
64+
:raises TypeError: Raised if the message cannot be deserialized.
65+
"""
66+
67+
from connect.resources.base import ApiClient
68+
response, _ = ApiClient(config, base_path='conversations/' + self.id + '/messages')\
69+
.post(json={'text': message})
70+
return ConversationMessage.deserialize(response)

connect/models/fulfillment.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from .asset import Asset
99
from .base import BaseModel
10+
from .conversation import Conversation
1011
from .marketplace import Contract, Marketplace
1112
from connect.models.schemas import FulfillmentSchema
1213

@@ -113,3 +114,23 @@ def needs_migration(self, migration_key='migration_info'):
113114
:rtype: bool
114115
"""
115116
return self.asset.get_param_by_id(migration_key) is not None
117+
118+
def get_conversation(self, config=None):
119+
"""
120+
:param Config config: Configuration, or ``None`` to use the environment config (default).
121+
:return: The conversation for this request, or ``None`` if there is none.
122+
:rtype: Conversation|None
123+
"""
124+
from connect.resources.base import ApiClient
125+
126+
client = ApiClient(config, base_path='conversations')
127+
response, _ = client.get(params={'instance_id': self.id})
128+
try:
129+
conversations = Conversation.deserialize(response)
130+
if conversations and conversations[0].id:
131+
response, _ = client.get(conversations[0].id)
132+
return Conversation.deserialize(response)
133+
else:
134+
return None
135+
except ValueError:
136+
return None

connect/models/schemas.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,3 +567,28 @@ class UsageRecordSchema(BaseSchema):
567567
def make_object(self, data):
568568
from connect.models import UsageRecord
569569
return UsageRecord(**data)
570+
571+
572+
class ConversationMessageSchema(BaseSchema):
573+
conversation = fields.Str()
574+
created = fields.DateTime()
575+
creator = fields.Nested(UserSchema)
576+
text = fields.Str()
577+
578+
@post_load
579+
def make_object(self, data):
580+
from connect.models import ConversationMessage
581+
return ConversationMessage(**data)
582+
583+
584+
class ConversationSchema(BaseSchema):
585+
instance_id = fields.Str()
586+
created = fields.DateTime()
587+
topic = fields.Str()
588+
messages = fields.Nested(ConversationMessageSchema, many=True)
589+
creator = fields.Nested(UserSchema)
590+
591+
@post_load
592+
def make_object(self, data):
593+
from connect.models import Conversation
594+
return Conversation(**data)

connect/resources/fulfillment_automation.py

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,35 +50,69 @@ def filters(self, status='pending', **kwargs):
5050

5151
def dispatch(self, request):
5252
# type: (Fulfillment) -> str
53+
54+
conversation = request.get_conversation(self.config)
55+
5356
try:
5457
if self.config.products \
5558
and request.asset.product.id not in self.config.products:
5659
return 'Invalid product'
5760

5861
logger.info('Start request process / ID request - {}'.format(request.id))
59-
result = self.process_request(request)
62+
process_result = self.process_request(request)
6063

61-
if not result:
64+
if not process_result:
6265
logger.info('Method `process_request` did not return result')
6366
return ''
6467

65-
params = {}
66-
if isinstance(result, ActivationTileResponse):
67-
params = {'activation_tile': result.tile}
68-
elif isinstance(result, ActivationTemplateResponse):
69-
params = {'template_id': result.template_id}
70-
71-
return self.approve(request.id, params)
68+
if isinstance(process_result, ActivationTileResponse):
69+
message = 'Activated using custom activation tile.'
70+
approved = self.approve(request.id, {'activation_tile': process_result.tile})
71+
elif isinstance(process_result, ActivationTemplateResponse):
72+
message = 'Activated using template {}.'.format(process_result.template_id)
73+
approved = self.approve(request.id, {'template_id': process_result.template_id})
74+
else:
75+
# We should not get here
76+
message = ''
77+
approved = ''
78+
79+
if conversation:
80+
try:
81+
conversation.add_message(message)
82+
except TypeError as ex:
83+
logger.error('Error updating conversation for request {}: {}'
84+
.format(request.id, ex))
85+
return approved
7286

7387
except InquireRequest as inquire:
7488
self.update_parameters(request.id, inquire.params)
75-
return self.inquire(request.id)
89+
inquired = self.inquire(request.id)
90+
try:
91+
conversation.add_message(str(inquire))
92+
except TypeError as ex:
93+
logger.error('Error updating conversation for request {}: {}'
94+
.format(request.id, ex))
95+
return inquired
7696

7797
except FailRequest as fail:
78-
return self.fail(request.id, reason=str(fail))
98+
# PyCharm incorrectly detects unreachable code here, so disable
99+
# noinspection PyUnreachableCode
100+
failed = self.fail(request.id, reason=str(fail))
101+
try:
102+
conversation.add_message(str(fail))
103+
except TypeError as ex:
104+
logger.error('Error updating conversation for request {}: {}'
105+
.format(request.id, ex))
106+
return failed
79107

80108
except SkipRequest as skip:
81-
return skip.code
109+
skipped = skip.code
110+
try:
111+
conversation.add_message(str(skip))
112+
except TypeError as ex:
113+
logger.error('Error updating conversation for request {}: {}'
114+
.format(request.id, ex))
115+
return skipped
82116

83117
@deprecated(deprecated_in='16.0', details='Use ``TierConfig.get`` instead.')
84118
def get_tier_config(self, tier_id, product_id):
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"id": "ME-000-000-000",
3+
"conversation": "CO-000-000-000",
4+
"created": "2018-12-18T13:03:30+00:00",
5+
"creator": {
6+
"id": "UR-000-000-000",
7+
"name": "Some User"
8+
},
9+
"text": "Hi, please see my listing request"
10+
}

tests/data/conversation.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"id": "CO-750-033-356",
3+
"instance_id": "LST-038-662-242",
4+
"created": "2018-12-18T12:49:34+00:00",
5+
"topic": "Topic",
6+
"messages": [
7+
{
8+
"id": "ME-506-258-087",
9+
"conversation": "CO-750-033-356",
10+
"created": "2018-12-18T13:03:30+00:00",
11+
"creator": {
12+
"id": "UR-922-977-649",
13+
"name": "Some User"
14+
},
15+
"text": "Hi, check out"
16+
}
17+
],
18+
"creator": {
19+
"id": "UR-922-977-649",
20+
"name": "Some User"
21+
}
22+
}

0 commit comments

Comments
 (0)