MagicFeedback 0.0.4__tar.gz → 0.0.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {magicfeedback-0.0.4 → magicfeedback-0.0.7}/PKG-INFO +3 -2
- {magicfeedback-0.0.4 → magicfeedback-0.0.7}/pyproject.toml +1 -1
- {magicfeedback-0.0.4 → magicfeedback-0.0.7}/src/MagicFeedback.egg-info/PKG-INFO +3 -2
- {magicfeedback-0.0.4 → magicfeedback-0.0.7}/src/MagicFeedback.egg-info/SOURCES.txt +1 -0
- {magicfeedback-0.0.4 → magicfeedback-0.0.7}/src/magicfeedback.py +59 -15
- magicfeedback-0.0.7/tests/test_feedback_answers_array.py +49 -0
- {magicfeedback-0.0.4 → magicfeedback-0.0.7}/LICENCE +0 -0
- {magicfeedback-0.0.4 → magicfeedback-0.0.7}/README.md +0 -0
- {magicfeedback-0.0.4 → magicfeedback-0.0.7}/setup.cfg +0 -0
- {magicfeedback-0.0.4 → magicfeedback-0.0.7}/src/MagicFeedback.egg-info/dependency_links.txt +0 -0
- {magicfeedback-0.0.4 → magicfeedback-0.0.7}/src/MagicFeedback.egg-info/top_level.txt +0 -0
- {magicfeedback-0.0.4 → magicfeedback-0.0.7}/src/__init__.py +0 -0
- {magicfeedback-0.0.4 → magicfeedback-0.0.7}/tests/test_apikey.py +0 -0
- {magicfeedback-0.0.4 → magicfeedback-0.0.7}/tests/test_campaign.py +0 -0
- {magicfeedback-0.0.4 → magicfeedback-0.0.7}/tests/test_contact.py +0 -0
- {magicfeedback-0.0.4 → magicfeedback-0.0.7}/tests/test_feedback_create.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: MagicFeedback
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.7
|
|
4
4
|
Summary: SDK for MagicFeedback API
|
|
5
5
|
Author-email: Francisco Arias <farias@magicfedback.io>
|
|
6
6
|
Project-URL: Homepage, https://github.com/MagicFeedback/magicfeedback_python_sdk
|
|
@@ -11,6 +11,7 @@ Classifier: Operating System :: OS Independent
|
|
|
11
11
|
Requires-Python: >=3.8
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENCE
|
|
14
|
+
Dynamic: license-file
|
|
14
15
|
|
|
15
16
|
# MagicFeedback SDK
|
|
16
17
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: MagicFeedback
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.7
|
|
4
4
|
Summary: SDK for MagicFeedback API
|
|
5
5
|
Author-email: Francisco Arias <farias@magicfedback.io>
|
|
6
6
|
Project-URL: Homepage, https://github.com/MagicFeedback/magicfeedback_python_sdk
|
|
@@ -11,6 +11,7 @@ Classifier: Operating System :: OS Independent
|
|
|
11
11
|
Requires-Python: >=3.8
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENCE
|
|
14
|
+
Dynamic: license-file
|
|
14
15
|
|
|
15
16
|
# MagicFeedback SDK
|
|
16
17
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import logging
|
|
2
3
|
from typing import Any, Dict
|
|
3
4
|
|
|
4
5
|
import requests
|
|
@@ -9,13 +10,33 @@ class MagicFeedbackClient:
|
|
|
9
10
|
|
|
10
11
|
def __init__(self, user: str, password: str, base_url: str = "https://api.magicfeedback.io", ip_key: str = 'AIzaSyAKcR895VURSQZSN2T_RD6jX_9y5HRmH80'):
|
|
11
12
|
|
|
13
|
+
self.logger = logging.getLogger("magicfeedback_sdk")
|
|
14
|
+
self.logger.addHandler(logging.NullHandler())
|
|
15
|
+
|
|
12
16
|
self.base_url = base_url
|
|
13
17
|
self.ip_key = ip_key
|
|
14
18
|
|
|
15
19
|
self.api_key = self.get_api_key(user, password)
|
|
16
|
-
|
|
20
|
+
self.logger.info("API Key: %s", self.api_key)
|
|
17
21
|
self.headers = {"Authorization": f"Bearer {self.api_key}"}
|
|
18
22
|
|
|
23
|
+
def logging(self, level: int):
|
|
24
|
+
"""Sets the logging level for the SDK.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
level (int): The logging level to set (e.g., logging.DEBUG, logging.INFO).
|
|
28
|
+
"""
|
|
29
|
+
self.logger.setLevel(level)
|
|
30
|
+
|
|
31
|
+
# Evita agregar múltiples handlers si ya se configuró
|
|
32
|
+
if not any(isinstance(h, logging.StreamHandler) for h in self.logger.handlers):
|
|
33
|
+
handler = logging.StreamHandler()
|
|
34
|
+
handler.setLevel(level)
|
|
35
|
+
formatter = logging.Formatter(
|
|
36
|
+
"%(asctime)s - %(levelname)s - %(message)s")
|
|
37
|
+
handler.setFormatter(formatter)
|
|
38
|
+
self.logger.addHandler(handler)
|
|
39
|
+
|
|
19
40
|
def get_api_key(self, user, password):
|
|
20
41
|
"""Obtains the API key using user and password authentication."""
|
|
21
42
|
# TODO: Implement control to check if the token is still valid - Only for 1 hour
|
|
@@ -33,9 +54,8 @@ class MagicFeedbackClient:
|
|
|
33
54
|
str: The obtained ID token.
|
|
34
55
|
"""
|
|
35
56
|
# TODO: Control in case the call is not good
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
print("Password: ", password)
|
|
57
|
+
self.logger.info("Logging in with user and password...")
|
|
58
|
+
self.logger.info("User: %s", user)
|
|
39
59
|
|
|
40
60
|
options = {
|
|
41
61
|
"method": "POST",
|
|
@@ -64,12 +84,12 @@ class MagicFeedbackClient:
|
|
|
64
84
|
method, url, headers=self.headers, json=json)
|
|
65
85
|
response.raise_for_status() # Raise exception for non-2xx status codes
|
|
66
86
|
# TODO: Control the status of the call
|
|
67
|
-
|
|
87
|
+
self.logger.debug("Status code: %s", response.status_code)
|
|
68
88
|
# Control if exist response that can be converted in json
|
|
69
89
|
if response.text:
|
|
70
|
-
|
|
90
|
+
self.logger.debug("Response: %s", response.json())
|
|
71
91
|
return response.json()
|
|
72
|
-
|
|
92
|
+
|
|
73
93
|
return {}
|
|
74
94
|
|
|
75
95
|
####################################################################################
|
|
@@ -97,6 +117,13 @@ class MagicFeedbackClient:
|
|
|
97
117
|
if field not in feedback:
|
|
98
118
|
raise ValueError(f"Missing required field: {field}")
|
|
99
119
|
|
|
120
|
+
# Ensure answers.values are wrapped in a list if not already
|
|
121
|
+
if "answers" in feedback and isinstance(feedback["answers"], list):
|
|
122
|
+
for answer in feedback["answers"]:
|
|
123
|
+
if "value" in answer and not isinstance(answer["value"], list):
|
|
124
|
+
# Wrap the value in a list if it is not already
|
|
125
|
+
answer["value"] = [answer["value"]]
|
|
126
|
+
|
|
100
127
|
return self._make_request("POST", url, json=feedback)
|
|
101
128
|
|
|
102
129
|
def get_feedback(self, feedback_id: str) -> Dict[str, Any]:
|
|
@@ -173,7 +200,7 @@ class MagicFeedbackClient:
|
|
|
173
200
|
url = f"{url}?filter={json.dumps(filter)}"
|
|
174
201
|
|
|
175
202
|
return self._make_request("GET", url)
|
|
176
|
-
|
|
203
|
+
|
|
177
204
|
def update_contact(self, contact_id: str, contact: Dict[str, Any]) -> Dict[str, Any]:
|
|
178
205
|
"""Updates a specific contact item.
|
|
179
206
|
|
|
@@ -186,7 +213,7 @@ class MagicFeedbackClient:
|
|
|
186
213
|
"""
|
|
187
214
|
url = f"{self.base_url}/crm/contacts/{contact_id}"
|
|
188
215
|
return self._make_request("PATCH", url, json=contact)
|
|
189
|
-
|
|
216
|
+
|
|
190
217
|
def delete_contact(self, contact_id: str) -> None:
|
|
191
218
|
"""Deletes a specific contact item.
|
|
192
219
|
|
|
@@ -220,7 +247,7 @@ class MagicFeedbackClient:
|
|
|
220
247
|
raise ValueError(f"Missing required field: {field}")
|
|
221
248
|
|
|
222
249
|
return self._make_request("POST", url, json=campaign)
|
|
223
|
-
|
|
250
|
+
|
|
224
251
|
def get_campaigns(self, filter) -> Dict[str, Any]:
|
|
225
252
|
"""Retrieves a specific campaign item.
|
|
226
253
|
|
|
@@ -236,7 +263,7 @@ class MagicFeedbackClient:
|
|
|
236
263
|
url = f"{url}?filter={json.dumps(filter)}"
|
|
237
264
|
|
|
238
265
|
return self._make_request("GET", url)
|
|
239
|
-
|
|
266
|
+
|
|
240
267
|
def create_campaign_session(self, campaign_id: str, session: Dict[str, Any]) -> Dict[str, Any]:
|
|
241
268
|
"""Creates a new campaign session item.
|
|
242
269
|
|
|
@@ -256,12 +283,12 @@ class MagicFeedbackClient:
|
|
|
256
283
|
for field in required_fields:
|
|
257
284
|
if field not in session:
|
|
258
285
|
raise ValueError(f"Missing required field: {field}")
|
|
259
|
-
|
|
286
|
+
|
|
260
287
|
if len(session.get("crmContactId")) == 0:
|
|
261
288
|
raise ValueError("Contact ID cannot be empty.")
|
|
262
289
|
|
|
263
290
|
return self._make_request("POST", url, json=session)
|
|
264
|
-
|
|
291
|
+
|
|
265
292
|
def get_campaign_sessions(self, campaign_id: str, filter) -> Dict[str, Any]:
|
|
266
293
|
"""Retrieves a specific campaign session item.
|
|
267
294
|
|
|
@@ -272,8 +299,25 @@ class MagicFeedbackClient:
|
|
|
272
299
|
Returns:
|
|
273
300
|
Dict[str, Any]: The retrieved campaign session item.
|
|
274
301
|
"""
|
|
275
|
-
url = f"{self.base_url}/campaigns/{campaign_id}/
|
|
302
|
+
url = f"{self.base_url}/campaigns/{campaign_id}/session"
|
|
276
303
|
if filter:
|
|
277
304
|
url = f"{url}?filter={json.dumps(filter)}"
|
|
278
305
|
|
|
279
|
-
return self._make_request("GET", url)
|
|
306
|
+
return self._make_request("GET", url)
|
|
307
|
+
|
|
308
|
+
####################################################################################
|
|
309
|
+
# Metrics API Methods #
|
|
310
|
+
####################################################################################
|
|
311
|
+
|
|
312
|
+
def get_metrics(self, filter) -> Dict[str, Any]:
|
|
313
|
+
"""Retrieves metrics data.
|
|
314
|
+
Args:
|
|
315
|
+
filter (Dict[str, Any]): The filter to apply to the metrics.
|
|
316
|
+
Returns:
|
|
317
|
+
Dict[str, Any]: The retrieved metrics data.
|
|
318
|
+
"""
|
|
319
|
+
url = f"{self.base_url}/metrics"
|
|
320
|
+
if filter:
|
|
321
|
+
url = f"{url}?filter={json.dumps(filter)}"
|
|
322
|
+
|
|
323
|
+
return self._make_request("GET", url)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from src.magicfeedback import MagicFeedbackClient
|
|
3
|
+
|
|
4
|
+
def test_create_feedback_with_answer_wrapping(client):
|
|
5
|
+
"""Tests creating a new feedback item and ensures answers.value is wrapped in a list if not already."""
|
|
6
|
+
|
|
7
|
+
feedback_data = {
|
|
8
|
+
"name": "Test SDK Feedback",
|
|
9
|
+
"type": "APP",
|
|
10
|
+
"identity": "MAGICFORM",
|
|
11
|
+
"answers": [
|
|
12
|
+
{"key": "name", "value": "John Doe"}, # Single value (should be wrapped)
|
|
13
|
+
{"key": "comment", "value": ["This is a test comment."]} # Already a list
|
|
14
|
+
],
|
|
15
|
+
"questions": [
|
|
16
|
+
{
|
|
17
|
+
"title": "Name",
|
|
18
|
+
"ref": "name",
|
|
19
|
+
"position": 1,
|
|
20
|
+
"type": "TEXT"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"title": "Comment",
|
|
24
|
+
"ref": "comment",
|
|
25
|
+
"position": 2,
|
|
26
|
+
"type": "LONGTEXT"
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
"integrationId": "0eb9d270-6dd7-11ef-9987-21e04f383573",
|
|
30
|
+
"companyId": "MAGICFEEDBACK_DEV_SDK",
|
|
31
|
+
"productId": "MAGICFEEDBACK_DEV_SDK_GENERAL",
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
response = client.create_feedback(feedback_data)
|
|
35
|
+
|
|
36
|
+
assert "id" in response
|
|
37
|
+
# Check if the created feedback has the correct name
|
|
38
|
+
assert response["name"] == "Test SDK Feedback"
|
|
39
|
+
|
|
40
|
+
# Validate answers are properly wrapped
|
|
41
|
+
for answer in feedback_data["answers"]:
|
|
42
|
+
assert isinstance(answer["value"], list), f"Answer value for key '{answer['key']}' is not a list."
|
|
43
|
+
|
|
44
|
+
@pytest.fixture
|
|
45
|
+
def client():
|
|
46
|
+
"""Provides a MagicFeedbackClient instance for testing."""
|
|
47
|
+
|
|
48
|
+
client = MagicFeedbackClient('sdk_tester@magicfeedback.io', 'caracter')
|
|
49
|
+
return client
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|