vc-utils 2.0__tar.gz → 3.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vc-utils
3
- Version: 2.0
3
+ Version: 3.0
4
4
  Summary: Vulcan Coalition Python Utility Library
5
5
  Home-page: https://github.com/vulcan-coalition/vulcan-utils
6
6
  Author: Chatavut Viriyasuthee
@@ -35,7 +35,7 @@ Vulcan python utility
35
35
  ## Installation
36
36
 
37
37
  ```bash
38
- pip install vulcan-utils
38
+ pip install vc-utils
39
39
  ```
40
40
 
41
41
  ## Usage
@@ -48,12 +48,13 @@ export LINKAGE_CLIENT_ID="test-client-id"
48
48
  export LINKAGE_CLIENT_SECRET="test-client-secret"
49
49
 
50
50
  export LINKAGE_M2M_HOST="https://application-linkage.vulcanproduct.com"
51
+ export LINKAGE_U2M_HOST="https://account-linkage.vulcanproduct.com"
51
52
  ```
52
53
 
53
54
  Then you can import and use the Linkage API in your code:
54
55
 
55
56
  ```python
56
- from vulcan_utils import linkage
57
+ import linkage
57
58
  ```
58
59
 
59
60
  ## Test
@@ -11,7 +11,7 @@ Vulcan python utility
11
11
  ## Installation
12
12
 
13
13
  ```bash
14
- pip install vulcan-utils
14
+ pip install vc-utils
15
15
  ```
16
16
 
17
17
  ## Usage
@@ -24,12 +24,13 @@ export LINKAGE_CLIENT_ID="test-client-id"
24
24
  export LINKAGE_CLIENT_SECRET="test-client-secret"
25
25
 
26
26
  export LINKAGE_M2M_HOST="https://application-linkage.vulcanproduct.com"
27
+ export LINKAGE_U2M_HOST="https://account-linkage.vulcanproduct.com"
27
28
  ```
28
29
 
29
30
  Then you can import and use the Linkage API in your code:
30
31
 
31
32
  ```python
32
- from vulcan_utils import linkage
33
+ import linkage
33
34
  ```
34
35
 
35
36
  ## Test
@@ -1 +1,2 @@
1
1
  from .session import init, Linkage_Application, get_user_data, get_authn_req_url, get_token, revoke
2
+ from .achievement import Achievement
@@ -0,0 +1,9 @@
1
+ from pydantic import BaseModel
2
+ from typing import Any, List, Optional
3
+
4
+
5
+ class Achievement(BaseModel):
6
+ title: str
7
+ description: str
8
+ metadata: Any = None
9
+ files: Optional[List[str]] = None # paths to files to be uploaded
@@ -3,6 +3,7 @@ import logging
3
3
  from . import key_generator
4
4
  import aiohttp
5
5
  from urllib.parse import urlencode
6
+ from .achievement import Achievement
6
7
 
7
8
 
8
9
  AUTHORIZE_ENDPOINT = "/authorize"
@@ -14,6 +15,7 @@ CLIENT_ID = None
14
15
  CLIENT_SECRET = None
15
16
 
16
17
  LINKAGE_M2M_HOST = None
18
+ LINKAGE_U2M_HOST = None
17
19
 
18
20
  async def init():
19
21
  LINKAGE_OPENID_CONF_PATH = os.getenv("LINKAGE_OPENID_CONF_PATH", None)
@@ -46,6 +48,11 @@ async def init():
46
48
  if not LINKAGE_M2M_HOST:
47
49
  raise Exception("LINKAGE_M2M_HOST environment variable must be set")
48
50
 
51
+ global LINKAGE_U2M_HOST
52
+ LINKAGE_U2M_HOST = os.getenv("LINKAGE_U2M_HOST", None)
53
+ if not LINKAGE_U2M_HOST:
54
+ raise Exception("LINKAGE_U2M_HOST environment variable must be set")
55
+
49
56
 
50
57
  async def application_get_token():
51
58
  # use separate session to avoid token refresh logic in Linkage_Application_Session
@@ -120,10 +127,91 @@ class Linkage_Application:
120
127
  error_data = await resp.json()
121
128
  raise Exception(error_data)
122
129
  return await resp.json()
130
+
123
131
 
132
+ async def update_achievement(self, session, user_id, achievement):
133
+ """
134
+ POST /achievements/user/{user_id}
135
+ title: str = Form(...),
136
+ description: str = Form(...),
137
+ metadata: Any = Form(None),
138
+ files: List[UploadFile] = File([]),
139
+ """
140
+ uri = f"{LINKAGE_M2M_HOST}/achievements/user/{user_id}"
141
+ opened_files = []
142
+ try:
143
+ data = aiohttp.FormData()
144
+ data.add_field("title", achievement.title)
145
+ data.add_field("description", achievement.description)
146
+ if achievement.metadata is not None:
147
+ data.add_field("metadata", str(achievement.metadata))
148
+ if achievement.files:
149
+ for file_path in achievement.files:
150
+ f = open(file_path, "rb")
151
+ opened_files.append(f)
152
+ data.add_field("files", f, filename=os.path.basename(file_path))
153
+ resp = await self.linkage_post(session, uri, data=data)
154
+ if resp.status != 200:
155
+ error_data = await resp.json()
156
+ raise Exception(error_data)
157
+ except Exception as e:
158
+ raise e
159
+ finally:
160
+ for f in opened_files:
161
+ f.close()
162
+
163
+ return await resp.json()
164
+
124
165
 
166
+ async def batch_read_achievements(self, session, user_ids, app_ids, expiration=300):
167
+ """
168
+ POST /achievements/batch
125
169
 
126
- async def get_user_data(token):
170
+ user_ids: List[str] = Body(..., embed=True),
171
+ app_ids: List[str] = Body(..., embed=True),
172
+ expiration: int = Query(300, description="Expiration time for signed URLs in seconds"),
173
+
174
+ """
175
+ uri = f"{LINKAGE_M2M_HOST}/achievements/batch?expiration={expiration}"
176
+ payload = {
177
+ "user_ids": user_ids,
178
+ "app_ids": app_ids,
179
+ }
180
+ resp = await self.linkage_post(session, uri, json=payload)
181
+ if resp.status != 200:
182
+ error_data = await resp.json()
183
+ raise Exception(error_data)
184
+
185
+ # convert the response to a dict of user_id -> list of achievements
186
+ # data has this format: {user_id: {"s": status, "achievements": {"app_id": {"s": status, "data": {title, description, metadata, files}}}}}
187
+ data = await resp.json()
188
+ result = {}
189
+ for user_id, value in data.items():
190
+ if value.get("s") != "ok":
191
+ logging.warning(f"Failed to fetch achievements for user {user_id}, status: {value.get('s')}")
192
+ continue
193
+
194
+ achs = []
195
+ for app_id, ach_value in value.get("achievements", {}).items():
196
+ if app_id not in app_ids:
197
+ logging.warning(f"Received achievement data for unexpected app_id {app_id}, skipping")
198
+ continue
199
+ if ach_value.get("s") != "ok":
200
+ logging.warning(f"Failed to fetch achievement for user {user_id} and app {app_id}, status: {ach_value.get('s')}")
201
+ continue
202
+ ach_data = ach_value.get("data", {})
203
+ ach = Achievement(
204
+ title=ach_data.get("title", ""),
205
+ description=ach_data.get("description", ""),
206
+ metadata=ach_data.get("metadata", None),
207
+ files=[d['url_download'] for d in ach_data.get("download_links", [])]
208
+ )
209
+ achs.append(ach)
210
+ result[user_id] = achs
211
+ return result
212
+
213
+
214
+ async def get_user_data(token, spec_id=None):
127
215
  if not token:
128
216
  return None
129
217
 
@@ -131,11 +219,18 @@ async def get_user_data(token):
131
219
  "Authorization": f"Bearer {token}"
132
220
  }
133
221
  async with aiohttp.ClientSession() as session:
134
- async with session.get(USERINFO_ENDPOINT, headers=headers) as resp:
135
- if resp.status != 200:
136
- error_data = await resp.json()
137
- raise Exception(error_data)
138
- return await resp.json()
222
+ if spec_id is not None:
223
+ async with session.get(f"{LINKAGE_U2M_HOST}/data/{spec_id}", headers=headers) as resp:
224
+ if resp.status != 200:
225
+ error_data = await resp.json()
226
+ raise Exception(error_data)
227
+ return await resp.json()
228
+ else:
229
+ async with session.get(USERINFO_ENDPOINT, headers=headers) as resp:
230
+ if resp.status != 200:
231
+ error_data = await resp.json()
232
+ raise Exception(error_data)
233
+ return await resp.json()
139
234
 
140
235
 
141
236
  def get_authn_req_url(scope, redirect_uri):
@@ -11,7 +11,7 @@ import shutil
11
11
 
12
12
  setup(
13
13
  name="vc-utils",
14
- version="2.0",
14
+ version="3.0",
15
15
  author="Chatavut Viriyasuthee",
16
16
  author_email="chatavut@lab.ai",
17
17
  description="Vulcan Coalition Python Utility Library",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vc-utils
3
- Version: 2.0
3
+ Version: 3.0
4
4
  Summary: Vulcan Coalition Python Utility Library
5
5
  Home-page: https://github.com/vulcan-coalition/vulcan-utils
6
6
  Author: Chatavut Viriyasuthee
@@ -35,7 +35,7 @@ Vulcan python utility
35
35
  ## Installation
36
36
 
37
37
  ```bash
38
- pip install vulcan-utils
38
+ pip install vc-utils
39
39
  ```
40
40
 
41
41
  ## Usage
@@ -48,12 +48,13 @@ export LINKAGE_CLIENT_ID="test-client-id"
48
48
  export LINKAGE_CLIENT_SECRET="test-client-secret"
49
49
 
50
50
  export LINKAGE_M2M_HOST="https://application-linkage.vulcanproduct.com"
51
+ export LINKAGE_U2M_HOST="https://account-linkage.vulcanproduct.com"
51
52
  ```
52
53
 
53
54
  Then you can import and use the Linkage API in your code:
54
55
 
55
56
  ```python
56
- from vulcan_utils import linkage
57
+ import linkage
57
58
  ```
58
59
 
59
60
  ## Test
@@ -1,6 +1,7 @@
1
1
  README.md
2
2
  setup.py
3
3
  linkage/__init__.py
4
+ linkage/achievement.py
4
5
  linkage/key_generator.py
5
6
  linkage/session.py
6
7
  vc_utils.egg-info/PKG-INFO
File without changes
File without changes