quapp-common 0.0.11.dev4__tar.gz → 0.0.11.dev6__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.
Files changed (80) hide show
  1. {quapp_common-0.0.11.dev4/quapp_common.egg-info → quapp_common-0.0.11.dev6}/PKG-INFO +1 -1
  2. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/pyproject.toml +1 -1
  3. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/async_tasks/export_circuit_task.py +21 -15
  4. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/component/backend/invocation.py +10 -9
  5. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/component/backend/job_fetcher.py +4 -4
  6. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/component/backend/job_fetching.py +2 -2
  7. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/component/callback/update_job_metadata.py +1 -1
  8. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/component/device/device_selection.py +1 -1
  9. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/config/logging_config.py +6 -4
  10. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/request/invocation_request.py +2 -2
  11. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/request/job_fetching_request.py +2 -2
  12. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/response/job_response.py +2 -2
  13. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/model/device/custom_device.py +1 -1
  14. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/model/device/device.py +1 -1
  15. quapp_common-0.0.11.dev6/quapp_common/util/http_utils.py +141 -0
  16. quapp_common-0.0.11.dev6/quapp_common/util/response_utils.py +200 -0
  17. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6/quapp_common.egg-info}/PKG-INFO +1 -1
  18. quapp_common-0.0.11.dev4/quapp_common/util/http_utils.py +0 -93
  19. quapp_common-0.0.11.dev4/quapp_common/util/response_utils.py +0 -97
  20. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/LICENSE +0 -0
  21. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/README.md +0 -0
  22. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/__init__.py +0 -0
  23. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/async_tasks/__init__.py +0 -0
  24. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/async_tasks/async_task.py +0 -0
  25. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/async_tasks/post_processing_task.py +0 -0
  26. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/component/__init__.py +0 -0
  27. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/component/backend/__init__.py +0 -0
  28. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/component/callback/__init__.py +0 -0
  29. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/component/device/__init__.py +0 -0
  30. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/config/__init__.py +0 -0
  31. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/config/thread_config.py +0 -0
  32. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/__init__.py +0 -0
  33. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/async_task/__init__.py +0 -0
  34. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/async_task/circuit_export/__init__.py +0 -0
  35. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/async_task/circuit_export/backend_holder.py +0 -0
  36. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/async_task/circuit_export/circuit_holder.py +0 -0
  37. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/backend/__init__.py +0 -0
  38. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/backend/backend_information.py +0 -0
  39. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/callback/__init__.py +0 -0
  40. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/callback/callback_url.py +0 -0
  41. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/device/__init__.py +0 -0
  42. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/device/circuit_running_option.py +0 -0
  43. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/promise/__init__.py +0 -0
  44. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/promise/post_processing_promise.py +0 -0
  45. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/promise/promise.py +0 -0
  46. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/request/__init__.py +0 -0
  47. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/request/request.py +0 -0
  48. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/response/__init__.py +0 -0
  49. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/response/authentication.py +0 -0
  50. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/data/response/custom_header.py +0 -0
  51. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/enum/__init__.py +0 -0
  52. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/enum/base_enum.py +0 -0
  53. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/enum/http_header.py +0 -0
  54. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/enum/invocation_step.py +0 -0
  55. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/enum/media_type.py +0 -0
  56. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/enum/processing_unit.py +0 -0
  57. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/enum/provider_tag.py +0 -0
  58. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/enum/sdk.py +0 -0
  59. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/enum/status/__init__.py +0 -0
  60. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/enum/status/job_status.py +0 -0
  61. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/enum/status/status_code.py +0 -0
  62. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/enum/token_type.py +0 -0
  63. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/factory/__init__.py +0 -0
  64. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/factory/device_factory.py +0 -0
  65. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/factory/handler_factory.py +0 -0
  66. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/factory/provider_factory.py +0 -0
  67. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/handler/__init__.py +0 -0
  68. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/handler/handler.py +0 -0
  69. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/model/__init__.py +0 -0
  70. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/model/device/__init__.py +0 -0
  71. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/model/provider/__init__.py +0 -0
  72. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/model/provider/provider.py +0 -0
  73. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/util/__init__.py +0 -0
  74. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/util/file_utils.py +0 -0
  75. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common/util/json_parser_utils.py +0 -0
  76. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common.egg-info/SOURCES.txt +0 -0
  77. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common.egg-info/dependency_links.txt +0 -0
  78. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common.egg-info/requires.txt +0 -0
  79. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/quapp_common.egg-info/top_level.txt +0 -0
  80. {quapp_common-0.0.11.dev4 → quapp_common-0.0.11.dev6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quapp-common
3
- Version: 0.0.11.dev4
3
+ Version: 0.0.11.dev6
4
4
  Summary: Quapp common library supporting Quapp Platform for Quantum Computing
5
5
  Author-email: "CITYNOW Co. Ltd. " <corp@citynow.vn>
6
6
  License: The MIT License (MIT)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "quapp-common"
7
- version = "0.0.11.dev4"
7
+ version = "0.0.11.dev6"
8
8
  description = "Quapp common library supporting Quapp Platform for Quantum Computing"
9
9
  readme = "README.md"
10
10
  authors = [{ name = "CITYNOW Co. Ltd. ", email = "corp@citynow.vn" }]
@@ -11,6 +11,7 @@ from .async_task import AsyncTask
11
11
  from ..config.logging_config import job_logger
12
12
  from ..data.async_task.circuit_export.backend_holder import BackendDataHolder
13
13
  from ..data.async_task.circuit_export.circuit_holder import CircuitDataHolder
14
+ from ..data.response.custom_header import CustomHeader
14
15
  from ..enum.media_type import MediaType
15
16
  from ..util.file_utils import FileUtils
16
17
  from ..util.http_utils import create_bearer_header, get_job_id_from_url
@@ -20,8 +21,11 @@ class CircuitExportTask(AsyncTask):
20
21
  MAX_CIRCUIT_IMAGE_SIZE = 5 * (1024 ** 2)
21
22
 
22
23
  def __init__(self, circuit_data_holder: CircuitDataHolder,
23
- backend_data_holder: BackendDataHolder):
24
+ backend_data_holder: BackendDataHolder,
25
+ project_header: CustomHeader, workspace_header: CustomHeader):
24
26
  super().__init__()
27
+ self.project_header = project_header
28
+ self.workspace_header = workspace_header
25
29
  self.circuit_data_holder = circuit_data_holder
26
30
  self.backend_data_holder = backend_data_holder
27
31
  self.logger = job_logger(
@@ -43,8 +47,8 @@ class CircuitExportTask(AsyncTask):
43
47
  self.logger.debug('Converting circuit to SVG')
44
48
  figure_buffer = self.__convert()
45
49
  except Exception as e:
46
- self.logger.error(f"Error converting circuit to SVG: {e}",
47
- exc_info=True)
50
+ self.logger.exception(f"Error converting circuit to SVG: {e}",
51
+ exc_info=True)
48
52
  return
49
53
 
50
54
  try:
@@ -54,7 +58,7 @@ class CircuitExportTask(AsyncTask):
54
58
  self.logger.debug(f"Content type: {content_type}")
55
59
  self.logger.debug(f"Buffer size: {len(io_buffer_value)} bytes")
56
60
  except Exception as e:
57
- self.logger.error(
61
+ self.logger.exception(
58
62
  f"Error determining if circuit SVG should be zipped: {e}",
59
63
  exc_info=True)
60
64
  return
@@ -64,11 +68,12 @@ class CircuitExportTask(AsyncTask):
64
68
  self.__send(io_buffer_value=io_buffer_value,
65
69
  content_type=content_type)
66
70
  self.logger.debug("Circuit sent to backend successfully.")
71
+ return
67
72
  except Exception as e:
68
- self.logger.error(f"Error sending exported circuit to backend: {e}",
69
- exc_info=True)
70
-
71
- self.logger.info("Circuit export task finished.")
73
+ self.logger.exception(
74
+ f"Error sending exported circuit to backend: {e}",
75
+ exc_info=True)
76
+ return
72
77
 
73
78
  def __convert(self):
74
79
  """
@@ -81,8 +86,8 @@ class CircuitExportTask(AsyncTask):
81
86
  try:
82
87
  circuit_figure = transpiled_circuit.draw(output='mpl', fold=-1)
83
88
  except Exception as exception:
84
- self.logger.error(f"Error drawing circuit: {exception}",
85
- exc_info=True)
89
+ self.logger.exception(f"Error drawing circuit: {exception}",
90
+ exc_info=True)
86
91
  raise exception
87
92
 
88
93
  self.logger.debug("Converting circuit figure to SVG file...")
@@ -93,7 +98,7 @@ class CircuitExportTask(AsyncTask):
93
98
  self.logger.debug(
94
99
  f"SVG export complete. Size: {figure_buffer.getbuffer().nbytes} bytes")
95
100
  except Exception as exception:
96
- self.logger.error(
101
+ self.logger.exception(
97
102
  f"Error saving circuit figure to SVG: {exception}",
98
103
  exc_info=True)
99
104
  raise exception
@@ -135,16 +140,17 @@ class CircuitExportTask(AsyncTask):
135
140
 
136
141
  try:
137
142
  response = requests.post(url=url, headers=create_bearer_header(
138
- self.backend_data_holder.user_token), files=payload)
143
+ self.backend_data_holder.user_token, self.project_header,
144
+ self.workspace_header), files=payload)
139
145
  except Exception as exception:
140
- self.logger.error(f"HTTP request failed: {exception}",
141
- exc_info=True)
146
+ self.logger.exception(f"HTTP request failed: {exception}",
147
+ exc_info=True)
142
148
  raise
143
149
 
144
150
  if response.ok:
145
151
  self.logger.info("Request sent to QuaO backend successfully.")
146
152
  else:
147
- self.logger.error(
153
+ self.logger.exception(
148
154
  f"Sending request to QuaO backend failed with status {response.status_code}! Response: {response.content}")
149
155
 
150
156
  self.logger.debug("HTTP request complete.")
@@ -77,9 +77,9 @@ class Invocation(ABC):
77
77
  circuit = self.__prepare_circuit(circuit_preparation_fn)
78
78
 
79
79
  if circuit is None:
80
- self.logger.error(
80
+ self.logger.exception(
81
81
  "Circuit preparation failed, returning None from pre-execute")
82
- return None
82
+ raise ValueError("Circuit preparation failed")
83
83
 
84
84
  try:
85
85
  self.logger.debug("Preparing backend data")
@@ -88,12 +88,13 @@ class Invocation(ABC):
88
88
  f"Backend data prepared: device {self.backend_information.device_name}, provider {self.backend_information.provider_tag.value}")
89
89
 
90
90
  except Exception as exception:
91
- self.logger.error(f"Error when prepare backend data: {exception}",
92
- exc_info=True)
91
+ self.logger.exception(
92
+ f"Error when prepare backend data: {exception}",
93
+ exc_info=True)
93
94
 
94
95
  job_response = build_error_job_response(exception,
95
96
  message='Error when prepare backend data')
96
-
97
+ job_response.authentication = self.authentication
97
98
  update_job_metadata(job_response, self.callback_dict.get(
98
99
  InvocationStep.PREPARATION).on_error)
99
100
 
@@ -113,7 +114,7 @@ class Invocation(ABC):
113
114
 
114
115
  try:
115
116
  if self.backend_information is None:
116
- self.logger.error(
117
+ self.logger.exception(
117
118
  "Backend information is None before execution")
118
119
  raise ValueError("Backend is not found")
119
120
 
@@ -129,7 +130,7 @@ class Invocation(ABC):
129
130
  device = self._create_device(provider)
130
131
 
131
132
  except Exception as exception:
132
- self.logger.error(
133
+ self.logger.exception(
133
134
  f"Exception when create provider or device: {exception}",
134
135
  exc_info=True)
135
136
 
@@ -182,8 +183,8 @@ class Invocation(ABC):
182
183
  return circuit
183
184
 
184
185
  except Exception as exception:
185
- self.logger.error(f'Error when prepare circuit: {exception}',
186
- exc_info=True)
186
+ self.logger.exception(f'Error when prepare circuit: {exception}',
187
+ exc_info=True)
187
188
 
188
189
  job_response = build_error_job_response(exception, job_response,
189
190
  message='Error when prepare circuit')
@@ -116,7 +116,7 @@ class JobFetcher(ABC):
116
116
  update_job_metadata(job_response, self.callback_urls[
117
117
  InvocationStep.EXECUTION].on_done)
118
118
  elif job_status == JobStatus.ERROR.value:
119
- self.logger.error("Job resulted in error, handling failure")
119
+ self.logger.exception("Job resulted in error, handling failure")
120
120
 
121
121
  self._handle_failed_job(original_job_result, job_response)
122
122
  update_job_metadata(job_response, self.callback_urls[
@@ -127,7 +127,7 @@ class JobFetcher(ABC):
127
127
  job_response.status_code = StatusCode.POLLING
128
128
 
129
129
  except Exception as exception:
130
- self.logger.error(
130
+ self.logger.exception(
131
131
  f"Exception during job fetch for provider_job_id {self.provider_job_id}: {exception}",
132
132
  exc_info=True)
133
133
 
@@ -151,7 +151,7 @@ class JobFetcher(ABC):
151
151
  def _handle_failed_job(self, original_job_result,
152
152
  job_response: JobResponse):
153
153
  """Handles the job result when the job has encountered an error."""
154
- self.logger.error("Parsing job result from failed job")
154
+ self.logger.exception("Parsing job result from failed job")
155
155
  job_response.job_result = parse(original_job_result)
156
156
 
157
157
  def _process_job_result(self, original_job_result,
@@ -205,7 +205,7 @@ class JobFetcher(ABC):
205
205
  return job_response
206
206
 
207
207
  except Exception as exception:
208
- self.logger.error(f"Exception during analysis: {exception}",
208
+ self.logger.exception(f"Exception during analysis: {exception}",
209
209
  exc_info=True)
210
210
  from ...util.response_utils import build_error_job_response
211
211
  job_response = build_error_job_response(exception, job_response,
@@ -75,7 +75,7 @@ class JobFetching(ABC):
75
75
  InvocationStep.EXECUTION).on_done)
76
76
 
77
77
  elif JobStatus.ERROR.value.__eq__(job_response.job_status):
78
- self.logger.error("Job resulted in error, handling failure")
78
+ self.logger.exception("Job resulted in error, handling failure")
79
79
 
80
80
  job_response.job_result = parse(original_job_result)
81
81
 
@@ -152,7 +152,7 @@ class JobFetching(ABC):
152
152
  return job_response
153
153
 
154
154
  except Exception as exception:
155
- self.logger.error(
155
+ self.logger.exception(
156
156
  "Exception when analyst job result with provider_job_id {0}: {1}".format(
157
157
  self.provider_job_id, str(exception)))
158
158
 
@@ -35,7 +35,7 @@ def update_job_metadata(job_response: JobResponse, callback_url: str):
35
35
 
36
36
 
37
37
  except Exception as exception:
38
- logger.error(f"Error occurred while calling backend: {exception}",
38
+ logger.exception(f"Error occurred while calling backend: {exception}",
39
39
  exc_info=True)
40
40
 
41
41
  raise exception
@@ -60,7 +60,7 @@ class DeviceSelection:
60
60
  response = requests.get(self.url, params=request, headers=header)
61
61
 
62
62
  if response.status_code != 200:
63
- logger.error(
63
+ logger.exception(
64
64
  'Select device fail with status code: {0} and content: {1}'.format(
65
65
  response.status_code, response.content))
66
66
 
@@ -21,11 +21,13 @@ def __custom_format(record):
21
21
 
22
22
  # Get the color for the current level, default to white if the level is unknown
23
23
  level_color = level_colors.get(record["level"].name, "white")
24
- return (
25
- f"<{level_color}>{{level}}</{level_color}> : <green>{{time: %Y-%m-%d %H:%M:%S}}</green> : <yellow>[{context}]</yellow> <{level_color}>{{message}}</{level_color}> : <magenta>{{process}}</magenta>\n")
24
+ return (f"<yellow>[ConsoleJobLog][{context}]</yellow> "
25
+ f"<{level_color}>{{level}}</{level_color}> : "
26
+ f"<green>{{time:YYYY-MM-DD HH:mm:ss.SSS}}</green> : "
27
+ f"<{level_color}>{{message}}</{level_color}>: "
28
+ f"<magenta>{{process}}</magenta>\n")
26
29
 
27
30
 
28
31
  logger.add(sink=sys.stderr, format=__custom_format, # Use custom format function
29
- level="DEBUG", colorize=True
30
- # Enable colors (supported by sys.stderr)
32
+ level="DEBUG", colorize=True# Enable colors (supported by sys.stderr)
31
33
  )
@@ -45,7 +45,7 @@ class InvocationRequest(Request):
45
45
  try:
46
46
  Sdk.resolve(sdk_value)
47
47
  except Exception:
48
- self.logger.error(
48
+ self.logger.exception(
49
49
  'sdk must be a valid Sdk value, got {0}'.format(
50
50
  sdk_value))
51
51
  raise ValueError(
@@ -54,7 +54,7 @@ class InvocationRequest(Request):
54
54
  # Validate authentication (if provided)
55
55
  authentication = request_data.get("authentication")
56
56
  if authentication is not None and not isinstance(authentication, dict):
57
- self.logger.error(
57
+ self.logger.exception(
58
58
  'authentication must be a dictionary if provided, got {0}'.format(
59
59
  type(authentication).__name__))
60
60
  raise ValueError(
@@ -8,7 +8,7 @@ class JobFetchingRequest(Request):
8
8
  def __init__(self, request_data: dict | None):
9
9
 
10
10
  if not isinstance(request_data, dict):
11
- self.logger.error(
11
+ self.logger.exception(
12
12
  f"request_data must be a dictionary, got {type(request_data).__name__}")
13
13
  raise ValueError(
14
14
  f"request_data must be a dictionary, got {type(request_data).__name__}")
@@ -17,7 +17,7 @@ class JobFetchingRequest(Request):
17
17
  provider_authentication = request_data.get("providerAuthentication")
18
18
  if provider_authentication is not None and not isinstance(
19
19
  provider_authentication, dict):
20
- self.logger.error(
20
+ self.logger.exception(
21
21
  f"Invalid provider_authentication: {type(provider_authentication).__name__}")
22
22
  raise ValueError(
23
23
  f"provider_authentication must be a dictionary if provided, got {type(provider_authentication).__name__}")
@@ -23,8 +23,8 @@ class JobResponse(object):
23
23
  self.job_result = job_result
24
24
  self.content_type = content_type
25
25
  self.job_histogram = job_histogram
26
- self.user_identity = authentication.user_identity
27
- self.user_token = authentication.user_token
26
+ self.user_identity = getattr(authentication, 'user_identity', None)
27
+ self.user_token = getattr(authentication, 'user_token', None)
28
28
  self.execution_time = execution_time
29
29
  self.status_code = status_code
30
30
  self.project_header = project_header
@@ -55,7 +55,7 @@ class CustomDevice(Device, ABC):
55
55
  return job_response
56
56
 
57
57
  except Exception as exception:
58
- self.logger.error(
58
+ self.logger.exception(
59
59
  f"Exception when analyzing job result: {exception}",
60
60
  exc_info=True)
61
61
 
@@ -176,7 +176,7 @@ class Device(ABC):
176
176
  return job_response
177
177
 
178
178
  except Exception as exception:
179
- self.logger.error(
179
+ self.logger.exception(
180
180
  f"Exception when analyzing job result: {exception}",
181
181
  exc_info=True)
182
182
 
@@ -0,0 +1,141 @@
1
+ # Quapp Platform Project
2
+ # http_utils.py
3
+ # Copyright © CITYNOW Co. Ltd. All rights reserved.
4
+
5
+ from urllib.parse import urlparse
6
+
7
+ from ..config.logging_config import logger
8
+ from ..data.response.custom_header import CustomHeader
9
+ from ..enum.http_header import HttpHeader
10
+ from ..enum.media_type import MediaType
11
+ from ..enum.token_type import TokenType
12
+
13
+ DEFAULT_WORKSPACE_HEADER = 'workspaceHeader'
14
+ DEFAULT_PROJECT_HEADER = 'projectHeader'
15
+ CUSTOM_HEADER_VALUE = 'value'
16
+ CUSTOM_HEADER_NAME = 'name'
17
+
18
+
19
+ def create_bearer_header(token, project_header: CustomHeader,
20
+ workspace_header: CustomHeader):
21
+ """
22
+ Creates an HTTP Bearer authorization header.
23
+
24
+ This function takes a token as an input and constructs a dictionary containing the
25
+ Bearer authorization header. The header consists of the token type "Bearer"
26
+ followed by the provided token.
27
+
28
+ Parameters:
29
+ token: str
30
+ The token string used for the authorization header.
31
+
32
+ Returns:
33
+ dict
34
+ A dictionary containing the constructed Bearer authorization header.
35
+ """
36
+ project_header_name = getattr(project_header, CUSTOM_HEADER_NAME,
37
+ DEFAULT_PROJECT_HEADER)
38
+ project_header_value = getattr(project_header, CUSTOM_HEADER_VALUE, None)
39
+ workspace_header_name = getattr(workspace_header, CUSTOM_HEADER_NAME,
40
+ DEFAULT_WORKSPACE_HEADER)
41
+ workspace_header_value = getattr(workspace_header, CUSTOM_HEADER_VALUE,
42
+ None)
43
+
44
+ return {
45
+ HttpHeader.AUTHORIZATION.value: TokenType.BEARER.value + ' ' + token,
46
+ project_header_name : project_header_value,
47
+ workspace_header_name : workspace_header_value}
48
+
49
+
50
+ def create_application_json_header(token: str, project_header: CustomHeader,
51
+ workspace_header: CustomHeader) -> dict:
52
+ """
53
+ Generates HTTP headers for JSON-based API requests, including authorization
54
+ and optional custom project and workspace headers.
55
+
56
+ Args:
57
+ token: Authorization token of type string for the HTTP headers.
58
+ project_header: CustomHeader object representing the project-specific
59
+ custom header details.
60
+ workspace_header: CustomHeader object representing the workspace-specific
61
+ custom header details.
62
+
63
+ Returns:
64
+ dict: Dictionary containing HTTP headers with authorization, content type,
65
+ accept headers, and custom project and workspace headers where applicable.
66
+ """
67
+ project_header_name = getattr(project_header, CUSTOM_HEADER_NAME,
68
+ DEFAULT_PROJECT_HEADER)
69
+ project_header_value = getattr(project_header, CUSTOM_HEADER_VALUE, None)
70
+ workspace_header_name = getattr(workspace_header, CUSTOM_HEADER_NAME,
71
+ DEFAULT_WORKSPACE_HEADER)
72
+ workspace_header_value = getattr(workspace_header, CUSTOM_HEADER_VALUE,
73
+ None)
74
+ return {
75
+ HttpHeader.AUTHORIZATION.value: '{0} {1}'.format(TokenType.BEARER.value,
76
+ token),
77
+ HttpHeader.CONTENT_TYPE.value : MediaType.APPLICATION_JSON.value,
78
+ HttpHeader.ACCEPT.value : MediaType.APPLICATION_JSON.value,
79
+ project_header_name : project_header_value,
80
+ workspace_header_name : workspace_header_value}
81
+
82
+
83
+ def get_custom_header(request_data: dict, key: str) -> CustomHeader:
84
+ """
85
+ Retrieve and construct a CustomHeader object from request data.
86
+
87
+ This function accesses a specified key in the request_data dictionary
88
+ to extract the necessary information for creating a CustomHeader
89
+ object. It retrieves the 'name' and 'value' properties from the
90
+ nested dictionary corresponding to the provided key.
91
+
92
+ Parameters:
93
+ request_data (dict): The dictionary containing header details.
94
+ key (str): The key used to extract the relevant header data.
95
+
96
+ Returns:
97
+ CustomHeader: An object that encapsulates the extracted 'name' and
98
+ 'value' of the custom header.
99
+ """
100
+ custom_header = request_data.get(key, {})
101
+ return CustomHeader(name=custom_header.get(CUSTOM_HEADER_NAME),
102
+ value=custom_header.get(CUSTOM_HEADER_VALUE))
103
+
104
+
105
+ def get_job_id_from_url(url: str):
106
+ """
107
+ Extracts the job ID from a given URL. This function securely parses the given URL
108
+ and attempts to retrieve the job ID located in the path, typically after the 'jobs'
109
+ keyword. If the pattern is not found or the URL is invalid, it returns None.
110
+
111
+ Args:
112
+ url: The URL string from which the job ID will be extracted.
113
+
114
+ Returns:
115
+ str or None: The extracted job ID if present, otherwise returns None.
116
+ """
117
+
118
+ path = urlparse(url).path
119
+ segments = [seg for seg in path.split('/') if seg]
120
+ job_id = None
121
+
122
+ logger.debug(f'Parsed URL segments: {segments}')
123
+
124
+ if 'jobs' in segments:
125
+ idx = segments.index('jobs')
126
+ try:
127
+ job_id = segments[idx + 1]
128
+ logger.debug(f"Found job id after 'jobs': {job_id}")
129
+ except IndexError:
130
+ logger.exception("No job id found after 'jobs'")
131
+ elif 'job' in segments:
132
+ idx = segments.index('job')
133
+ try:
134
+ job_id = segments[idx + 1]
135
+ logger.debug(f"Found job id after 'job': {job_id}")
136
+ except IndexError:
137
+ logger.exception("No job id found after 'job'")
138
+ else:
139
+ logger.exception("Neither 'job' nor 'jobs' found in URL segments.")
140
+
141
+ return job_id
@@ -0,0 +1,200 @@
1
+ # Quapp Platform Project
2
+ # response_utils.py
3
+ # Copyright © CITYNOW Co. Ltd. All rights reserved.
4
+
5
+ from .http_utils import CUSTOM_HEADER_VALUE
6
+ from ..component.backend.job_fetcher import JobFetcher
7
+ from ..data.promise.post_processing_promise import PostProcessingPromise
8
+ from ..data.response.job_response import JobResponse
9
+ from ..enum.status.job_status import JobStatus
10
+ from ..enum.status.status_code import StatusCode
11
+
12
+ AUTHENTICATION = 'authentication'
13
+ BACKEND_AUTHENTICATION = 'backend_authentication'
14
+ PROJECT_HEADER = 'project_header'
15
+ PROVIDER_JOB_ID = 'provider_job_id'
16
+ WORKSPACE_HEADER = 'workspace_header'
17
+
18
+
19
+ def generate_response(job_response: JobResponse) -> dict:
20
+ """
21
+ Generates a standardized response dictionary based on the given job response.
22
+
23
+ This function processes the provided JobResponse instance and constructs a
24
+ response dictionary containing relevant job details such as status code,
25
+ execution time, job results, and additional attributes if they exist. It ensures
26
+ that all necessary information is included in the response for further
27
+ processing or communication with other systems.
28
+
29
+ Parameters:
30
+ job_response (JobResponse): An instance of the JobResponse class containing
31
+ detailed job information.
32
+
33
+ Returns:
34
+ dict: A dictionary containing the processed job response with keys such as
35
+ 'statusCode', 'body', 'userIdentity', 'userToken', 'projectId', and
36
+ 'workspaceId'.
37
+
38
+ Raises:
39
+ This function does not explicitly raise exceptions.
40
+ """
41
+ if job_response:
42
+ status_code = job_response.status_code.value
43
+ body = {'providerJobId': job_response.provider_job_id,
44
+ 'jobStatus' : job_response.job_status,
45
+ 'jobResult' : job_response.job_result,
46
+ 'contentType' : job_response.content_type.value,
47
+ 'histogram' : job_response.job_histogram,
48
+ 'executionTime': job_response.execution_time}
49
+
50
+ # Add 'shots' only if it exists in the job_response
51
+ if hasattr(job_response, 'shots'):
52
+ body['shots'] = job_response.shots
53
+
54
+ else:
55
+ status_code = job_response.status_code.value
56
+ body = 'Error in function code. Please contact the developer.'
57
+
58
+ return {'statusCode' : status_code,
59
+ 'body' : body,
60
+ 'userIdentity': job_response.user_identity,
61
+ 'userToken' : job_response.user_token,
62
+ 'projectId' : getattr(
63
+ job_response.project_header, CUSTOM_HEADER_VALUE, None),
64
+ 'workspaceId' : getattr(
65
+ job_response.workspace_header, CUSTOM_HEADER_VALUE, None)}
66
+
67
+
68
+ def build_done_job_response(fetcher: JobFetcher = None,
69
+ post_promise: PostProcessingPromise = None) -> JobResponse | None:
70
+ """
71
+ Builds a JobResponse object for a completed job.
72
+
73
+ This function assembles a JobResponse object which signifies the completion
74
+ of a job. It extracts relevant information such as the provider job ID,
75
+ authentication details, project header, and workspace header, and sets the
76
+ status code to indicate the job is done. If both input parameters are missing,
77
+ the function returns None.
78
+
79
+ Parameters:
80
+ fetcher (JobFetcher, optional): An optional job fetcher instance used to
81
+ extract the provider job ID and other details.
82
+ post_promise (PostProcessingPromise, optional): An optional post-processing
83
+ promise instance to assist in extracting relevant details.
84
+
85
+ Returns:
86
+ JobResponse | None: A JobResponse object with the completed job information,
87
+ or None if no inputs were provided.
88
+ """
89
+ return JobResponse(
90
+ provider_job_id=__extract_value(PROVIDER_JOB_ID, fetcher,
91
+ post_promise),
92
+ authentication=__extract_authentication(fetcher, post_promise),
93
+ project_header=__extract_value(PROJECT_HEADER, fetcher,
94
+ post_promise),
95
+ workspace_header=__extract_value(WORKSPACE_HEADER, fetcher,
96
+ post_promise),
97
+ status_code=StatusCode.DONE)
98
+
99
+
100
+ def build_error_job_response(exception: Exception,
101
+ job_response: JobResponse = None,
102
+ fetcher: JobFetcher = None,
103
+ post_promise: PostProcessingPromise = None,
104
+ message: str = None) -> JobResponse:
105
+ """
106
+ Builds and returns a JobResponse object populated with error-related information.
107
+
108
+ This function constructs a JobResponse object that provides comprehensive details
109
+ about an error occurrence. It extracts specific headers and identifiers from
110
+ the given fetcher or post-promise if provided, and includes information
111
+ about the error such as the exception and a corresponding message. The status
112
+ and result of the job indicate an error state.
113
+
114
+ Parameters:
115
+ exception: Exception
116
+ The exception instance representing the error that occurred.
117
+
118
+ job_response: JobResponse, optional
119
+ A JobResponse object to populate with error details. If not provided,
120
+ a new JobResponse object will be created.
121
+
122
+ fetcher: JobFetcher, optional
123
+ An optional fetcher from which relevant headers and identifiers can
124
+ be extracted.
125
+
126
+ post_promise: PostProcessingPromise, optional
127
+ An optional post-processing promise from which relevant headers and
128
+ identifiers can be extracted.
129
+
130
+ message: str, optional
131
+ A custom error message to be included in the job response. If not
132
+ specified, a default message "Unknown error occurred." will be used.
133
+
134
+ Returns:
135
+ JobResponse
136
+ A JobResponse object populated with error-related details such as
137
+ status, exception, and error message.
138
+ """
139
+ if job_response is None:
140
+ job_response = JobResponse()
141
+ job_response.provider_job_id = __extract_value(PROVIDER_JOB_ID, fetcher,
142
+ post_promise)
143
+ job_response.authentication = __extract_authentication(fetcher,
144
+ post_promise)
145
+ job_response.project_header = __extract_value(PROJECT_HEADER, fetcher,
146
+ post_promise)
147
+ job_response.tenant_header = __extract_value(WORKSPACE_HEADER, fetcher,
148
+ post_promise)
149
+ job_response.status_code = StatusCode.ERROR
150
+ job_response.job_status = JobStatus.ERROR.value
151
+ job_response.job_result = {'message' : message or 'Unknown error occurred.',
152
+ 'exception': str(exception)}
153
+ return job_response
154
+
155
+
156
+ def __extract_authentication(fetcher: JobFetcher,
157
+ post_promise: PostProcessingPromise):
158
+ """
159
+ Extract authentication information from the provided fetcher or post promise.
160
+
161
+ This function retrieves authentication data by first attempting to extract it from
162
+ the JobFetcher instance. If it's not available on the JobFetcher, it then falls back
163
+ to extracting it from the PostProcessingPromise instance. If neither source contains
164
+ authentication data, a default value of None is returned.
165
+
166
+ Parameters:
167
+ fetcher (JobFetcher): The JobFetcher instance to retrieve authentication data from.
168
+ post_promise (PostProcessingPromise): The PostProcessingPromise instance to retrieve
169
+ authentication data from if not available on the fetcher.
170
+
171
+ Returns:
172
+ Any: The extracted authentication data, or None if no authentication data is found.
173
+ """
174
+ return getattr(fetcher, BACKEND_AUTHENTICATION,
175
+ getattr(post_promise, AUTHENTICATION, None))
176
+
177
+
178
+ def __extract_value(property_name: str, fetcher: JobFetcher,
179
+ post_promise: PostProcessingPromise):
180
+ """
181
+ Extracts a specified property value from a fetcher object or a fallback
182
+ post-processing promise object.
183
+
184
+ Parameters:
185
+ property_name: str
186
+ The name of the property to extract.
187
+ fetcher: JobFetcher
188
+ The primary object from which the property value is retrieved.
189
+ post_promise: PostProcessingPromise
190
+ The fallback object from which the property value is retrieved if
191
+ unavailable in the fetcher.
192
+
193
+ Returns:
194
+ Any
195
+ The value of the specified property retrieved from the fetcher or the
196
+ fallback post-promise object.
197
+
198
+ """
199
+ return getattr(fetcher, property_name,
200
+ getattr(post_promise, property_name, None))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quapp-common
3
- Version: 0.0.11.dev4
3
+ Version: 0.0.11.dev6
4
4
  Summary: Quapp common library supporting Quapp Platform for Quantum Computing
5
5
  Author-email: "CITYNOW Co. Ltd. " <corp@citynow.vn>
6
6
  License: The MIT License (MIT)
@@ -1,93 +0,0 @@
1
- # Quapp Platform Project
2
- # http_utils.py
3
- # Copyright © CITYNOW Co. Ltd. All rights reserved.
4
-
5
- from urllib.parse import urlparse
6
-
7
- from ..config.logging_config import logger
8
- from ..data.response.custom_header import CustomHeader
9
- from ..enum.http_header import HttpHeader
10
- from ..enum.media_type import MediaType
11
- from ..enum.token_type import TokenType
12
-
13
- CUSTOM_HEADER_VALUE = 'value'
14
- CUSTOM_HEADER_NAME = 'name'
15
-
16
-
17
- def create_bearer_header(token, project_header: CustomHeader = None,
18
- workspace_header: CustomHeader = None):
19
- """
20
- Creates an HTTP Bearer authorization header.
21
-
22
- This function takes a token as an input and constructs a dictionary containing the
23
- Bearer authorization header. The header consists of the token type "Bearer"
24
- followed by the provided token.
25
-
26
- Parameters:
27
- token: str
28
- The token string used for the authorization header.
29
-
30
- Returns:
31
- dict
32
- A dictionary containing the constructed Bearer authorization header.
33
- """
34
-
35
- return {
36
- HttpHeader.AUTHORIZATION.value: TokenType.BEARER.value + ' ' + token,
37
- project_header.name : project_header.value,
38
- workspace_header.name : workspace_header.value}
39
-
40
-
41
- def create_application_json_header(token: str, project_header: CustomHeader,
42
- workspace_header: CustomHeader):
43
- return {
44
- HttpHeader.AUTHORIZATION.value: TokenType.BEARER.value + ' ' + token,
45
- HttpHeader.CONTENT_TYPE.value : MediaType.APPLICATION_JSON.value,
46
- HttpHeader.ACCEPT.value : MediaType.APPLICATION_JSON.value,
47
- project_header.name : project_header.value,
48
- workspace_header.name : workspace_header.value}
49
-
50
-
51
- def get_custom_header(request_data: dict, key: str) -> CustomHeader:
52
- custom_header = request_data.get(key, {})
53
- return CustomHeader(name=custom_header.get(CUSTOM_HEADER_NAME),
54
- value=custom_header.get(CUSTOM_HEADER_VALUE))
55
-
56
-
57
- def get_job_id_from_url(url: str):
58
- """
59
- Extracts the job ID from a given URL. This function securely parses the given URL
60
- and attempts to retrieve the job ID located in the path, typically after the 'jobs'
61
- keyword. If the pattern is not found or the URL is invalid, it returns None.
62
-
63
- Args:
64
- url: The URL string from which the job ID will be extracted.
65
-
66
- Returns:
67
- str or None: The extracted job ID if present, otherwise returns None.
68
- """
69
-
70
- path = urlparse(url).path
71
- segments = [seg for seg in path.split('/') if seg]
72
- job_id = None
73
-
74
- logger.debug(f'Parsed URL segments: {segments}')
75
-
76
- if 'jobs' in segments:
77
- idx = segments.index('jobs')
78
- try:
79
- job_id = segments[idx + 1]
80
- logger.debug(f"Found job id after 'jobs': {job_id}")
81
- except IndexError:
82
- logger.error("No job id found after 'jobs'")
83
- elif 'job' in segments:
84
- idx = segments.index('job')
85
- try:
86
- job_id = segments[idx + 1]
87
- logger.debug(f"Found job id after 'job': {job_id}")
88
- except IndexError:
89
- logger.error("No job id found after 'job'")
90
- else:
91
- logger.error("Neither 'job' nor 'jobs' found in URL segments.")
92
-
93
- return job_id
@@ -1,97 +0,0 @@
1
- '''
2
- QApp Platform Project response_utils.py Copyright © CITYNOW Co. Ltd. All rights reserved.
3
- '''
4
-
5
- from ..component.backend.job_fetcher import JobFetcher
6
- from ..config.logging_config import job_logger
7
- from ..data.promise.post_processing_promise import PostProcessingPromise
8
- from ..data.response.job_response import JobResponse
9
- from ..enum.status.job_status import JobStatus
10
- from ..enum.status.status_code import StatusCode
11
-
12
- AUTHENTICATION = 'authentication'
13
- BACKEND_AUTHENTICATION = 'backend_authentication'
14
- PROJECT_HEADER = 'project_header'
15
- PROVIDER_JOB_ID = 'provider_job_id'
16
- WORKSPACE_HEADER = 'workspace_header'
17
-
18
-
19
- def generate_response(job_response: JobResponse) -> dict:
20
- if job_response:
21
- status_code = job_response.status_code.value
22
- body = {'providerJobId': job_response.provider_job_id,
23
- 'jobStatus' : job_response.job_status,
24
- 'jobResult' : job_response.job_result,
25
- 'contentType' : job_response.content_type.value,
26
- 'histogram' : job_response.job_histogram,
27
- 'executionTime': job_response.execution_time}
28
-
29
- # Add 'shots' only if it exists in the job_response
30
- if hasattr(job_response, 'shots'):
31
- body['shots'] = job_response.shots
32
-
33
- else:
34
- status_code = job_response.status_code.value
35
- body = 'Error in function code. Please contact the developer.'
36
-
37
- return {'statusCode' : status_code, 'body': body,
38
- 'userIdentity': job_response.user_identity,
39
- 'userToken' : job_response.user_token,
40
- 'projectId' : job_response.project_header.value,
41
- 'workspaceId' : job_response.workspace_header.value}
42
-
43
-
44
- def build_done_job_response(fetcher: JobFetcher = None,
45
- post_promise: PostProcessingPromise = None) -> JobResponse | None:
46
- return JobResponse(
47
- provider_job_id=__extract_provider_job_id(fetcher, post_promise),
48
- authentication=__extract_authentication(fetcher, post_promise),
49
- project_header=__extract_project_header(fetcher, post_promise),
50
- workspace_header=__extract_workspace_header(fetcher, post_promise),
51
- status_code=StatusCode.DONE)
52
-
53
-
54
- def build_error_job_response(exception: Exception,
55
- job_response: JobResponse = None,
56
- fetcher: JobFetcher = None,
57
- post_promise: PostProcessingPromise = None,
58
- message: str = None) -> JobResponse:
59
- if job_response is None:
60
- job_response = JobResponse()
61
- job_response.provider_job_id = __extract_provider_job_id(fetcher,
62
- post_promise)
63
- job_response.authentication = __extract_authentication(fetcher,
64
- post_promise)
65
- job_response.project_header = __extract_project_header(fetcher,
66
- post_promise)
67
- job_response.tenant_header = __extract_workspace_header(fetcher,
68
- post_promise)
69
- job_response.status_code = StatusCode.ERROR
70
- job_response.job_status = JobStatus.ERROR.value
71
- job_response.job_result = {'message' : message or 'Unknown error occurred.',
72
- 'exception': str(exception)}
73
- return job_response
74
-
75
-
76
- def __extract_authentication(fetcher: JobFetcher,
77
- post_promise: PostProcessingPromise):
78
- return getattr(fetcher, BACKEND_AUTHENTICATION,
79
- getattr(post_promise, AUTHENTICATION, None))
80
-
81
-
82
- def __extract_project_header(fetcher: JobFetcher,
83
- post_promise: PostProcessingPromise):
84
- return getattr(fetcher, PROJECT_HEADER,
85
- getattr(post_promise, PROJECT_HEADER, None))
86
-
87
-
88
- def __extract_provider_job_id(fetcher: JobFetcher,
89
- post_promise: PostProcessingPromise):
90
- return getattr(fetcher, PROVIDER_JOB_ID,
91
- getattr(post_promise, PROVIDER_JOB_ID, None))
92
-
93
-
94
- def __extract_workspace_header(fetcher: JobFetcher,
95
- post_promise: PostProcessingPromise):
96
- return getattr(fetcher, WORKSPACE_HEADER,
97
- getattr(post_promise, WORKSPACE_HEADER, None))