brynq-sdk-zoho 2.0.4__tar.gz → 3.1.0__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.
- {brynq_sdk_zoho-2.0.4 → brynq_sdk_zoho-3.1.0}/PKG-INFO +1 -1
- {brynq_sdk_zoho-2.0.4 → brynq_sdk_zoho-3.1.0}/brynq_sdk_zoho/extract_zoho_desk.py +93 -9
- brynq_sdk_zoho-3.1.0/brynq_sdk_zoho/upload_zoho_desk.py +121 -0
- {brynq_sdk_zoho-2.0.4 → brynq_sdk_zoho-3.1.0}/brynq_sdk_zoho.egg-info/PKG-INFO +1 -1
- {brynq_sdk_zoho-2.0.4 → brynq_sdk_zoho-3.1.0}/setup.py +1 -1
- brynq_sdk_zoho-2.0.4/brynq_sdk_zoho/upload_zoho_desk.py +0 -50
- {brynq_sdk_zoho-2.0.4 → brynq_sdk_zoho-3.1.0}/brynq_sdk_zoho/__init__.py +0 -0
- {brynq_sdk_zoho-2.0.4 → brynq_sdk_zoho-3.1.0}/brynq_sdk_zoho.egg-info/SOURCES.txt +0 -0
- {brynq_sdk_zoho-2.0.4 → brynq_sdk_zoho-3.1.0}/brynq_sdk_zoho.egg-info/dependency_links.txt +0 -0
- {brynq_sdk_zoho-2.0.4 → brynq_sdk_zoho-3.1.0}/brynq_sdk_zoho.egg-info/not-zip-safe +0 -0
- {brynq_sdk_zoho-2.0.4 → brynq_sdk_zoho-3.1.0}/brynq_sdk_zoho.egg-info/requires.txt +0 -0
- {brynq_sdk_zoho-2.0.4 → brynq_sdk_zoho-3.1.0}/brynq_sdk_zoho.egg-info/top_level.txt +0 -0
- {brynq_sdk_zoho-2.0.4 → brynq_sdk_zoho-3.1.0}/setup.cfg +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sys
|
|
3
3
|
import pandas as pd
|
|
4
|
-
from typing import Union, List
|
|
4
|
+
from typing import Union, List, Optional, Literal
|
|
5
5
|
import requests
|
|
6
6
|
import json
|
|
7
7
|
from brynq_sdk_brynq import BrynQ
|
|
@@ -12,29 +12,28 @@ sys.path.append(basedir)
|
|
|
12
12
|
|
|
13
13
|
class ExtractZohoDesk(BrynQ):
|
|
14
14
|
|
|
15
|
-
def __init__(self,
|
|
15
|
+
def __init__(self, system_type: Optional[Literal['source', 'target']] = None, debug: bool = False):
|
|
16
16
|
"""
|
|
17
17
|
For the full documentation, see: https://avisi-apps.gitbook.io/tracket/api/
|
|
18
18
|
"""
|
|
19
19
|
super().__init__()
|
|
20
|
-
self.headers = self._get_authentication(
|
|
20
|
+
self.headers = self._get_authentication(system_type)
|
|
21
21
|
self.base_url = "https://desk.zoho.com/api/v1/"
|
|
22
22
|
self.payload = {}
|
|
23
23
|
self.timeout = 3600
|
|
24
24
|
|
|
25
|
-
def _get_authentication(self,
|
|
25
|
+
def _get_authentication(self, system_type):
|
|
26
26
|
"""
|
|
27
27
|
Get the credentials for the Tracket API from BrynQ, with those credentials, get the access_token for Tracket.
|
|
28
28
|
Return the headers with the access_token.
|
|
29
29
|
"""
|
|
30
30
|
# Get credentials from BrynQ
|
|
31
|
-
credentials = self.
|
|
31
|
+
credentials = self.interfaces.credentials.get(system="zoho-desk", system_type=system_type)
|
|
32
|
+
credentials = credentials.get('data')
|
|
32
33
|
|
|
33
34
|
# With those credentials, get the access_token from Tracket
|
|
34
|
-
zoho_system_id = credentials["id"]
|
|
35
|
-
token = BrynQ().refresh_system_credential(system="zoho-desk", system_id=zoho_system_id)["access_token"]
|
|
36
35
|
headers = {
|
|
37
|
-
'Authorization': f'Zoho-oauthtoken {
|
|
36
|
+
'Authorization': f'Zoho-oauthtoken {credentials.get("access_token")}'
|
|
38
37
|
}
|
|
39
38
|
return headers
|
|
40
39
|
|
|
@@ -141,7 +140,7 @@ class ExtractZohoDesk(BrynQ):
|
|
|
141
140
|
"""
|
|
142
141
|
df = pd.DataFrame()
|
|
143
142
|
end_of_loop = False
|
|
144
|
-
offset =
|
|
143
|
+
offset = 1
|
|
145
144
|
while not end_of_loop:
|
|
146
145
|
url = f"{base_url}?from={offset}&limit=90&{query_params}"
|
|
147
146
|
df_temp = self._single_call(url)
|
|
@@ -171,3 +170,88 @@ class ExtractZohoDesk(BrynQ):
|
|
|
171
170
|
return pd.DataFrame()
|
|
172
171
|
else:
|
|
173
172
|
raise Exception(response.text)
|
|
173
|
+
|
|
174
|
+
def get_zoho_articles(self, exclude_category_ids: list = []) -> list:
|
|
175
|
+
"""
|
|
176
|
+
Retrieves all Zoho articles, excluding articles with specified category ID
|
|
177
|
+
Category ID's can be easily found in your KB-Admin in the link of any article.
|
|
178
|
+
"""
|
|
179
|
+
limit = 50
|
|
180
|
+
offset = 1
|
|
181
|
+
all_articles = []
|
|
182
|
+
|
|
183
|
+
while True:
|
|
184
|
+
url = f'{self.base_url}articles'
|
|
185
|
+
params = {"status": "Draft,Published", "from": offset, "limit": limit}
|
|
186
|
+
response = requests.get(url, headers=self.headers, params=params, timeout=120)
|
|
187
|
+
if response.status_code == 200:
|
|
188
|
+
articles = response.json().get('data', [])
|
|
189
|
+
if not articles:
|
|
190
|
+
break
|
|
191
|
+
|
|
192
|
+
# Filter out articles with the excluded category ID
|
|
193
|
+
filtered_articles = []
|
|
194
|
+
for article in articles:
|
|
195
|
+
article_category_id = article.get('categoryId')
|
|
196
|
+
locale = article.get('locale', "")
|
|
197
|
+
if article_category_id not in exclude_category_ids and locale == "nl":
|
|
198
|
+
filtered_articles.append(article)
|
|
199
|
+
|
|
200
|
+
all_articles.extend(filtered_articles)
|
|
201
|
+
|
|
202
|
+
if len(articles) < limit:
|
|
203
|
+
break
|
|
204
|
+
else:
|
|
205
|
+
error_text = response.text
|
|
206
|
+
all_articles.insert("Failed to retrieve Zoho articles, status: {response.status_code}, body: {error_text}")
|
|
207
|
+
offset += limit
|
|
208
|
+
return all_articles
|
|
209
|
+
|
|
210
|
+
def get_base_article_by_id(self, article_id: str) -> dict:
|
|
211
|
+
"""
|
|
212
|
+
Retrieves the base (non-versioned) Zoho article by ID.
|
|
213
|
+
"""
|
|
214
|
+
try:
|
|
215
|
+
detail_url = f"{self.base_url}articles/{article_id}"
|
|
216
|
+
response = requests.get(detail_url, headers=self.headers, timeout=60)
|
|
217
|
+
if response.status_code == 200:
|
|
218
|
+
return response.json()
|
|
219
|
+
message = f"Failed to retrieve base article for article ID: {article_id}, status: {response.status_code}"
|
|
220
|
+
return message
|
|
221
|
+
except Exception as e:
|
|
222
|
+
message = f"Error getting base article by id: {e}"
|
|
223
|
+
return message
|
|
224
|
+
|
|
225
|
+
def get_zoho_article_latest(self, zoho_article: dict) -> dict:
|
|
226
|
+
"""
|
|
227
|
+
Retrieves detailed article data from Zoho Desk using the base article object.
|
|
228
|
+
Uses the article's id and, if available, its latestVersion to fetch the right version.
|
|
229
|
+
"""
|
|
230
|
+
try:
|
|
231
|
+
article_id = zoho_article.get("id")
|
|
232
|
+
if not article_id:
|
|
233
|
+
message = "Error: No article id provided in base article object."
|
|
234
|
+
return message
|
|
235
|
+
|
|
236
|
+
latest_version = zoho_article.get("latestVersion")
|
|
237
|
+
|
|
238
|
+
if latest_version:
|
|
239
|
+
versioned_article_url = f"{self.base_url}articles/{article_id}?version={latest_version}"
|
|
240
|
+
version_response = requests.get(versioned_article_url, headers=self.headers, timeout=60)
|
|
241
|
+
if version_response.status_code == 200:
|
|
242
|
+
return version_response.json()
|
|
243
|
+
message = f"Failed to retrieve versioned article data, status: {version_response.status_code}"
|
|
244
|
+
return message
|
|
245
|
+
|
|
246
|
+
detail_url = f"{self.base_url}articles/{article_id}"
|
|
247
|
+
response = requests.get(detail_url, headers=self.headers, timeout=60)
|
|
248
|
+
if response.status_code == 200:
|
|
249
|
+
article_data = response.json()
|
|
250
|
+
print(f"Retrieved detailed article data for article ID: {article_id}")
|
|
251
|
+
return article_data
|
|
252
|
+
else:
|
|
253
|
+
message = f"Failed to retrieve versioned article data, status: {version_response.status_code}"
|
|
254
|
+
return message
|
|
255
|
+
except Exception as e:
|
|
256
|
+
message = "Retrieving latest Zoho Article Failed."
|
|
257
|
+
return message
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from typing import Union, List, Optional, Literal
|
|
5
|
+
import requests
|
|
6
|
+
import json
|
|
7
|
+
import re
|
|
8
|
+
from brynq_sdk_brynq import BrynQ
|
|
9
|
+
basedir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
10
|
+
sys.path.append(basedir)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class UploadZohoDesk(BrynQ):
|
|
14
|
+
|
|
15
|
+
def __init__(self, system_type: Optional[Literal['source', 'target']] = None, debug: bool = False):
|
|
16
|
+
"""
|
|
17
|
+
For the full documentation, see: https://avisi-apps.gitbook.io/tracket/api/
|
|
18
|
+
"""
|
|
19
|
+
super().__init__()
|
|
20
|
+
self.headers = self._get_authentication(system_type)
|
|
21
|
+
self.base_url = "https://desk.zoho.com/api/v1/"
|
|
22
|
+
self.timeout = 3600
|
|
23
|
+
|
|
24
|
+
def _get_authentication(self, system_type):
|
|
25
|
+
"""
|
|
26
|
+
Get the credentials for the Traket API from BrynQ, with those credentials, get the access_token for Tracket.
|
|
27
|
+
Return the headers with the access_token.
|
|
28
|
+
"""
|
|
29
|
+
# Get credentials from BrynQ
|
|
30
|
+
credentials = self.interfaces.credentials.get(system="zoho-desk", system_type=system_type)
|
|
31
|
+
credentials = credentials.get('data')
|
|
32
|
+
|
|
33
|
+
headers = {
|
|
34
|
+
'Authorization': f'Zoho-oauthtoken {credentials.get("access_token")}',
|
|
35
|
+
'Content-Type': 'application/json'
|
|
36
|
+
}
|
|
37
|
+
return headers
|
|
38
|
+
|
|
39
|
+
def update_ticket_time_entry(self, ticket_id, time_entry_id, payload):
|
|
40
|
+
"""
|
|
41
|
+
This function updates the time entry of a ticket in zoho desk
|
|
42
|
+
:param ticket_id: str
|
|
43
|
+
:param time_entry_id: str
|
|
44
|
+
:param payload: dict
|
|
45
|
+
"""
|
|
46
|
+
url = f"{self.base_url}tickets/{ticket_id}/timeEntry/{time_entry_id}"
|
|
47
|
+
response = requests.request("PATCH", url, headers=self.headers, data=json.dumps(payload), timeout=self.timeout)
|
|
48
|
+
return response
|
|
49
|
+
|
|
50
|
+
def update_article(self, article_id: str, updated_content: str, category_id: int, status="Draft") -> str:
|
|
51
|
+
"""
|
|
52
|
+
Updates the Zoho article with the updated content as a draft.
|
|
53
|
+
Returns the web URL where the draft can be reviewed, or an empty string if the update fails.
|
|
54
|
+
|
|
55
|
+
Status can be:
|
|
56
|
+
- Draft
|
|
57
|
+
- Published
|
|
58
|
+
- Review
|
|
59
|
+
- Unpublished
|
|
60
|
+
"""
|
|
61
|
+
try:
|
|
62
|
+
|
|
63
|
+
payload = {
|
|
64
|
+
"answer": updated_content,
|
|
65
|
+
"status": status
|
|
66
|
+
}
|
|
67
|
+
if category_id is not None:
|
|
68
|
+
payload["categoryId"] = category_id
|
|
69
|
+
update_url = f"{self.base_url}articles/{article_id}"
|
|
70
|
+
update_headers = self.headers
|
|
71
|
+
|
|
72
|
+
update_response = requests.patch(update_url, headers=update_headers, data=json.dumps(payload), timeout=60)
|
|
73
|
+
if update_response.status_code == 200:
|
|
74
|
+
update_data = update_response.json()
|
|
75
|
+
web_url = update_data.get("webUrl", "")
|
|
76
|
+
return web_url
|
|
77
|
+
else:
|
|
78
|
+
message = f"Uploading Draft failed. Response: {update_response.status_code}"
|
|
79
|
+
return message
|
|
80
|
+
except Exception as e:
|
|
81
|
+
message = "Uploading Draft failed."
|
|
82
|
+
return message
|
|
83
|
+
|
|
84
|
+
def upload_translation(self, translated, article_data, locale = "en-us"):
|
|
85
|
+
try:
|
|
86
|
+
article_id = article_data.get("id")
|
|
87
|
+
title = article_data.get("title")
|
|
88
|
+
|
|
89
|
+
url = f"{self.base_url}articles/{article_id}/translations"
|
|
90
|
+
|
|
91
|
+
# Get authorId from the article data or use a default
|
|
92
|
+
author_id = article_data.get("authorId") or article_data.get("createdBy", {}).get("id")
|
|
93
|
+
payload = {
|
|
94
|
+
"title" : title,
|
|
95
|
+
"answer" : translated,
|
|
96
|
+
"status" : "Draft",
|
|
97
|
+
"locale" : locale,
|
|
98
|
+
"authorId" : author_id
|
|
99
|
+
}
|
|
100
|
+
response = requests.post(url, headers=self.headers, data=json.dumps(payload), timeout=60)
|
|
101
|
+
if response.status_code == 200:
|
|
102
|
+
update_data = response.json()
|
|
103
|
+
web_url = update_data.get("webUrl", "")
|
|
104
|
+
return web_url
|
|
105
|
+
else:
|
|
106
|
+
url = f"{self.base_url}articles/{article_id}/translations/{locale}"
|
|
107
|
+
payload = {
|
|
108
|
+
"answer" : translated,
|
|
109
|
+
"status" : "Draft"
|
|
110
|
+
}
|
|
111
|
+
response = requests.patch(url, headers=self.headers, data=json.dumps(payload), timeout=60)
|
|
112
|
+
if response.status_code == 200:
|
|
113
|
+
update_data = response.json()
|
|
114
|
+
web_url = update_data.get("webUrl", "")
|
|
115
|
+
return web_url
|
|
116
|
+
else:
|
|
117
|
+
message = "Uploading Translation failed."
|
|
118
|
+
return message
|
|
119
|
+
except Exception as e:
|
|
120
|
+
message = "Uploading Translation failed."
|
|
121
|
+
return message
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import sys
|
|
3
|
-
import pandas as pd
|
|
4
|
-
from typing import Union, List
|
|
5
|
-
import requests
|
|
6
|
-
import json
|
|
7
|
-
from brynq_sdk_brynq import BrynQ
|
|
8
|
-
|
|
9
|
-
basedir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
10
|
-
sys.path.append(basedir)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class UploadZohoDesk(BrynQ):
|
|
14
|
-
|
|
15
|
-
def __init__(self, label: Union[str, List], debug: bool = False):
|
|
16
|
-
"""
|
|
17
|
-
For the full documentation, see: https://avisi-apps.gitbook.io/tracket/api/
|
|
18
|
-
"""
|
|
19
|
-
super().__init__()
|
|
20
|
-
self.headers = self._get_authentication(label=label)
|
|
21
|
-
self.base_url = "https://desk.zoho.com/api/v1/"
|
|
22
|
-
self.timeout = 3600
|
|
23
|
-
|
|
24
|
-
def _get_authentication(self, label):
|
|
25
|
-
"""
|
|
26
|
-
Get the credentials for the Traket API from BrynQ, with those credentials, get the access_token for Tracket.
|
|
27
|
-
Return the headers with the access_token.
|
|
28
|
-
"""
|
|
29
|
-
# Get credentials from BrynQ
|
|
30
|
-
credentials = self.get_system_credential(system='zoho-desk', label=label)
|
|
31
|
-
|
|
32
|
-
# With those credentials, get the access_token from Tracket
|
|
33
|
-
zoho_system_id = credentials["id"]
|
|
34
|
-
token = BrynQ().refresh_system_credential(system="zoho-desk", system_id=zoho_system_id)["access_token"]
|
|
35
|
-
headers = {
|
|
36
|
-
'Authorization': f'Zoho-oauthtoken {token}',
|
|
37
|
-
'Content-Type': 'application/json'
|
|
38
|
-
}
|
|
39
|
-
return headers
|
|
40
|
-
|
|
41
|
-
def update_ticket_time_entry(self, ticket_id, time_entry_id, payload):
|
|
42
|
-
"""
|
|
43
|
-
This function updates the time entry of a ticket in zoho desk
|
|
44
|
-
:param ticket_id: str
|
|
45
|
-
:param time_entry_id: str
|
|
46
|
-
:param payload: dict
|
|
47
|
-
"""
|
|
48
|
-
url = f"{self.base_url}tickets/{ticket_id}/timeEntry/{time_entry_id}"
|
|
49
|
-
response = requests.request("PATCH", url, headers=self.headers, data=json.dumps(payload), timeout=self.timeout)
|
|
50
|
-
return response
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|