qase-python-commons 4.0.0__py3-none-any.whl → 4.1.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.

Potentially problematic release.


This version of qase-python-commons might be problematic. Click here for more details.

qase/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """
2
+ Qase Python Commons package.
3
+ """
qase/commons/config.py CHANGED
@@ -1,18 +1,28 @@
1
1
  import os
2
2
  import json
3
- from .logger import Logger
3
+ from .logger import Logger, LoggingOptions
4
4
  from .models.config.qaseconfig import QaseConfig, Mode
5
+ from .utils import QaseUtils
5
6
 
6
7
 
7
8
  class ConfigManager:
8
9
 
9
10
  def __init__(self, config_file='./qase.config.json'):
10
- self.logger = Logger()
11
11
  self.__config_file = config_file
12
12
  self.config = QaseConfig()
13
13
 
14
+ # Initialize temporary logger for error handling during config loading
15
+ self.logger = Logger(debug=False)
16
+
14
17
  self.__load_file_config()
15
18
  self.__load_env_config()
19
+
20
+ # Re-initialize logger with proper logging options after config is loaded
21
+ logging_options = LoggingOptions(
22
+ console=self.config.logging.console if self.config.logging.console is not None else True,
23
+ file=self.config.logging.file if self.config.logging.file is not None else self.config.debug
24
+ )
25
+ self.logger = Logger(debug=self.config.debug, logging_options=logging_options)
16
26
 
17
27
  def validate_config(self):
18
28
  errors: list[str] = []
@@ -63,6 +73,11 @@ class ConfigManager:
63
73
  config.get("excludeParams")
64
74
  )
65
75
 
76
+ if config.get("statusMapping"):
77
+ self.config.set_status_mapping(
78
+ config.get("statusMapping")
79
+ )
80
+
66
81
  if config.get("executionPlan"):
67
82
  execution_plan = config.get("executionPlan")
68
83
  if execution_plan.get("path"):
@@ -196,6 +211,9 @@ class ConfigManager:
196
211
  xfail_status.get("xpass")
197
212
  )
198
213
 
214
+ if config.get("logging"):
215
+ self.config.set_logging(config.get("logging"))
216
+
199
217
  except Exception as e:
200
218
  self.logger.log("Failed to load config from file", "error")
201
219
 
@@ -224,6 +242,19 @@ class ConfigManager:
224
242
  self.config.set_exclude_params(
225
243
  [param.strip() for param in value.split(',')])
226
244
 
245
+ if key == 'QASE_STATUS_MAPPING':
246
+ # Parse status mapping from environment variable
247
+ # Format: "source1=target1,source2=target2"
248
+ if value:
249
+ mapping_dict = {}
250
+ pairs = value.split(',')
251
+ for pair in pairs:
252
+ pair = pair.strip()
253
+ if pair and '=' in pair:
254
+ source_status, target_status = pair.split('=', 1)
255
+ mapping_dict[source_status.strip()] = target_status.strip()
256
+ self.config.set_status_mapping(mapping_dict)
257
+
227
258
  if key == 'QASE_EXECUTION_PLAN_PATH':
228
259
  self.config.execution_plan.set_path(value)
229
260
 
@@ -308,5 +339,11 @@ class ConfigManager:
308
339
  if key == 'QASE_PYTEST_XFAIL_STATUS_XPASS':
309
340
  self.config.framework.pytest.xfail_status.set_xpass(value)
310
341
 
342
+ if key == 'QASE_LOGGING_CONSOLE':
343
+ self.config.logging.set_console(QaseUtils.parse_bool(value))
344
+
345
+ if key == 'QASE_LOGGING_FILE':
346
+ self.config.logging.set_file(QaseUtils.parse_bool(value))
347
+
311
348
  except Exception as e:
312
- self.logger.log("Failed to load config from env vars {e}", "error")
349
+ self.logger.log(f"Failed to load config from env vars {e}", "error")
qase/commons/logger.py CHANGED
@@ -1,14 +1,39 @@
1
1
  import os
2
2
  import datetime
3
3
  import threading
4
+ from typing import Optional
5
+
6
+
7
+ class LoggingOptions:
8
+ def __init__(self, console: bool = True, file: bool = False):
9
+ self.console = console
10
+ self.file = file
4
11
 
5
12
 
6
13
  class Logger:
7
14
  _log_file = None
8
15
 
9
- def __init__(self, debug: bool = False, prefix: str = '', dir: str = os.path.join('.', 'logs')) -> None:
16
+ def __init__(self, debug: bool = False, prefix: str = '', dir: str = os.path.join('.', 'logs'),
17
+ logging_options: Optional[LoggingOptions] = None) -> None:
10
18
  self.debug = debug
11
- if self.debug:
19
+ self.prefix = prefix
20
+ self.dir = dir
21
+
22
+ # Initialize logging options
23
+ if logging_options is None:
24
+ # Default behavior: console always enabled, file enabled only in debug mode
25
+ self.logging_options = LoggingOptions(
26
+ console=True,
27
+ file=debug
28
+ )
29
+ else:
30
+ self.logging_options = logging_options
31
+
32
+ # Override with environment variables if set
33
+ self._load_env_logging_options()
34
+
35
+ # Setup file logging if enabled
36
+ if self.logging_options.file:
12
37
  if Logger._log_file is None:
13
38
  timestamp = self._get_timestamp()
14
39
  filename = f'{prefix}_{timestamp}.log'
@@ -21,16 +46,38 @@ class Logger:
21
46
 
22
47
  self.lock = threading.Lock()
23
48
 
49
+ def _load_env_logging_options(self):
50
+ """Load logging options from environment variables"""
51
+ # QASE_LOGGING_CONSOLE
52
+ console_env = os.environ.get('QASE_LOGGING_CONSOLE')
53
+ if console_env is not None:
54
+ self.logging_options.console = console_env.lower() in ('true', '1', 'yes', 'on')
55
+
56
+ # QASE_LOGGING_FILE
57
+ file_env = os.environ.get('QASE_LOGGING_FILE')
58
+ if file_env is not None:
59
+ self.logging_options.file = file_env.lower() in ('true', '1', 'yes', 'on')
60
+
61
+ # Legacy QASE_DEBUG support
62
+ debug_env = os.environ.get('QASE_DEBUG')
63
+ if debug_env is not None and debug_env.lower() in ('true', '1', 'yes', 'on'):
64
+ # When debug is enabled via env, enable file logging if not explicitly disabled
65
+ if not hasattr(self.logging_options, 'file') or self.logging_options.file is None:
66
+ self.logging_options.file = True
67
+
24
68
  def log(self, message: str, level: str = 'info'):
25
69
  time_str = self._get_timestamp("%H:%M:%S")
26
70
  log = f"[Qase][{time_str}][{level}] {message}\n"
27
71
 
28
- try:
29
- print(log, end='')
30
- except (OSError, IOError):
31
- pass
72
+ # Console output
73
+ if self.logging_options.console:
74
+ try:
75
+ print(log, end='')
76
+ except (OSError, IOError):
77
+ pass
32
78
 
33
- if self.debug:
79
+ # File output
80
+ if self.logging_options.file:
34
81
  with self.lock:
35
82
  with open(Logger._log_file, 'a', encoding='utf-8') as f:
36
83
  f.write(log)
@@ -39,6 +86,15 @@ class Logger:
39
86
  if self.debug:
40
87
  self.log(message, 'debug')
41
88
 
89
+ def log_error(self, message: str):
90
+ self.log(message, 'error')
91
+
92
+ def log_warning(self, message: str):
93
+ self.log(message, 'warning')
94
+
95
+ def log_info(self, message: str):
96
+ self.log(message, 'info')
97
+
42
98
  @staticmethod
43
99
  def _get_timestamp(fmt: str = "%Y%m%d"):
44
100
  now = datetime.datetime.now()
@@ -1,5 +1,5 @@
1
1
  from enum import Enum
2
- from typing import List
2
+ from typing import List, Dict, Optional
3
3
 
4
4
  from .framework import Framework
5
5
  from .report import ReportConfig
@@ -14,6 +14,21 @@ class Mode(Enum):
14
14
  off = "off"
15
15
 
16
16
 
17
+ class LoggingConfig(BaseModel):
18
+ console: Optional[bool] = None
19
+ file: Optional[bool] = None
20
+
21
+ def __init__(self):
22
+ self.console = None
23
+ self.file = None
24
+
25
+ def set_console(self, console: bool):
26
+ self.console = console
27
+
28
+ def set_file(self, file: bool):
29
+ self.file = file
30
+
31
+
17
32
  class ExecutionPlan(BaseModel):
18
33
  path: str = None
19
34
 
@@ -36,6 +51,8 @@ class QaseConfig(BaseModel):
36
51
  profilers: list = None
37
52
  framework: Framework = None
38
53
  exclude_params: list = None
54
+ status_mapping: Dict[str, str] = None
55
+ logging: LoggingConfig = None
39
56
 
40
57
  def __init__(self):
41
58
  self.mode = Mode.off
@@ -47,6 +64,8 @@ class QaseConfig(BaseModel):
47
64
  self.framework = Framework()
48
65
  self.profilers = []
49
66
  self.exclude_params = []
67
+ self.status_mapping = {}
68
+ self.logging = LoggingConfig()
50
69
 
51
70
  def set_mode(self, mode: str):
52
71
  if any(mode == e.value for e in Mode.__members__.values()):
@@ -70,3 +89,12 @@ class QaseConfig(BaseModel):
70
89
 
71
90
  def set_exclude_params(self, exclude_params: List[str]):
72
91
  self.exclude_params = exclude_params
92
+
93
+ def set_status_mapping(self, status_mapping: Dict[str, str]):
94
+ self.status_mapping = status_mapping
95
+
96
+ def set_logging(self, logging_config: dict):
97
+ if logging_config.get("console") is not None:
98
+ self.logging.set_console(QaseUtils.parse_bool(logging_config.get("console")))
99
+ if logging_config.get("file") is not None:
100
+ self.logging.set_file(QaseUtils.parse_bool(logging_config.get("file")))
@@ -33,10 +33,16 @@ class Execution(BaseModel):
33
33
  self.thread = thread
34
34
 
35
35
  def set_status(self, status: Optional[str]):
36
- if status in ['passed', 'failed', 'skipped', 'untested', 'invalid']:
36
+ if status is None:
37
37
  self.status = status
38
+ return
39
+
40
+ # Convert to lowercase for validation
41
+ status_lower = status.lower()
42
+ if status_lower in ['passed', 'failed', 'skipped', 'untested', 'invalid', 'disabled', 'blocked']:
43
+ self.status = status_lower
38
44
  else:
39
- raise ValueError('Step status must be one of: passed, failed, skipped, untested, invalid')
45
+ raise ValueError('Step status must be one of: passed, failed, skipped, untested, invalid, disabled, blocked')
40
46
 
41
47
  def get_status(self):
42
48
  return self.status
@@ -11,6 +11,7 @@ from ..models.config.qaseconfig import Mode
11
11
  from typing import Union, List
12
12
 
13
13
  from ..util import get_host_info
14
+ from ..status_mapping.status_mapping import StatusMapping
14
15
 
15
16
  """
16
17
  CoreReporter is a facade for all reporters and it is used to initialize and manage them.
@@ -23,11 +24,17 @@ class QaseCoreReporter:
23
24
  reporter_name: Union[str, None] = None):
24
25
  config.validate_config()
25
26
  self.config = config.config
26
- self.logger = Logger(self.config.debug)
27
+ # Use the logger from ConfigManager instead of creating a new one
28
+ self.logger = config.logger
27
29
  self._execution_plan = None
28
30
  self.profilers = []
29
31
  self.overhead = 0
30
32
 
33
+ # Initialize status mapping
34
+ self.status_mapping = StatusMapping.from_dict(self.config.status_mapping)
35
+ if not self.status_mapping.is_empty():
36
+ self.logger.log_debug(f"Status mapping initialized: {self.status_mapping}")
37
+
31
38
  # self._selective_execution_setup()
32
39
  self.fallback = self._fallback_setup()
33
40
 
@@ -87,6 +94,9 @@ class QaseCoreReporter:
87
94
  ts = time.time()
88
95
  self.logger.log_debug(f"Adding result {result}")
89
96
 
97
+ # Apply status mapping before adding result
98
+ self._apply_status_mapping(result)
99
+
90
100
  self.reporter.add_result(result)
91
101
 
92
102
  self.logger.log_debug(f"Result {result.get_title()} added")
@@ -208,3 +218,26 @@ class QaseCoreReporter:
208
218
  if self.config.fallback == Mode.report:
209
219
  return QaseReport(config=self.config, logger=self.logger)
210
220
  return None
221
+
222
+ def _apply_status_mapping(self, result: Result) -> None:
223
+ """
224
+ Apply status mapping to a test result.
225
+
226
+ This method applies the configured status mapping to the result's execution status.
227
+ The mapping is applied before the result is sent to the reporter.
228
+
229
+ Args:
230
+ result: Test result to apply status mapping to
231
+ """
232
+ if self.status_mapping.is_empty():
233
+ return
234
+
235
+ original_status = result.get_status()
236
+ if not original_status:
237
+ return
238
+
239
+ mapped_status = self.status_mapping.apply_mapping(original_status)
240
+
241
+ if mapped_status != original_status:
242
+ result.execution.set_status(mapped_status)
243
+ self.logger.log_debug(f"Status mapped for '{result.get_title()}': {original_status} -> {mapped_status}")
@@ -0,0 +1,12 @@
1
+ """
2
+ Utilities package for Qase Python Commons.
3
+ """
4
+
5
+ from .status_mapping import StatusMapping, StatusMappingError, create_status_mapping_from_config, create_status_mapping_from_env
6
+
7
+ __all__ = [
8
+ 'StatusMapping',
9
+ 'StatusMappingError',
10
+ 'create_status_mapping_from_config',
11
+ 'create_status_mapping_from_env'
12
+ ]
@@ -0,0 +1,237 @@
1
+ """
2
+ Status mapping utilities for Qase Python Commons.
3
+
4
+ This module provides functionality to map test result statuses from one value to another
5
+ based on configuration. This is useful for standardizing status values across different
6
+ testing frameworks or for custom status transformations.
7
+ """
8
+
9
+ from typing import Dict, Optional, List
10
+ import os
11
+ import logging
12
+
13
+
14
+ class StatusMappingError(Exception):
15
+ """Exception raised when status mapping encounters an error."""
16
+ pass
17
+
18
+
19
+ class StatusMapping:
20
+ """
21
+ Handles mapping of test result statuses.
22
+
23
+ This class provides functionality to:
24
+ - Parse status mapping from configuration
25
+ - Validate status mappings
26
+ - Apply status mappings to test results
27
+ - Support both JSON configuration and environment variables
28
+ """
29
+
30
+ # Valid statuses that can be mapped
31
+ VALID_STATUSES = {
32
+ 'passed', 'failed', 'skipped', 'disabled', 'blocked', 'invalid'
33
+ }
34
+
35
+ def __init__(self, mapping: Optional[Dict[str, str]] = None):
36
+ """
37
+ Initialize StatusMapping with optional mapping dictionary.
38
+
39
+ Args:
40
+ mapping: Dictionary mapping source status to target status
41
+ """
42
+ self.mapping = mapping or {}
43
+ self.logger = logging.getLogger(__name__)
44
+
45
+ @classmethod
46
+ def from_dict(cls, mapping_dict: Dict[str, str]) -> 'StatusMapping':
47
+ """
48
+ Create StatusMapping from dictionary.
49
+
50
+ Args:
51
+ mapping_dict: Dictionary with status mappings
52
+
53
+ Returns:
54
+ StatusMapping instance
55
+
56
+ Raises:
57
+ StatusMappingError: If mapping contains invalid statuses
58
+ """
59
+ instance = cls()
60
+ instance.set_mapping(mapping_dict)
61
+ return instance
62
+
63
+ @classmethod
64
+ def from_env_string(cls, env_string: str) -> 'StatusMapping':
65
+ """
66
+ Create StatusMapping from environment variable string.
67
+
68
+ Expected format: "source1=target1,source2=target2"
69
+
70
+ Args:
71
+ env_string: Environment variable string
72
+
73
+ Returns:
74
+ StatusMapping instance
75
+
76
+ Raises:
77
+ StatusMappingError: If string format is invalid
78
+ """
79
+ instance = cls()
80
+ instance.parse_env_string(env_string)
81
+ return instance
82
+
83
+ def set_mapping(self, mapping_dict: Dict[str, str]) -> None:
84
+ """
85
+ Set status mapping from dictionary.
86
+
87
+ Args:
88
+ mapping_dict: Dictionary with status mappings
89
+
90
+ Raises:
91
+ StatusMappingError: If mapping contains invalid statuses
92
+ """
93
+ if not isinstance(mapping_dict, dict):
94
+ raise StatusMappingError("Mapping must be a dictionary")
95
+
96
+ # Validate all statuses in the mapping
97
+ for source_status, target_status in mapping_dict.items():
98
+ if source_status not in self.VALID_STATUSES:
99
+ raise StatusMappingError(f"Invalid source status: {source_status}")
100
+ if target_status not in self.VALID_STATUSES:
101
+ raise StatusMappingError(f"Invalid target status: {target_status}")
102
+
103
+ self.mapping = mapping_dict.copy()
104
+ self.logger.debug(f"Status mapping set: {self.mapping}")
105
+
106
+ def parse_env_string(self, env_string: str) -> None:
107
+ """
108
+ Parse status mapping from environment variable string.
109
+
110
+ Expected format: "source1=target1,source2=target2"
111
+
112
+ Args:
113
+ env_string: Environment variable string
114
+
115
+ Raises:
116
+ StatusMappingError: If string format is invalid
117
+ """
118
+ if not env_string or not env_string.strip():
119
+ self.mapping = {}
120
+ return
121
+
122
+ mapping_dict = {}
123
+ pairs = env_string.split(',')
124
+
125
+ for pair in pairs:
126
+ pair = pair.strip()
127
+ if not pair:
128
+ continue
129
+
130
+ if '=' not in pair:
131
+ raise StatusMappingError(f"Invalid mapping format: {pair}. Expected 'source=target'")
132
+
133
+ source_status, target_status = pair.split('=', 1)
134
+ source_status = source_status.strip()
135
+ target_status = target_status.strip()
136
+
137
+ if not source_status or not target_status:
138
+ raise StatusMappingError(f"Empty status in mapping: {pair}")
139
+
140
+ mapping_dict[source_status] = target_status
141
+
142
+ self.set_mapping(mapping_dict)
143
+
144
+ def apply_mapping(self, status: str) -> str:
145
+ """
146
+ Apply status mapping to a given status.
147
+
148
+ Args:
149
+ status: Original status
150
+
151
+ Returns:
152
+ Mapped status if mapping exists, otherwise original status
153
+ """
154
+ if not status:
155
+ return status
156
+
157
+ if status in self.mapping:
158
+ mapped_status = self.mapping[status]
159
+ self.logger.debug(f"Status mapped: {status} -> {mapped_status}")
160
+ return mapped_status
161
+
162
+ return status
163
+
164
+ def get_mapping(self) -> Dict[str, str]:
165
+ """
166
+ Get current status mapping.
167
+
168
+ Returns:
169
+ Dictionary with current status mappings
170
+ """
171
+ return self.mapping.copy()
172
+
173
+ def is_empty(self) -> bool:
174
+ """
175
+ Check if mapping is empty.
176
+
177
+ Returns:
178
+ True if no mappings are defined
179
+ """
180
+ return len(self.mapping) == 0
181
+
182
+ def validate(self) -> List[str]:
183
+ """
184
+ Validate current mapping and return any issues.
185
+
186
+ Returns:
187
+ List of validation error messages
188
+ """
189
+ errors = []
190
+
191
+ for source_status, target_status in self.mapping.items():
192
+ if source_status not in self.VALID_STATUSES:
193
+ errors.append(f"Invalid source status: {source_status}")
194
+ if target_status not in self.VALID_STATUSES:
195
+ errors.append(f"Invalid target status: {target_status}")
196
+
197
+ return errors
198
+
199
+ def __str__(self) -> str:
200
+ """String representation of the mapping."""
201
+ return str(self.mapping)
202
+
203
+ def __repr__(self) -> str:
204
+ """Detailed string representation."""
205
+ return f"StatusMapping({self.mapping})"
206
+
207
+
208
+ def create_status_mapping_from_config(config_value: Optional[Dict[str, str]]) -> StatusMapping:
209
+ """
210
+ Create StatusMapping from configuration value.
211
+
212
+ Args:
213
+ config_value: Configuration dictionary or None
214
+
215
+ Returns:
216
+ StatusMapping instance
217
+ """
218
+ if config_value is None:
219
+ return StatusMapping()
220
+
221
+ return StatusMapping.from_dict(config_value)
222
+
223
+
224
+ def create_status_mapping_from_env(env_var_name: str = 'STATUS_MAPPING') -> StatusMapping:
225
+ """
226
+ Create StatusMapping from environment variable.
227
+
228
+ Args:
229
+ env_var_name: Name of environment variable
230
+
231
+ Returns:
232
+ StatusMapping instance
233
+ """
234
+ env_value = os.getenv(env_var_name)
235
+ if env_value:
236
+ return StatusMapping.from_env_string(env_value)
237
+ return StatusMapping()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qase-python-commons
3
- Version: 4.0.0
3
+ Version: 4.1.0
4
4
  Summary: A library for Qase TestOps and Qase Report
5
5
  Author-email: Qase Team <support@qase.io>
6
6
  Project-URL: Homepage, https://github.com/qase-tms/qase-python/tree/main/qase-python-commons
@@ -21,7 +21,7 @@ Requires-Python: >=3.9
21
21
  Description-Content-Type: text/markdown
22
22
  Requires-Dist: certifi>=2024.2.2
23
23
  Requires-Dist: attrs>=23.2.0
24
- Requires-Dist: qase-api-client~=2.0.0
24
+ Requires-Dist: qase-api-client~=2.0.1
25
25
  Requires-Dist: qase-api-v2-client~=2.0.0
26
26
  Requires-Dist: more_itertools
27
27
  Provides-Extra: testing
@@ -36,7 +36,12 @@ Requires-Dist: urllib3; extra == "testing"
36
36
 
37
37
  ## Description
38
38
 
39
- This package contains reporters for Qase TestOps and Qase Report that are used in [qase-pytest](https://github.com/qase-tms/qase-python/tree/master/qase-pytest) and [qase-robotframework](https://github.com/qase-tms/qase-python/tree/master/qase-robotframework).
39
+ This package contains reporters for Qase TestOps and Qase Report that are used in following projects:
40
+
41
+ - [qase-pytest](https://github.com/qase-tms/qase-python/tree/main/qase-pytest)
42
+ - [qase-robotframework](https://github.com/qase-tms/qase-python/tree/main/qase-robotframework)
43
+ - [qase-behave](https://github.com/qase-tms/qase-python/tree/main/qase-behave)
44
+ - [qase-tavern](https://github.com/qase-tms/qase-python/tree/main/qase-tavern)
40
45
 
41
46
  ## How to install
42
47
 
@@ -1,7 +1,8 @@
1
+ qase/__init__.py,sha256=FLefSJnT6MTsTfZSDjMbMImI6674fAoJ5ZT3PNWGcRo,37
1
2
  qase/commons/__init__.py,sha256=3HI65PJES4Q6YvtkSuRPh6tZboTETJo8wbdHlNYaePU,323
2
- qase/commons/config.py,sha256=yqV8I4rtQz5siOcP1gWXNwWw7RXDRDWLB_Hk0C2yEug,13322
3
+ qase/commons/config.py,sha256=ivunk-6IBfsXyRf-_Y_Je_K8boD4gDupv8v2Yf3nm2M,15162
3
4
  qase/commons/loader.py,sha256=-MMY4HgSI6q1xq3NaJoq_w4liM73qdFKjYLVCT1E7Pc,1064
4
- qase/commons/logger.py,sha256=KEQr8G0eFZxlI3LJIaaNWOKD8o3NhKsZD06swXFn3FI,1313
5
+ qase/commons/logger.py,sha256=V_QTSDWNnUgd0j58rydYYN1AYOu3wa9T2fWjpOyxyuA,3430
5
6
  qase/commons/utils.py,sha256=utPRoYyThLs2tgD1lmjkwJ9KZuE7ZjliUiZkJJWFca0,3330
6
7
  qase/commons/client/api_v1_client.py,sha256=_dpvldRonALKubInPU03hYhyGJqxeq3DiIuyjDeOngg,11543
7
8
  qase/commons/client/api_v2_client.py,sha256=GsIrXJcBw6GtzvJjbjMYa0tvUIxEEe4pALRN2LBMKPM,9043
@@ -12,7 +13,7 @@ qase/commons/models/attachment.py,sha256=cGfB0BaTDVfA7euJubKvi2cXXNvlSm_dy5x-ak3
12
13
  qase/commons/models/basemodel.py,sha256=0j8E-LE6hxAKQPYLNM9qThor9s2ZndZys_kibeoLImo,426
13
14
  qase/commons/models/external_link.py,sha256=bdXj7pNHHkfb3dcYtXN8sNp-HFxHvZeYz8QIPGcvX0w,1317
14
15
  qase/commons/models/relation.py,sha256=HymHeh1uBcoQnTs4Vra7WJ_KFkhryj5o7cShjoGQImI,511
15
- qase/commons/models/result.py,sha256=8dES10mjBx7SdltAlKDB39bO3k1-xGQVKrHWn19gXK0,3793
16
+ qase/commons/models/result.py,sha256=8Tp-R34vxZhxmgqQErr2vH8ie-ZidDNqxZy6Xty4uqg,4019
16
17
  qase/commons/models/run.py,sha256=ANbljW1mgua4JF1wGZG9S-jnUpI7FC4F15sAQOoNnko,3096
17
18
  qase/commons/models/runtime.py,sha256=h0kJcPYRo8dglIQVFyzh-cFpWb3bAaQaIq5wV8VfClM,1508
18
19
  qase/commons/models/step.py,sha256=svH2jvxiJRBugCjKdifXzOKli9OzQQvn8lsPhzd20yY,5433
@@ -21,7 +22,7 @@ qase/commons/models/config/batch.py,sha256=X0H8SVOCCD2pV6LSMqjI-tIjRcLifnrM5Mare
21
22
  qase/commons/models/config/connection.py,sha256=wK2fGjc0G0rMVVhPnjw_t_M1YWZwANlhwl-awmI7XSo,516
22
23
  qase/commons/models/config/framework.py,sha256=sSWKQp18zxiS_79_XDH2hkHLpYEQnyH3bRquaT8XYYY,1803
23
24
  qase/commons/models/config/plan.py,sha256=JbAY7qfGXYreXOLa32OLxw6z0paeCCf87-2b1m8xkks,137
24
- qase/commons/models/config/qaseconfig.py,sha256=ho_22bcouoJb1f68EGffeBs_ovK3DVyfbFrYXwQFrWs,1918
25
+ qase/commons/models/config/qaseconfig.py,sha256=oWc03PKV8-jb3JNgs_tqQP-22wsKZW25eSaAacYl4no,2838
25
26
  qase/commons/models/config/report.py,sha256=g3Z2B3Tewaasjc1TMj2mkXxz0Zc1C39sHeTXH0MRM2Y,497
26
27
  qase/commons/models/config/run.py,sha256=UGE5PA_EyiFpfIev-TOob1BocxT1vdUdWW3ELMD58zQ,1239
27
28
  qase/commons/models/config/testops.py,sha256=rJ9wW-VMt-5XaoPdRUKeM9rlV0eTiRINEhEulFVczv0,1893
@@ -30,13 +31,15 @@ qase/commons/profilers/db.py,sha256=Am1tvvLgJq4_A8JsuSeBGf47BD2lnSX-5KiMjSgr-Ko,
30
31
  qase/commons/profilers/network.py,sha256=zKNBnTQG4BMg8dn8O--tQzQLpu-qs5ADhHEnqIas0gM,4950
31
32
  qase/commons/profilers/sleep.py,sha256=HT6h0R-2XHZAoBYRxS2T_KC8RrnEoVjP7MXusaE4Nec,1624
32
33
  qase/commons/reporters/__init__.py,sha256=J0aNLzb_MPPT_zF8BtX_w9nj_U7Ad06RGpyWK5Pxq1o,169
33
- qase/commons/reporters/core.py,sha256=JyFMGhcDOr3nBlhh0Ca8Dx6BmvNBoG8m1Y4hpC0qIog,8185
34
+ qase/commons/reporters/core.py,sha256=FJx1kxXkUz9vwkSAXapkNS3vF97Igvlq7Yyjp-4BpeI,9539
34
35
  qase/commons/reporters/report.py,sha256=ZLwtVn5gjwgJFtfbpLUO-vW3M3skEq3AhKJwtmM0nUw,4810
35
36
  qase/commons/reporters/testops.py,sha256=uph_8upIt5bu4ncqyK7ehXR9NqtpZ34wGUBFLVJKOpE,7481
37
+ qase/commons/status_mapping/__init__.py,sha256=NPsWKDC7rGSCYfVunnGnHxL0_HmKWH7RBaMjWTH_Mtk,322
38
+ qase/commons/status_mapping/status_mapping.py,sha256=kohSLNK6_qbMSP-M8gxHTTmOECgzDE3XvLqOzidPlYI,7213
36
39
  qase/commons/util/__init__.py,sha256=0sRRfrMOIPCHpk9tXM94Pj10qrk18B61qEcbLpRjw_I,74
37
40
  qase/commons/util/host_data.py,sha256=n8o5PDs8kELCZZ5GR7Jug6LsgZHWJudU7iRmZHRdrlw,5264
38
41
  qase/commons/validators/base.py,sha256=wwSn-4YiuXtfGMGnSKgo9Vm5hAKevVmmfd2Ro6Q7MYQ,173
39
- qase_python_commons-4.0.0.dist-info/METADATA,sha256=i3e1yWuvsf7RkNj4zWPVbER7RikCOgaPsBirjLVhVqo,1808
40
- qase_python_commons-4.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
41
- qase_python_commons-4.0.0.dist-info/top_level.txt,sha256=Mn5aFk7H7Uia4s1NRDsvebu8vCrFy9nOuRIBfkIY5kQ,5
42
- qase_python_commons-4.0.0.dist-info/RECORD,,
42
+ qase_python_commons-4.1.0.dist-info/METADATA,sha256=JcshiFrQAEhDrqLCi3QflCIJcPMfDF4qLsEqLt-ansE,1982
43
+ qase_python_commons-4.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
44
+ qase_python_commons-4.1.0.dist-info/top_level.txt,sha256=Mn5aFk7H7Uia4s1NRDsvebu8vCrFy9nOuRIBfkIY5kQ,5
45
+ qase_python_commons-4.1.0.dist-info/RECORD,,