quapp-common 0.0.11.dev1__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 (72) hide show
  1. qapp_common/__init__.py +0 -0
  2. qapp_common/async_tasks/__init__.py +0 -0
  3. qapp_common/async_tasks/async_task.py +11 -0
  4. qapp_common/async_tasks/export_circuit_task.py +119 -0
  5. qapp_common/async_tasks/post_processing_task.py +59 -0
  6. qapp_common/component/__init__.py +0 -0
  7. qapp_common/component/backend/__init__.py +0 -0
  8. qapp_common/component/backend/invocation.py +255 -0
  9. qapp_common/component/backend/job_fetching.py +247 -0
  10. qapp_common/component/callback/__init__.py +0 -0
  11. qapp_common/component/callback/update_job_metadata.py +39 -0
  12. qapp_common/component/device/__init__.py +0 -0
  13. qapp_common/component/device/device_selection.py +81 -0
  14. qapp_common/config/__init__.py +0 -0
  15. qapp_common/config/logging_config.py +8 -0
  16. qapp_common/config/thread_config.py +10 -0
  17. qapp_common/data/__init__.py +0 -0
  18. qapp_common/data/async_task/__init__.py +0 -0
  19. qapp_common/data/async_task/circuit_export/__init__.py +0 -0
  20. qapp_common/data/async_task/circuit_export/backend_holder.py +12 -0
  21. qapp_common/data/async_task/circuit_export/circuit_holder.py +11 -0
  22. qapp_common/data/backend/__init__.py +0 -0
  23. qapp_common/data/backend/backend_information.py +14 -0
  24. qapp_common/data/callback/__init__.py +0 -0
  25. qapp_common/data/callback/callback_url.py +10 -0
  26. qapp_common/data/device/__init__.py +0 -0
  27. qapp_common/data/device/circuit_running_option.py +16 -0
  28. qapp_common/data/promise/__init__.py +0 -0
  29. qapp_common/data/promise/post_processing_promise.py +18 -0
  30. qapp_common/data/promise/promise.py +11 -0
  31. qapp_common/data/request/__init__.py +0 -0
  32. qapp_common/data/request/invocation_request.py +24 -0
  33. qapp_common/data/request/job_fetching_request.py +11 -0
  34. qapp_common/data/request/request.py +23 -0
  35. qapp_common/data/response/__init__.py +0 -0
  36. qapp_common/data/response/authentication.py +9 -0
  37. qapp_common/data/response/job_response.py +32 -0
  38. qapp_common/data/response/project_header.py +9 -0
  39. qapp_common/enum/__init__.py +0 -0
  40. qapp_common/enum/base_enum.py +16 -0
  41. qapp_common/enum/http_header.py +10 -0
  42. qapp_common/enum/invocation_step.py +13 -0
  43. qapp_common/enum/media_type.py +12 -0
  44. qapp_common/enum/processing_unit.py +10 -0
  45. qapp_common/enum/provider_tag.py +22 -0
  46. qapp_common/enum/sdk.py +22 -0
  47. qapp_common/enum/status/__init__.py +0 -0
  48. qapp_common/enum/status/job_status.py +10 -0
  49. qapp_common/enum/status/status_code.py +10 -0
  50. qapp_common/enum/token_type.py +8 -0
  51. qapp_common/factory/__init__.py +0 -0
  52. qapp_common/factory/device_factory.py +22 -0
  53. qapp_common/factory/handler_factory.py +20 -0
  54. qapp_common/factory/provider_factory.py +22 -0
  55. qapp_common/handler/__init__.py +0 -0
  56. qapp_common/handler/handler.py +20 -0
  57. qapp_common/model/__init__.py +0 -0
  58. qapp_common/model/device/__init__.py +0 -0
  59. qapp_common/model/device/custom_device.py +87 -0
  60. qapp_common/model/device/device.py +257 -0
  61. qapp_common/model/provider/__init__.py +0 -0
  62. qapp_common/model/provider/provider.py +34 -0
  63. qapp_common/util/__init__.py +0 -0
  64. qapp_common/util/file_utils.py +23 -0
  65. qapp_common/util/http_utils.py +37 -0
  66. qapp_common/util/json_parser_utils.py +75 -0
  67. qapp_common/util/response_utils.py +36 -0
  68. quapp_common-0.0.11.dev1.dist-info/METADATA +41 -0
  69. quapp_common-0.0.11.dev1.dist-info/RECORD +72 -0
  70. quapp_common-0.0.11.dev1.dist-info/WHEEL +5 -0
  71. quapp_common-0.0.11.dev1.dist-info/licenses/LICENSE +8 -0
  72. quapp_common-0.0.11.dev1.dist-info/top_level.txt +1 -0
File without changes
File without changes
@@ -0,0 +1,11 @@
1
+ """
2
+ QApp Platform Project async_task.py Copyright © CITYNOW Co. Ltd. All rights reserved.
3
+ """
4
+ from abc import abstractmethod
5
+
6
+
7
+ class AsyncTask:
8
+
9
+ @abstractmethod
10
+ def do(self):
11
+ pass
@@ -0,0 +1,119 @@
1
+ """
2
+ QApp Platform Project export_circuit.py Copyright © CITYNOW Co. Ltd. All rights reserved.
3
+ """
4
+ from io import BytesIO
5
+
6
+ import requests
7
+
8
+ from .async_task import AsyncTask
9
+ from ..data.async_task.circuit_export.backend_holder import BackendDataHolder
10
+ from ..data.async_task.circuit_export.circuit_holder import CircuitDataHolder
11
+ from ..config.logging_config import logger
12
+ from ..enum.media_type import MediaType
13
+ from ..util.file_utils import FileUtils
14
+ from ..util.http_utils import HttpUtils
15
+
16
+
17
+ class CircuitExportTask(AsyncTask):
18
+ MAX_CIRCUIT_IMAGE_SIZE = 5 * (1024 ** 2)
19
+
20
+ def __init__(self,
21
+ circuit_data_holder: CircuitDataHolder,
22
+ backend_data_holder: BackendDataHolder):
23
+ super().__init__()
24
+ self.circuit_data_holder = circuit_data_holder
25
+ self.backend_data_holder = backend_data_holder
26
+
27
+ def do(self):
28
+ """
29
+ Export circuit to svg file then send to QuaO server for saving
30
+ """
31
+ logger.debug("[Circuit export] Start")
32
+
33
+ circuit_export_url = self.circuit_data_holder.export_url
34
+
35
+ if circuit_export_url is None or len(circuit_export_url) < 1:
36
+ return
37
+
38
+ figure_buffer = self.__convert()
39
+
40
+ io_buffer_value, content_type = self.__determine_zip(figure_buffer=figure_buffer)
41
+
42
+ self.__send(io_buffer_value=io_buffer_value,
43
+ content_type=content_type)
44
+
45
+ def __convert(self):
46
+ """
47
+
48
+ @return:
49
+ """
50
+ logger.debug("[Circuit export] Preparing circuit figure...")
51
+ transpiled_circuit = self._transpile_circuit()
52
+ circuit_figure = transpiled_circuit.draw(output='mpl', fold=-1)
53
+
54
+ logger.debug("[Circuit export] Converting circuit figure to svg file...")
55
+ figure_buffer = BytesIO()
56
+ circuit_figure.savefig(figure_buffer, format='svg', bbox_inches='tight')
57
+
58
+ return figure_buffer
59
+
60
+ @staticmethod
61
+ def __determine_zip(figure_buffer):
62
+ """
63
+
64
+ @param figure_buffer:
65
+ @return:
66
+ """
67
+ buffer_value = figure_buffer.getvalue()
68
+ content_type = MediaType.SVG_XML
69
+
70
+ logger.debug("[Circuit export] Checking max file size")
71
+ estimated_file_size = len(buffer_value)
72
+
73
+ if estimated_file_size > CircuitExportTask.MAX_CIRCUIT_IMAGE_SIZE:
74
+ logger.debug("[Circuit export] Zip file")
75
+ zip_file_buffer = FileUtils.zip(io_buffer_value=buffer_value,
76
+ file_name="circuit_image.svg")
77
+
78
+ buffer_value = zip_file_buffer.getvalue()
79
+ content_type = MediaType.APPLICATION_ZIP
80
+
81
+ return buffer_value, content_type
82
+
83
+ def __send(self, io_buffer_value, content_type: MediaType):
84
+ """
85
+
86
+ @param io_buffer_value:
87
+ @param content_type:
88
+ """
89
+ url = self.circuit_data_holder.export_url
90
+
91
+ logger.debug(
92
+ "[Circuit export] Sending circuit svg image to [{0}] with POST method ...".format(
93
+ url))
94
+
95
+ payload = {'circuit': (
96
+ 'circuit_image.svg',
97
+ io_buffer_value,
98
+ content_type.value)}
99
+
100
+ response = requests.post(
101
+ url=url,
102
+ headers=HttpUtils.create_bearer_header(self.backend_data_holder.user_token),
103
+ files=payload)
104
+
105
+ if response.ok:
106
+ logger.debug("Sending request to QuaO backend successfully!")
107
+ else:
108
+ logger.debug("Sending request to QuaO backend failed with status {0}!".format(
109
+ response.status_code))
110
+
111
+ logger.debug("[Circuit export] Finish")
112
+
113
+ def _transpile_circuit(self):
114
+ """
115
+
116
+ @return: Transpiled circuit
117
+ """
118
+
119
+ raise NotImplemented('[CircuitExportTask] __transpile_circuit() method must be implemented')
@@ -0,0 +1,59 @@
1
+ """
2
+ QApp Platform Project post_processing_task.py Copyright © CITYNOW Co. Ltd. All rights reserved.
3
+ """
4
+ from .async_task import AsyncTask
5
+ from ..component.callback.update_job_metadata import update_job_metadata
6
+ from ..config.logging_config import logger
7
+ from ..data.promise.post_processing_promise import PostProcessingPromise
8
+ from ..data.response.job_response import JobResponse
9
+ from ..enum.media_type import MediaType
10
+ from ..enum.status.job_status import JobStatus
11
+ from ..enum.status.status_code import StatusCode
12
+ from ..util.json_parser_utils import JsonParserUtils
13
+
14
+
15
+ class PostProcessingTask(AsyncTask):
16
+ def __init__(self,
17
+ post_processing_fn,
18
+ promise: PostProcessingPromise):
19
+ self.post_processing_fn = post_processing_fn
20
+ self.promise = promise
21
+
22
+ def do(self):
23
+ """
24
+ Execute post_processing and send to backend
25
+ """
26
+
27
+ job_response = JobResponse(
28
+ authentication=self.promise.authentication,
29
+ project_header=self.promise.project_header,
30
+ job_status=JobStatus.DONE.value,
31
+ status_code=StatusCode.DONE)
32
+
33
+ update_job_metadata(job_response=job_response,
34
+ callback_url=self.promise.callback_url.on_start)
35
+
36
+ job_response.content_type = MediaType.APPLICATION_JSON
37
+
38
+ try:
39
+
40
+ logger.info("Execute post_processing ...")
41
+ job_result_post_processing = self.post_processing_fn(self.promise.job_result)
42
+
43
+ logger.debug("Parsing job result....")
44
+ print(job_result_post_processing)
45
+ job_response.job_result = JsonParserUtils.parse(job_result_post_processing)
46
+
47
+ update_job_metadata(job_response=job_response,
48
+ callback_url=self.promise.callback_url.on_done)
49
+
50
+ except Exception as exception:
51
+ job_response.job_result = {
52
+ "error": "Error when execute post_processing(): ",
53
+ "exception": str(exception),
54
+ }
55
+ job_response.status_code = StatusCode.ERROR
56
+ job_response.job_status = JobStatus.ERROR.value
57
+
58
+ update_job_metadata(job_response=job_response,
59
+ callback_url=self.promise.callback_url.on_error)
File without changes
File without changes
@@ -0,0 +1,255 @@
1
+ """
2
+ QApp Platform Project backend.py Copyright © CITYNOW Co. Ltd. All rights reserved.
3
+ """
4
+ from abc import abstractmethod, ABC
5
+
6
+ from ..callback.update_job_metadata import update_job_metadata
7
+ from ...component.device.device_selection import DeviceSelection
8
+ from ...config.logging_config import logger
9
+ from ...config.thread_config import circuit_running_pool
10
+ from ...data.backend.backend_information import BackendInformation
11
+ from ...data.device.circuit_running_option import CircuitRunningOption
12
+ from ...data.request.invocation_request import InvocationRequest
13
+ from ...data.response.authentication import Authentication
14
+ from ...data.response.job_response import JobResponse
15
+ from ...data.response.project_header import ProjectHeader
16
+ from ...enum.invocation_step import InvocationStep
17
+ from ...enum.media_type import MediaType
18
+ from ...enum.sdk import Sdk
19
+ from ...enum.status.job_status import JobStatus
20
+ from ...enum.status.status_code import StatusCode
21
+ from ...model.provider.provider import Provider
22
+
23
+ EXPORT_CIRCUIT_SDK = {Sdk.QISKIT, Sdk.BRAKET}
24
+
25
+
26
+ class Invocation(ABC):
27
+ def __init__(self, request_data: InvocationRequest):
28
+ self.sdk: Sdk = Sdk.resolve(request_data.sdk)
29
+ self.input = request_data.input
30
+ self.device_id = request_data.device_id
31
+ self.backend_information: BackendInformation
32
+ self.authentication: Authentication = request_data.authentication
33
+ self.project_header: ProjectHeader = request_data.project_header
34
+ self.options = CircuitRunningOption(
35
+ shots=request_data.shots,
36
+ processing_unit=request_data.processing_unit,
37
+ executor=circuit_running_pool,
38
+ max_job_size=1,
39
+ )
40
+ self.device_selection_url: str = request_data.device_selection_url
41
+ self.circuit_export_url: str = request_data.circuit_export_url
42
+ self.callback_dict: dict = {
43
+ InvocationStep.PREPARATION: request_data.preparation,
44
+ InvocationStep.EXECUTION: request_data.execution,
45
+ InvocationStep.ANALYSIS: request_data.analysis,
46
+ InvocationStep.FINALIZATION: request_data.finalization,
47
+ }
48
+ self.invoke_authentication = request_data.invoke_authentication
49
+
50
+ def submit_job(self, circuit_preparation_fn, post_processing_fn):
51
+ """
52
+
53
+ @param post_processing_fn: Post-processing function
54
+ @param circuit_preparation_fn: Circuit-preparation function
55
+ @return: Job result
56
+ """
57
+ logger.debug("[Invocation] Invoke job")
58
+
59
+ circuit = self.__pre_execute(circuit_preparation_fn)
60
+
61
+ if circuit is None:
62
+ return
63
+
64
+ self.__execute(circuit, post_processing_fn)
65
+
66
+ def __pre_execute(self, circuit_preparation_fn):
67
+ """
68
+
69
+ @param circuit_preparation_fn: Circuit preparation function
70
+ """
71
+ circuit = self.__prepare_circuit(circuit_preparation_fn)
72
+
73
+ if circuit is None:
74
+ return None
75
+
76
+ try:
77
+ self.__prepare_backend_data(circuit)
78
+
79
+ except Exception as exception:
80
+ job_response = JobResponse(
81
+ status_code=StatusCode.ERROR,
82
+ authentication=self.authentication,
83
+ project_header=self.project_header,
84
+ job_result={"error": str(exception)},
85
+ content_type=MediaType.APPLICATION_JSON,
86
+ job_status=JobStatus.ERROR.value,
87
+ )
88
+
89
+ update_job_metadata(
90
+ job_response=job_response,
91
+ callback_url=self.callback_dict.get(
92
+ InvocationStep.PREPARATION
93
+ ).on_error,
94
+ )
95
+
96
+ self._export_circuit(circuit)
97
+
98
+ return circuit
99
+
100
+ def __execute(self, circuit, post_processing_fn):
101
+ """
102
+
103
+ @param circuit: Circuit was run
104
+ @param post_processing_fn: Post-processing function
105
+ @return: Job response
106
+ """
107
+
108
+ logger.debug("[Invocation] Execute job")
109
+
110
+ try:
111
+ if self.backend_information is None:
112
+ raise Exception("Backend is not found")
113
+
114
+ device_name = self.backend_information.device_name
115
+ provider_tag = self.backend_information.provider_tag
116
+ backend_authentication = self.backend_information.authentication
117
+
118
+ logger.debug(
119
+ "[Invocation] Execute job with provider tag: {0}".format(
120
+ provider_tag.value
121
+ )
122
+ )
123
+
124
+ provider = self._create_provider()
125
+
126
+ logger.debug(
127
+ "[Invocation] Execute job with device name: {0}".format(device_name)
128
+ )
129
+ device = self._create_device(provider)
130
+
131
+ except Exception as exception:
132
+ job_response = JobResponse(
133
+ job_status=JobStatus.ERROR.value,
134
+ content_type=MediaType.APPLICATION_JSON,
135
+ status_code=StatusCode.ERROR,
136
+ authentication=self.authentication,
137
+ project_header=self.project_header,
138
+ job_result={"error": str(exception)},
139
+ )
140
+ update_job_metadata(
141
+ job_response=job_response,
142
+ callback_url=self.callback_dict.get(InvocationStep.EXECUTION).on_error,
143
+ )
144
+
145
+ return
146
+
147
+ device.run_circuit(
148
+ circuit=circuit,
149
+ post_processing_fn=post_processing_fn,
150
+ options=self.options,
151
+ callback_dict=self.callback_dict,
152
+ authentication=self.authentication,
153
+ project_header=self.project_header,
154
+ )
155
+
156
+ def __prepare_circuit(self, circuit_preparation_fn):
157
+ """
158
+
159
+ @param circuit_preparation_fn: Circuit preparation function
160
+ @return: circuit
161
+ """
162
+ logger.debug("[Invocation] Prepare circuit")
163
+
164
+ job_response = JobResponse(
165
+ status_code=StatusCode.DONE,
166
+ authentication=self.authentication,
167
+ project_header=self.project_header,
168
+ )
169
+ update_job_metadata(
170
+ job_response=job_response,
171
+ callback_url=self.callback_dict.get(InvocationStep.PREPARATION).on_start,
172
+ )
173
+
174
+ try:
175
+ circuit = circuit_preparation_fn(self.input)
176
+
177
+ if circuit is None:
178
+ raise Exception("Invalid circuit")
179
+
180
+ update_job_metadata(
181
+ job_response=job_response,
182
+ callback_url=self.callback_dict.get(InvocationStep.PREPARATION).on_done,
183
+ )
184
+
185
+ return circuit
186
+
187
+ except Exception as exception:
188
+ job_response.job_status = JobStatus.ERROR.value
189
+ job_response.content_type = MediaType.APPLICATION_JSON
190
+ job_response.status_code = StatusCode.ERROR
191
+ job_response.job_result = {"error": str(exception)}
192
+
193
+ update_job_metadata(
194
+ job_response=job_response,
195
+ callback_url=self.callback_dict.get(
196
+ InvocationStep.PREPARATION
197
+ ).on_error,
198
+ )
199
+
200
+ return None
201
+
202
+ def __prepare_backend_data(self, circuit):
203
+ """
204
+
205
+ @param circuit: Circuit was run
206
+ """
207
+
208
+ required_qubit_amount = self._get_qubit_amount(circuit)
209
+
210
+ device_selection = DeviceSelection(
211
+ required_qubit_amount,
212
+ self.device_id,
213
+ self.authentication.user_token,
214
+ self.device_selection_url,
215
+ )
216
+
217
+ self.backend_information = device_selection.select()
218
+ logger.info("before")
219
+ logger.info(self.backend_information.authentication)
220
+ self.backend_information.authentication = self.invoke_authentication
221
+ logger.info("after")
222
+ logger.info(self.backend_information.authentication)
223
+
224
+ @abstractmethod
225
+ def _export_circuit(self, circuit):
226
+ """
227
+
228
+ @param circuit: Circuit was exported
229
+ """
230
+
231
+ raise NotImplemented('[Invocation] _export_circuit() method must be implemented')
232
+
233
+ @abstractmethod
234
+ def _create_provider(self, ):
235
+ """
236
+ Create provider with ProviderFactory
237
+ """
238
+
239
+ raise NotImplemented('[Invocation] _create_provider() method must be implemented')
240
+
241
+ @abstractmethod
242
+ def _create_device(self, provider: Provider):
243
+ """
244
+ Create device with DeviceFactory
245
+ """
246
+
247
+ raise NotImplemented('[Invocation] _create_device() method must be implemented')
248
+
249
+ @abstractmethod
250
+ def _get_qubit_amount(self, circuit):
251
+ """
252
+ Get number qubit of circuit
253
+ """
254
+
255
+ raise NotImplemented('[Invocation] _get_qubit_amount() method must be implemented')
@@ -0,0 +1,247 @@
1
+ """
2
+ QApp Platform Project job_fetching.py Copyright © CITYNOW Co. Ltd. All rights reserved.
3
+ """
4
+ from abc import ABC, abstractmethod
5
+ from typing import Any
6
+
7
+ from ..callback.update_job_metadata import update_job_metadata
8
+ from ...async_tasks.post_processing_task import PostProcessingTask
9
+ from ...config.thread_config import circuit_running_pool
10
+ from ...data.callback.callback_url import CallbackUrl
11
+ from ...data.promise.post_processing_promise import PostProcessingPromise
12
+ from ...data.request.job_fetching_request import JobFetchingRequest
13
+ from ...data.response.authentication import Authentication
14
+ from ...data.response.job_response import JobResponse
15
+ from ...data.response.project_header import ProjectHeader
16
+ from ...enum.invocation_step import InvocationStep
17
+ from ...config.logging_config import logger
18
+ from ...enum.media_type import MediaType
19
+ from ...enum.status.job_status import JobStatus
20
+ from ...enum.status.status_code import StatusCode
21
+ from ...util.json_parser_utils import JsonParserUtils
22
+ from ...util.response_utils import ResponseUtils
23
+
24
+
25
+ class JobFetching(ABC):
26
+ def __init__(self, request_data: JobFetchingRequest):
27
+ self.provider_authentication: dict = request_data.provider_authentication
28
+ self.provider_job_id: str = request_data.provider_job_id
29
+ self.backend_authentication: Authentication = request_data.authentication
30
+ self.project_header: ProjectHeader = request_data.project_header
31
+ self.callback_dict: dict = {
32
+ InvocationStep.EXECUTION: request_data.execution,
33
+ InvocationStep.ANALYSIS: request_data.analysis,
34
+ InvocationStep.FINALIZATION: request_data.finalization
35
+ }
36
+
37
+ def fetch(self, post_processing_fn):
38
+ """
39
+
40
+ @param post_processing_fn:
41
+ @return:
42
+ """
43
+ logger.debug("[Fetching] Fetch job")
44
+
45
+ job_response = JobResponse(
46
+ provider_job_id=self.provider_job_id,
47
+ authentication=self.backend_authentication,
48
+ project_header=self.project_header,
49
+ status_code=StatusCode.DONE)
50
+
51
+ try:
52
+ provider = self._collect_provider()
53
+
54
+ job = self._retrieve_job(provider)
55
+ job_status = self._get_job_status(job)
56
+ original_job_result = self._get_job_result(job)
57
+
58
+ job_response.job_status = job_status
59
+
60
+ if JobStatus.DONE.value.__eq__(job_response.job_status):
61
+ circuit_running_pool.submit(self.__handle_job_result,
62
+ original_job_result,
63
+ job_response,
64
+ self.callback_dict,
65
+ post_processing_fn)
66
+
67
+ update_job_metadata(
68
+ job_response=job_response,
69
+ callback_url=self.callback_dict.get(InvocationStep.EXECUTION).on_done)
70
+
71
+ elif JobStatus.ERROR.value.__eq__(job_response.job_status):
72
+ job_response.job_result = JsonParserUtils.parse(original_job_result)
73
+
74
+ update_job_metadata(
75
+ job_response=job_response,
76
+ callback_url=self.callback_dict.get(InvocationStep.EXECUTION).on_error)
77
+
78
+ else:
79
+ job_response.status_code = StatusCode.POLLING
80
+
81
+ except Exception as exception:
82
+ logger.debug("Exception when fetch job with provider_job_id {0}: {1}".format(
83
+ self.provider_job_id, str(exception)))
84
+
85
+ job_response.job_result = {
86
+ "error": "Exception when fetch job with provider_job_id {0}: {1}".format(
87
+ self.provider_job_id, str(exception)),
88
+ "exception": str(exception),
89
+ }
90
+ job_response.status_code = StatusCode.ERROR
91
+ job_response.job_status = JobStatus.ERROR.value
92
+
93
+ update_job_metadata(
94
+ job_response=job_response,
95
+ callback_url=self.callback_dict.get(InvocationStep.EXECUTION).on_error)
96
+
97
+ return ResponseUtils.generate_response(job_response)
98
+
99
+ def __handle_job_result(self,
100
+ original_job_result,
101
+ job_response: JobResponse,
102
+ callback_dict: dict,
103
+ post_processing_fn):
104
+ """
105
+ Fetch job from IBM Quantum
106
+
107
+ @return: Job status
108
+ """
109
+
110
+ job_response = self.__on_analysis(
111
+ callback_url=callback_dict.get(InvocationStep.ANALYSIS),
112
+ job_response=job_response,
113
+ original_job_result=original_job_result)
114
+
115
+ if job_response is None:
116
+ return
117
+
118
+ self.__on_finalization(post_processing_fn=post_processing_fn,
119
+ callback_url=callback_dict.get(InvocationStep.FINALIZATION),
120
+ original_job_result=original_job_result)
121
+
122
+ def __on_analysis(self,
123
+ callback_url: CallbackUrl,
124
+ job_response: JobResponse,
125
+ original_job_result):
126
+ """
127
+
128
+ @param callback_url:
129
+ @param job_response:
130
+ @param original_job_result:
131
+ @return:
132
+ """
133
+ logger.debug("[Fetching] On analysis")
134
+
135
+ update_job_metadata(job_response=job_response,
136
+ callback_url=callback_url.on_start)
137
+
138
+ try:
139
+ job_response.content_type = MediaType.APPLICATION_JSON
140
+
141
+ job_response.job_histogram = self.__produce_histogram_data(original_job_result)
142
+
143
+ job_result_parse = JsonParserUtils.parse(original_job_result.result())
144
+
145
+ job_response.execution_time = self.__get_execution_time(original_job_result)
146
+
147
+ update_job_metadata(
148
+ job_response=job_response,
149
+ callback_url=callback_url.on_done)
150
+
151
+ return job_response
152
+
153
+ except Exception as exception:
154
+ logger.error("Exception when analyst job result with provider_job_id {0}: {1}".format(
155
+ self.provider_job_id, str(exception)))
156
+
157
+ job_response.status_code = StatusCode.ERROR
158
+ job_response.job_status = JobStatus.ERROR.value
159
+ job_response.job_result = {
160
+ "error": "Exception when analyst job result with provider_job_id {0}".format(
161
+ self.provider_job_id),
162
+ "exception": str(exception),
163
+ }
164
+
165
+ update_job_metadata(
166
+ job_response=job_response,
167
+ callback_url=callback_url.on_error)
168
+
169
+ return None
170
+
171
+ def __on_finalization(self,
172
+ post_processing_fn,
173
+ callback_url: CallbackUrl,
174
+ original_job_result):
175
+ """
176
+
177
+ @param post_processing_fn:
178
+ @param callback_url:
179
+ @param original_job_result:
180
+ """
181
+ logger.debug("[Fetching] On finalization")
182
+
183
+ promise = PostProcessingPromise(callback_url=callback_url,
184
+ authentication=self.backend_authentication,
185
+ job_result=original_job_result.result(),
186
+ project_header=self.project_header)
187
+
188
+ PostProcessingTask(post_processing_fn=post_processing_fn,
189
+ promise=promise).do()
190
+
191
+ @staticmethod
192
+ def __produce_histogram_data(job_result) -> Any | None:
193
+ """
194
+
195
+ @param job_result:
196
+ @return:
197
+ """
198
+ logger.debug("[Fetching] Produce histogram")
199
+
200
+ try:
201
+ histogram_data = job_result.result()[0].data.meas.get_counts()
202
+
203
+ except Exception as e:
204
+ logger.debug("[QiskitDevice] Can't produce histogram with error: {0}".format(str(e)))
205
+ histogram_data = None
206
+
207
+ return histogram_data
208
+
209
+ @staticmethod
210
+ def __get_execution_time(job_result):
211
+ """
212
+
213
+ @param job_result:
214
+ @return:
215
+ """
216
+ return job_result.usage()
217
+ @abstractmethod
218
+ def _collect_provider(self, ):
219
+ """
220
+ Create provider with ProviderFactory
221
+ """
222
+
223
+ raise NotImplemented('[Invocation] _create_provider() method must be implemented')
224
+
225
+ @abstractmethod
226
+ def _retrieve_job(self, provider):
227
+ """
228
+ Retrieve job from provider
229
+ """
230
+
231
+ raise NotImplemented('[Invocation] _retrieve_job() method must be implemented')
232
+
233
+ @abstractmethod
234
+ def _get_job_status(self, job):
235
+ """
236
+ Get job status
237
+ """
238
+
239
+ raise NotImplemented('[Invocation] _get_job_status() method must be implemented')
240
+
241
+ @abstractmethod
242
+ def _get_job_result(self, job):
243
+ """
244
+ Get job result
245
+ """
246
+
247
+ raise NotImplemented('[Invocation] _get_job_result() method must be implemented')