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.
Files changed (38) hide show
  1. api_requester/__init__.py +9 -0
  2. api_requester/core/admin/api/auth_api.py +34 -0
  3. api_requester/core/admin/api/user_on_board_application_api.py +31 -0
  4. api_requester/core/admin/service/auth_service.py +34 -0
  5. api_requester/core/admin/service/impl/system_env_service_impl.py +39 -0
  6. api_requester/core/admin/service/impl/user_on_board_application_service_impl.py +23 -0
  7. api_requester/core/admin/service/user_on_board_application_service.py +35 -0
  8. api_requester/core/call_api.py +64 -0
  9. api_requester/core/common/api/system_env_api.py +24 -0
  10. api_requester/core/common/service/system_env_service.py +27 -0
  11. api_requester/docs/application.yaml +46 -0
  12. api_requester/dto/admin/cross_user_user_on_board_dto.py +20 -0
  13. api_requester/dto/admin/jwt_authentication_request.py +20 -0
  14. api_requester/dto/admin/user_on_board_dto.py +29 -0
  15. api_requester/dto/base_dto.py +167 -0
  16. api_requester/dto/biz_base.py +31 -0
  17. api_requester/dto/user.py +39 -0
  18. api_requester/http/app_url.py +31 -0
  19. api_requester/http/authorize.py +156 -0
  20. api_requester/http/eclinical_requests.py +264 -0
  21. api_requester/http/exceptions.py +72 -0
  22. api_requester/http/gateway.py +105 -0
  23. api_requester/http/sample_headers.py +23 -0
  24. api_requester/http/token_manager.py +30 -0
  25. api_requester/utils/constant.py +108 -0
  26. api_requester/utils/json_utils.py +83 -0
  27. api_requester/utils/lib.py +456 -0
  28. api_requester/utils/log.py +91 -0
  29. api_requester/utils/path.py +21 -0
  30. api_requester/utils/placeholder_replacer.py +22 -0
  31. api_requester/utils/read_file.py +94 -0
  32. api_requester/utils/rsa.py +36 -0
  33. api_requester/utils/time_utils.py +27 -0
  34. eclinical_requester-1.0.0.dist-info/LICENSE +19 -0
  35. eclinical_requester-1.0.0.dist-info/METADATA +19 -0
  36. eclinical_requester-1.0.0.dist-info/RECORD +38 -0
  37. eclinical_requester-1.0.0.dist-info/WHEEL +5 -0
  38. eclinical_requester-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,9 @@
1
+ # !/usr/bin/python3
2
+ # -*- coding:utf-8 -*-
3
+ """
4
+ @Author: xiaodong.li
5
+ @Time: 1/8/2025 3:52 PM
6
+ @Description: Description
7
+ @File: __init__.py.py
8
+ """
9
+ __version__ = "1.0.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): ...