eclinical-requester 1.0.0__py3-none-any.whl
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.
- api_requester/__init__.py +9 -0
- api_requester/core/admin/api/auth_api.py +34 -0
- api_requester/core/admin/api/user_on_board_application_api.py +31 -0
- api_requester/core/admin/service/auth_service.py +34 -0
- api_requester/core/admin/service/impl/system_env_service_impl.py +39 -0
- api_requester/core/admin/service/impl/user_on_board_application_service_impl.py +23 -0
- api_requester/core/admin/service/user_on_board_application_service.py +35 -0
- api_requester/core/call_api.py +64 -0
- api_requester/core/common/api/system_env_api.py +24 -0
- api_requester/core/common/service/system_env_service.py +27 -0
- api_requester/docs/application.yaml +46 -0
- api_requester/dto/admin/cross_user_user_on_board_dto.py +20 -0
- api_requester/dto/admin/jwt_authentication_request.py +20 -0
- api_requester/dto/admin/user_on_board_dto.py +29 -0
- api_requester/dto/base_dto.py +167 -0
- api_requester/dto/biz_base.py +31 -0
- api_requester/dto/user.py +39 -0
- api_requester/http/app_url.py +31 -0
- api_requester/http/authorize.py +156 -0
- api_requester/http/eclinical_requests.py +264 -0
- api_requester/http/exceptions.py +72 -0
- api_requester/http/gateway.py +105 -0
- api_requester/http/sample_headers.py +23 -0
- api_requester/http/token_manager.py +30 -0
- api_requester/utils/constant.py +108 -0
- api_requester/utils/json_utils.py +83 -0
- api_requester/utils/lib.py +456 -0
- api_requester/utils/log.py +91 -0
- api_requester/utils/path.py +21 -0
- api_requester/utils/placeholder_replacer.py +22 -0
- api_requester/utils/read_file.py +94 -0
- api_requester/utils/rsa.py +36 -0
- api_requester/utils/time_utils.py +27 -0
- eclinical_requester-1.0.0.dist-info/LICENSE +19 -0
- eclinical_requester-1.0.0.dist-info/METADATA +19 -0
- eclinical_requester-1.0.0.dist-info/RECORD +38 -0
- eclinical_requester-1.0.0.dist-info/WHEEL +5 -0
- eclinical_requester-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
# !/usr/bin/python3
|
2
|
+
# -*- coding:utf-8 -*-
|
3
|
+
"""
|
4
|
+
@Author: xiaodong.li
|
5
|
+
@Time: 2024/04/26 19:01
|
6
|
+
@Description: Description
|
7
|
+
@File: auth_api.py
|
8
|
+
"""
|
9
|
+
from api_requester.http.eclinical_requests import *
|
10
|
+
|
11
|
+
|
12
|
+
class AdminAuthApi:
|
13
|
+
"""
|
14
|
+
The AdminAuthApi was generated by Generator from the AuthController.java file.
|
15
|
+
"""
|
16
|
+
|
17
|
+
@post('/auth')
|
18
|
+
def create_authentication_token_api(self, *args, **kwargs): ...
|
19
|
+
|
20
|
+
@get('/refresh')
|
21
|
+
def refresh_and_get_authentication_token_api(self, *args, **kwargs): ...
|
22
|
+
|
23
|
+
@get('/auth/logout')
|
24
|
+
def logout_api(self, *args, **kwargs): ...
|
25
|
+
|
26
|
+
@get('/auth/all/logout')
|
27
|
+
def logout_all_api(self, *args, **kwargs): ...
|
28
|
+
|
29
|
+
@get('/me')
|
30
|
+
def me_api(self, *args, **kwargs): ...
|
31
|
+
|
32
|
+
@post('/auth/sso/token')
|
33
|
+
def auth_sso_token_api(self, *args, **kwargs): ...
|
34
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# !/usr/bin/python3
|
2
|
+
# -*- coding:utf-8 -*-
|
3
|
+
"""
|
4
|
+
@Author: xiaodong.li
|
5
|
+
@Time: 2024/04/24 20:45
|
6
|
+
@Description: Description
|
7
|
+
@File: user_on_board_application_api.py
|
8
|
+
"""
|
9
|
+
from api_requester.http.eclinical_requests import *
|
10
|
+
|
11
|
+
|
12
|
+
class AdminUserOnBoardApplicationApi:
|
13
|
+
"""
|
14
|
+
The AdminUserOnBoardApplicationApi was generated by Generator from the UserOnBoardApplicationController.java file.
|
15
|
+
"""
|
16
|
+
|
17
|
+
@get('/user/onboard/company')
|
18
|
+
def get_company_list_api(self, *args, **kwargs): ...
|
19
|
+
|
20
|
+
@get('/user/onboard/applications')
|
21
|
+
def get_user_onboard_application_list_api(self, *args, **kwargs): ...
|
22
|
+
|
23
|
+
@post('/user/onboard')
|
24
|
+
def on_board_api(self, *args, **kwargs): ...
|
25
|
+
|
26
|
+
@get('/user/entry/portal')
|
27
|
+
def entry_portal_api(self, *args, **kwargs): ...
|
28
|
+
|
29
|
+
@post('/user/entry/portal')
|
30
|
+
def login_portal_api(self, *args, **kwargs): ...
|
31
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# !/usr/bin/python3
|
2
|
+
# -*- coding:utf-8 -*-
|
3
|
+
"""
|
4
|
+
@Author: xiaodong.li
|
5
|
+
@Time: 2024/09/06 14:40
|
6
|
+
@Description: Description
|
7
|
+
@File: auth_service.py
|
8
|
+
"""
|
9
|
+
from api_requester.core.admin.api.auth_api import AdminAuthApi
|
10
|
+
from api_requester.dto.admin.jwt_authentication_request import JwtAuthenticationRequest
|
11
|
+
|
12
|
+
|
13
|
+
class AdminAuthService(AdminAuthApi):
|
14
|
+
"""
|
15
|
+
The AdminAuthService was generated by Generator from the AuthController.java file.
|
16
|
+
"""
|
17
|
+
|
18
|
+
def __init__(self):
|
19
|
+
AdminAuthApi.__init__(self)
|
20
|
+
|
21
|
+
def create_authentication_token(self, authentication_request: JwtAuthenticationRequest = None):
|
22
|
+
return self.create_authentication_token_api(json=authentication_request)
|
23
|
+
|
24
|
+
def refresh_and_get_authentication_token(self):
|
25
|
+
return self.refresh_and_get_authentication_token_api()
|
26
|
+
|
27
|
+
def logout(self):
|
28
|
+
return self.logout_api()
|
29
|
+
|
30
|
+
def logout_all(self, login_name: str = None):
|
31
|
+
return self.logout_all_api(params=dict(loginName=login_name))
|
32
|
+
|
33
|
+
def me(self):
|
34
|
+
return self.me_api()
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# !/usr/bin/python3
|
2
|
+
# -*- coding:utf-8 -*-
|
3
|
+
"""
|
4
|
+
@Author: xiaodong.li
|
5
|
+
@Time: 4/29/2024 2:41 PM
|
6
|
+
@Description: Description
|
7
|
+
@File: system_env_service_impl.py
|
8
|
+
"""
|
9
|
+
from api_requester.core.common.service.system_env_service import CommonSystemEnvService
|
10
|
+
from api_requester.http.token_manager import apply_token_to_headers
|
11
|
+
from api_requester.utils.log import Logger
|
12
|
+
|
13
|
+
|
14
|
+
class CommonSystemEnvServiceImpl(CommonSystemEnvService):
|
15
|
+
"""
|
16
|
+
CommonSystemEnvServiceImpl
|
17
|
+
"""
|
18
|
+
|
19
|
+
def __init__(self):
|
20
|
+
CommonSystemEnvService.__init__(self)
|
21
|
+
|
22
|
+
def switch_role(self, role, env=None):
|
23
|
+
payload = self.get_current_system_env()
|
24
|
+
if payload.get("role").get("code") == role or (env is not None and payload.get("env").get("name") == env):
|
25
|
+
return
|
26
|
+
items = self.sponsor_env_list()
|
27
|
+
if not items:
|
28
|
+
return
|
29
|
+
for item in items:
|
30
|
+
if item.get("defaultEnv"):
|
31
|
+
pass
|
32
|
+
if env is None and item.get("defaultEnv") or item.get("name") == env:
|
33
|
+
onboard_role_list = item.get("onboardRoleList")
|
34
|
+
onboard_role = next((role_dto for role_dto in onboard_role_list if role_dto.get("name") == role), None)
|
35
|
+
if onboard_role:
|
36
|
+
self.switch_env(env_id=item.get("id"), role_id=onboard_role.get("id"))
|
37
|
+
apply_token_to_headers(self)
|
38
|
+
Logger().info(f"The user switches the {role} role successfully.")
|
39
|
+
return
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# !/usr/bin/python3
|
2
|
+
# -*- coding:utf-8 -*-
|
3
|
+
"""
|
4
|
+
@Author: xiaodong.li
|
5
|
+
@Time: 2024/03/19 13:55
|
6
|
+
@Description: Description
|
7
|
+
@File: user_on_board_application_service_impl.py
|
8
|
+
"""
|
9
|
+
from api_requester.core.admin.service.user_on_board_application_service import AdminUserOnBoardApplicationService
|
10
|
+
from api_requester.utils.lib import get_val_from_list
|
11
|
+
|
12
|
+
|
13
|
+
class AdminUserOnBoardApplicationServiceImpl(AdminUserOnBoardApplicationService):
|
14
|
+
"""
|
15
|
+
AdminUserOnBoardApplicationServiceImpl
|
16
|
+
"""
|
17
|
+
|
18
|
+
def __init__(self):
|
19
|
+
AdminUserOnBoardApplicationService.__init__(self)
|
20
|
+
|
21
|
+
@get_val_from_list()
|
22
|
+
def get_company_id(self, name=None):
|
23
|
+
return self.get_company_list()
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# !/usr/bin/python3
|
2
|
+
# -*- coding:utf-8 -*-
|
3
|
+
"""
|
4
|
+
@Author: xiaodong.li
|
5
|
+
@Time: 2024/09/06 14:41
|
6
|
+
@Description: Description
|
7
|
+
@File: user_on_board_application_service.py
|
8
|
+
"""
|
9
|
+
from api_requester.core.admin.api.user_on_board_application_api import AdminUserOnBoardApplicationApi
|
10
|
+
from api_requester.dto.admin.cross_user_user_on_board_dto import CrossUserUserOnBoardDto
|
11
|
+
from api_requester.dto.admin.user_on_board_dto import UserOnBoardDto
|
12
|
+
|
13
|
+
|
14
|
+
class AdminUserOnBoardApplicationService(AdminUserOnBoardApplicationApi):
|
15
|
+
"""
|
16
|
+
The AdminUserOnBoardApplicationService was generated by Generator from the UserOnBoardApplicationController.java file.
|
17
|
+
"""
|
18
|
+
|
19
|
+
def __init__(self):
|
20
|
+
AdminUserOnBoardApplicationApi.__init__(self)
|
21
|
+
|
22
|
+
def get_company_list(self):
|
23
|
+
return self.get_company_list_api()
|
24
|
+
|
25
|
+
def get_user_onboard_application_list(self, company_id: int = None):
|
26
|
+
return self.get_user_onboard_application_list_api(params=dict(companyId=company_id))
|
27
|
+
|
28
|
+
def on_board(self, user_on_board_dto: UserOnBoardDto = None):
|
29
|
+
return self.on_board_api(json=user_on_board_dto)
|
30
|
+
|
31
|
+
def entry_portal(self):
|
32
|
+
return self.entry_portal_api()
|
33
|
+
|
34
|
+
def login_portal(self, cross_user_user_on_board_dto: CrossUserUserOnBoardDto = None):
|
35
|
+
return self.login_portal_api(json=cross_user_user_on_board_dto)
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# !/usr/bin/python3
|
2
|
+
# -*- coding:utf-8 -*-
|
3
|
+
"""
|
4
|
+
@Author: xiaodong.li
|
5
|
+
@Time: 12/27/2024 2:59 PM
|
6
|
+
@Description: Description
|
7
|
+
@File: call_api.py
|
8
|
+
"""
|
9
|
+
from requests import request
|
10
|
+
|
11
|
+
from api_requester.dto.admin.user_on_board_dto import UserOnBoardDto
|
12
|
+
from api_requester.dto.biz_base import BizBase
|
13
|
+
from api_requester.dto.user import EClinicalUser
|
14
|
+
from api_requester.http.app_url import AppUrl
|
15
|
+
from api_requester.http.authorize import Authorize
|
16
|
+
from api_requester.utils.placeholder_replacer import PlaceholderReplacer
|
17
|
+
|
18
|
+
|
19
|
+
class CallApi(BizBase):
|
20
|
+
|
21
|
+
def __init__(self, username=None, password=None, sponsor=None, study=None, test_env=None, app_env=None, app=None,
|
22
|
+
company=None, role=None, external=False):
|
23
|
+
u = EClinicalUser(
|
24
|
+
username=username,
|
25
|
+
password=password,
|
26
|
+
sponsor=sponsor,
|
27
|
+
study=study,
|
28
|
+
test_env=test_env,
|
29
|
+
app_env=app_env,
|
30
|
+
app=app,
|
31
|
+
company=company,
|
32
|
+
role=role,
|
33
|
+
external=external
|
34
|
+
)
|
35
|
+
super().__init__(u)
|
36
|
+
self.headers = dict()
|
37
|
+
self.user_onboard_dto: UserOnBoardDto = UserOnBoardDto(-1)
|
38
|
+
|
39
|
+
def login(self):
|
40
|
+
auth = Authorize(self.user)
|
41
|
+
auth.login()
|
42
|
+
self.headers = auth.headers
|
43
|
+
self.time_mills = auth.time_mills
|
44
|
+
self.user_onboard_dto = auth.user_onboard_dto
|
45
|
+
return self
|
46
|
+
|
47
|
+
def run(self, method, api, **kwargs):
|
48
|
+
self.login()
|
49
|
+
replacer = PlaceholderReplacer({
|
50
|
+
"Sponsor ID": self.user_onboard_dto.sponsorId,
|
51
|
+
"Study ID": self.user_onboard_dto.studyId,
|
52
|
+
"Env ID": self.user_onboard_dto.envId,
|
53
|
+
"Sponsor Name": self.user.sponsor,
|
54
|
+
"Study Name": self.user.study,
|
55
|
+
"Lifecycle": self.user.app_env,
|
56
|
+
})
|
57
|
+
url = replacer.replace(api)
|
58
|
+
for key, value in kwargs.items():
|
59
|
+
kwargs[key] = replacer.replace(value)
|
60
|
+
if self.user.external is False:
|
61
|
+
url = AppUrl(self.user.app, self.user.test_env).which_url(self.user.app)(url)
|
62
|
+
else:
|
63
|
+
url = AppUrl(self.user.app, self.user.test_env).external_url(url)
|
64
|
+
request(method, url, headers=self.headers, **kwargs)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# !/usr/bin/python3
|
2
|
+
# -*- coding:utf-8 -*-
|
3
|
+
"""
|
4
|
+
@Author: xiaodong.li
|
5
|
+
@Time: 2024/11/01 17:01
|
6
|
+
@Description: Description
|
7
|
+
@File: system_env_api.py
|
8
|
+
"""
|
9
|
+
from api_requester.http.eclinical_requests import *
|
10
|
+
|
11
|
+
|
12
|
+
class CommonSystemEnvApi:
|
13
|
+
"""
|
14
|
+
The CommonSystemEnvApi was generated by Generator from the SystemEnvController.java file.
|
15
|
+
"""
|
16
|
+
|
17
|
+
@get('/system/env')
|
18
|
+
def get_current_system_env_api(self, *args, **kwargs): ...
|
19
|
+
|
20
|
+
@get('/system/env/list')
|
21
|
+
def sponsor_env_list_api(self, *args, **kwargs): ...
|
22
|
+
|
23
|
+
@put('/system/env/{envId}/role/{roleId}')
|
24
|
+
def switch_env_api(self, *args, **kwargs): ...
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# !/usr/bin/python3
|
2
|
+
# -*- coding:utf-8 -*-
|
3
|
+
"""
|
4
|
+
@Author: xiaodong.li
|
5
|
+
@Time: 2024/11/01 17:01
|
6
|
+
@Description: Description
|
7
|
+
@File: system_env_service.py
|
8
|
+
"""
|
9
|
+
from api_requester.core.common.api.system_env_api import CommonSystemEnvApi
|
10
|
+
|
11
|
+
|
12
|
+
class CommonSystemEnvService(CommonSystemEnvApi):
|
13
|
+
"""
|
14
|
+
The CommonSystemEnvService was generated by Generator from the SystemEnvController.java file.
|
15
|
+
"""
|
16
|
+
|
17
|
+
def __init__(self):
|
18
|
+
CommonSystemEnvApi.__init__(self)
|
19
|
+
|
20
|
+
def get_current_system_env(self):
|
21
|
+
return self.get_current_system_env_api()
|
22
|
+
|
23
|
+
def sponsor_env_list(self):
|
24
|
+
return self.sponsor_env_list_api()
|
25
|
+
|
26
|
+
def switch_env(self, env_id: int, role_id: int):
|
27
|
+
return self.switch_env_api(url_kwargs=dict(envId=env_id, roleId=role_id))
|
@@ -0,0 +1,46 @@
|
|
1
|
+
serverUrl:
|
2
|
+
dev03: http://dev-03-app-01.chengdudev.edetekapps.cn
|
3
|
+
dev04: http://dev-04-app-01.chengdudev.edetekapps.cn
|
4
|
+
dev01: http://dev-01-app-01.chengdudev.edetekapps.cn
|
5
|
+
test01: http://test-01-app-01.chengdudev.edetekapps.cn
|
6
|
+
us.dev: https://ec.eclinical-dev.edetekapps.com
|
7
|
+
us.demo: https://ec4.ec4demo.edetekapps.com
|
8
|
+
|
9
|
+
admin:
|
10
|
+
urlPrefix: /api/admin
|
11
|
+
blacklist:
|
12
|
+
- request /administration/coding/**
|
13
|
+
- request /monitor/{tokenBase64}/api/{monitorServerBase64}/**
|
14
|
+
filters:
|
15
|
+
- RewritePath=/api/external/admin(?P<segment>/?.*),/admin/api/external\g<segment>
|
16
|
+
|
17
|
+
design:
|
18
|
+
urlPrefix: /api/design
|
19
|
+
|
20
|
+
ctms:
|
21
|
+
urlPrefix: /api/ctms
|
22
|
+
filters:
|
23
|
+
- RewritePath=/api/external/facade(?P<segment>/?.*),/api/external\g<segment>
|
24
|
+
|
25
|
+
etmf:
|
26
|
+
urlPrefix: /api/etmf
|
27
|
+
|
28
|
+
edc:
|
29
|
+
urlPrefix: /api/edc
|
30
|
+
filters:
|
31
|
+
- RewritePath=/api/external/facade(?P<segment>/?.*),/api/external/edc\g<segment>
|
32
|
+
|
33
|
+
iwrs:
|
34
|
+
urlPrefix: /api/iwrs
|
35
|
+
|
36
|
+
external:
|
37
|
+
urlPrefix: /api/external
|
38
|
+
|
39
|
+
coding:
|
40
|
+
urlPrefix: /api/coding
|
41
|
+
|
42
|
+
imaging:
|
43
|
+
urlPrefix: /api/imaging
|
44
|
+
|
45
|
+
pv:
|
46
|
+
urlPrefix: /api/pv
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# !/usr/bin/python3
|
2
|
+
# -*- coding:utf-8 -*-
|
3
|
+
"""
|
4
|
+
@Author: xiaodong.li
|
5
|
+
@Time: 2024/04/24 20:45
|
6
|
+
@Description: Description
|
7
|
+
@File: cross_user_user_on_board_dto.py
|
8
|
+
"""
|
9
|
+
from dataclasses import dataclass
|
10
|
+
|
11
|
+
from api_requester.dto.base_dto import BaseDto
|
12
|
+
|
13
|
+
|
14
|
+
@dataclass
|
15
|
+
class CrossUserUserOnBoardDto(BaseDto):
|
16
|
+
"""
|
17
|
+
The CrossUserUserOnBoardDto was generated by Generator from the CrossUserUserOnBoardDto.java file.
|
18
|
+
"""
|
19
|
+
companyId: int = None
|
20
|
+
redirectUrl: str = None
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# !/usr/bin/python3
|
2
|
+
# -*- coding:utf-8 -*-
|
3
|
+
"""
|
4
|
+
@Author: xiaodong.li
|
5
|
+
@Time: 2024/04/24 20:45
|
6
|
+
@Description: Description
|
7
|
+
@File: jwt_authentication_request.py
|
8
|
+
"""
|
9
|
+
from dataclasses import dataclass
|
10
|
+
|
11
|
+
from api_requester.dto.base_dto import BaseDto
|
12
|
+
|
13
|
+
|
14
|
+
@dataclass
|
15
|
+
class JwtAuthenticationRequest(BaseDto):
|
16
|
+
"""
|
17
|
+
The JwtAuthenticationRequest was generated by Generator from the JwtAuthenticationRequest.java file.
|
18
|
+
"""
|
19
|
+
userName: str
|
20
|
+
password: str
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# !/usr/bin/python3
|
2
|
+
# -*- coding:utf-8 -*-
|
3
|
+
"""
|
4
|
+
@Author: xiaodong.li
|
5
|
+
@Time: 2024/04/24 20:45
|
6
|
+
@Description: Description
|
7
|
+
@File: user_on_board_dto.py
|
8
|
+
"""
|
9
|
+
from dataclasses import dataclass
|
10
|
+
|
11
|
+
from api_requester.dto.base_dto import BaseDto
|
12
|
+
|
13
|
+
|
14
|
+
@dataclass
|
15
|
+
class UserOnBoardDto(BaseDto):
|
16
|
+
"""
|
17
|
+
The UserOnBoardDto was generated by Generator from the UserOnBoardDto.java file.
|
18
|
+
"""
|
19
|
+
applicationId: int
|
20
|
+
sponsorId: int = None
|
21
|
+
studyId: int = None
|
22
|
+
envId: int = None
|
23
|
+
workForCompanyId: int = None
|
24
|
+
userId: int = None
|
25
|
+
userType: str = None
|
26
|
+
roleId: int = None
|
27
|
+
token: str = None
|
28
|
+
permissionId: int = None
|
29
|
+
companyLevelLogin: bool = False
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# !/usr/bin/python3
|
2
|
+
# -*- coding:utf-8 -*-
|
3
|
+
"""
|
4
|
+
@Author: xiaodong.li
|
5
|
+
@Time: 7/24/2023 3:56 PM
|
6
|
+
@Description: Description
|
7
|
+
@File: base_dto.py
|
8
|
+
"""
|
9
|
+
import mimetypes
|
10
|
+
import os
|
11
|
+
from dataclasses import dataclass, fields, Field
|
12
|
+
from typing import Dict, Any, TypeVar, Type, List, get_origin
|
13
|
+
|
14
|
+
T = TypeVar('T', bound='BaseDto')
|
15
|
+
|
16
|
+
|
17
|
+
@dataclass
|
18
|
+
class BaseDto:
|
19
|
+
|
20
|
+
def __getattr__(self, name):
|
21
|
+
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
|
22
|
+
|
23
|
+
def __setattr__(self, key, value):
|
24
|
+
if key not in [field.name for field in fields(self)]:
|
25
|
+
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{key}'")
|
26
|
+
super().__setattr__(key, value)
|
27
|
+
|
28
|
+
def data(self, has_none_value=True):
|
29
|
+
tmp = dict()
|
30
|
+
for k, v in self.__dict__.items():
|
31
|
+
if v is None and has_none_value is False:
|
32
|
+
continue
|
33
|
+
tmp.update({k: v})
|
34
|
+
return tmp
|
35
|
+
|
36
|
+
def to_dict(self) -> Dict[str, Any]:
|
37
|
+
data = dict()
|
38
|
+
for f in fields(self):
|
39
|
+
key = f.metadata.get('alias', f.name)
|
40
|
+
value = getattr(self, f.name)
|
41
|
+
multipart_file = f.metadata.get('multipart_file', False)
|
42
|
+
if multipart_file:
|
43
|
+
value = process_multipart_file(value)
|
44
|
+
elif isinstance(value, BaseDto):
|
45
|
+
value = value.to_dict()
|
46
|
+
elif isinstance(value, list):
|
47
|
+
value = [item.to_dict() if isinstance(item, BaseDto) else item for item in value]
|
48
|
+
elif isinstance(value, set): # 20241104在Python的requests库中,传递JSON数据时,set类型并不是JSON可序列化的格式,
|
49
|
+
# 因此需要将 set 转换为其他类型(如 list)才能正确发送
|
50
|
+
value = list(value)
|
51
|
+
if not f.metadata.get('ignore', False):
|
52
|
+
data[key] = value
|
53
|
+
return data
|
54
|
+
|
55
|
+
@classmethod
|
56
|
+
def from_dict(cls: Type[T], data: Dict[str, Any]) -> T:
|
57
|
+
kwargs = dict()
|
58
|
+
for f in fields(cls):
|
59
|
+
key = f.metadata.get("alias", f.name)
|
60
|
+
if key in data:
|
61
|
+
kwargs[f.name] = convert_value(f, data[key])
|
62
|
+
return cls(**kwargs)
|
63
|
+
|
64
|
+
def build_multipart_fields(self, values_as_str=False, has_none=True) -> List[tuple[str, Any]]:
|
65
|
+
data = list()
|
66
|
+
for f in fields(self):
|
67
|
+
key = f.metadata.get('alias', f.name)
|
68
|
+
value = getattr(self, f.name)
|
69
|
+
multipart_file = f.metadata.get('multipart_file', False)
|
70
|
+
if multipart_file:
|
71
|
+
value = process_multipart_file(value)
|
72
|
+
if isinstance(value, BaseDto):
|
73
|
+
value = value.to_dict()
|
74
|
+
elif isinstance(value, list):
|
75
|
+
value = [item.to_dict() if isinstance(item, BaseDto) else item for item in value]
|
76
|
+
if not f.metadata.get('ignore', False):
|
77
|
+
# if multipart_file and isinstance(value, list):
|
78
|
+
# data.extend((key, i) for i in value)
|
79
|
+
# elif isinstance(value, list):
|
80
|
+
# for i in value:
|
81
|
+
# if isinstance(i, BaseDto):
|
82
|
+
# data.append((key, i.to_dict()))
|
83
|
+
# elif values_as_str and i is not None:
|
84
|
+
# data.append((key, str(i)))
|
85
|
+
# else:
|
86
|
+
# data.append((key, i))
|
87
|
+
# else:
|
88
|
+
# if not has_none and value is None:
|
89
|
+
# continue
|
90
|
+
# data.append((key, str(value) if values_as_str and value is not None else value))
|
91
|
+
if value and isinstance(value, (list, dict)):
|
92
|
+
if isinstance(value, list) and all(isinstance(i, (str, int)) for i in value):
|
93
|
+
data.extend([(key, str(i)) if values_as_str else (key, i) for i in value])
|
94
|
+
else:
|
95
|
+
data.extend(tuple(
|
96
|
+
flatten_data(value, parent_key=key, has_none=has_none,
|
97
|
+
values_as_str=values_as_str).items()))
|
98
|
+
else:
|
99
|
+
if not has_none and (value is None or (isinstance(value, (list, dict)) and not value)):
|
100
|
+
continue
|
101
|
+
data.append((key, str(value) if values_as_str and value is not None else value))
|
102
|
+
return data
|
103
|
+
|
104
|
+
@classmethod
|
105
|
+
def has_multipart_file_field(cls) -> bool:
|
106
|
+
for f in fields(cls):
|
107
|
+
if f.metadata.get("multipart_file", False):
|
108
|
+
return True
|
109
|
+
return False
|
110
|
+
|
111
|
+
|
112
|
+
def convert_value(field: Field, value: Any) -> Any:
|
113
|
+
origin = get_origin(field.type)
|
114
|
+
if origin is not None:
|
115
|
+
return value
|
116
|
+
if isinstance(value, field.type):
|
117
|
+
return value
|
118
|
+
elif isinstance(value, (dict, int)) and field.type is str:
|
119
|
+
return str(value)
|
120
|
+
elif isinstance(value, str) and field.type is int:
|
121
|
+
if value.isdigit():
|
122
|
+
return int(value)
|
123
|
+
else:
|
124
|
+
return value
|
125
|
+
else:
|
126
|
+
return value
|
127
|
+
|
128
|
+
|
129
|
+
def build_file_dto(file_path):
|
130
|
+
return os.path.basename(file_path), open(file_path, 'rb'), next(iter(mimetypes.guess_type(file_path)))
|
131
|
+
|
132
|
+
|
133
|
+
def process_multipart_file(value):
|
134
|
+
"""处理 multipart_file 类型的值"""
|
135
|
+
if isinstance(value, str):
|
136
|
+
if os.path.exists(value) and os.path.isfile(value):
|
137
|
+
return build_file_dto(value)
|
138
|
+
else:
|
139
|
+
raise ValueError(f"无效的文件路径: {value}")
|
140
|
+
elif isinstance(value, list):
|
141
|
+
if all(isinstance(i, str) and os.path.exists(i) and os.path.isfile(i) for i in value):
|
142
|
+
return [build_file_dto(i) for i in value]
|
143
|
+
else:
|
144
|
+
raise ValueError(f"列表中包含无效的文件路径: {value}")
|
145
|
+
return value
|
146
|
+
|
147
|
+
|
148
|
+
def flatten_data(data, parent_key='', sep='.', values_as_str=False, has_none=True):
|
149
|
+
items = []
|
150
|
+
if isinstance(data, dict):
|
151
|
+
for k, v in data.items():
|
152
|
+
if not has_none and v is None:
|
153
|
+
continue
|
154
|
+
new_key = f"{parent_key}{sep}{k}" if parent_key else k
|
155
|
+
items.extend(flatten_data(v, new_key, sep=sep, values_as_str=values_as_str, has_none=has_none).items())
|
156
|
+
elif isinstance(data, list):
|
157
|
+
for idx, v in enumerate(data):
|
158
|
+
if not has_none and v is None:
|
159
|
+
continue
|
160
|
+
new_key = f"{parent_key}[{idx}]"
|
161
|
+
items.extend(flatten_data(v, new_key, sep=sep, values_as_str=values_as_str, has_none=has_none).items())
|
162
|
+
else:
|
163
|
+
if not has_none and data is None:
|
164
|
+
return dict()
|
165
|
+
items.append(
|
166
|
+
(parent_key, str(data) if values_as_str and data is not None and not isinstance(data, tuple) else data))
|
167
|
+
return dict(items)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# !/usr/bin/python3
|
2
|
+
# -*- coding:utf-8 -*-
|
3
|
+
"""
|
4
|
+
@Author: xiaodong.li
|
5
|
+
@Time: 6/12/2024 2:20 PM
|
6
|
+
@Description: Description
|
7
|
+
@File: biz_base.py
|
8
|
+
"""
|
9
|
+
import time
|
10
|
+
from abc import abstractmethod, ABC
|
11
|
+
from typing import Optional
|
12
|
+
|
13
|
+
from requests import Response
|
14
|
+
|
15
|
+
from api_requester.dto.user import EClinicalUser
|
16
|
+
|
17
|
+
|
18
|
+
class BizBase(ABC):
|
19
|
+
|
20
|
+
def __init__(self, user=None, headers=None, last_result=None, refresh_content_type=True):
|
21
|
+
self.user: Optional[EClinicalUser] = user
|
22
|
+
self.headers: dict = headers or dict()
|
23
|
+
self.last_kwargs: dict = dict()
|
24
|
+
self.last_result: Optional[Response] = last_result
|
25
|
+
self.time_mills = time.time()
|
26
|
+
self.refresh_content_type: bool = refresh_content_type
|
27
|
+
self.raises_exception: bool = True
|
28
|
+
self.allure_attach: bool = False
|
29
|
+
|
30
|
+
@abstractmethod
|
31
|
+
def login(self): ...
|