qmenta-client 1.1.dev1507__py3-none-any.whl → 2.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.
qmenta/client/Project.py
CHANGED
|
@@ -231,7 +231,7 @@ class Project:
|
|
|
231
231
|
name="",
|
|
232
232
|
input_data_type="qmenta_medical_image_data:3.10",
|
|
233
233
|
add_to_container_id=0,
|
|
234
|
-
chunk_size=2**9,
|
|
234
|
+
chunk_size=2 ** 9,
|
|
235
235
|
split_data=False,
|
|
236
236
|
):
|
|
237
237
|
"""
|
|
@@ -262,14 +262,14 @@ class Project:
|
|
|
262
262
|
a power of 2: 2**x. Default value of x is 9 (chunk_size = 512 kB)
|
|
263
263
|
split_data : bool
|
|
264
264
|
If True, the platform will try to split the uploaded file into
|
|
265
|
-
different sessions. It will be ignored when the ssid
|
|
266
|
-
add_to_container_id are given.
|
|
265
|
+
different sessions. It will be ignored when the ssid is given.
|
|
267
266
|
|
|
268
267
|
Returns
|
|
269
268
|
-------
|
|
270
269
|
bool
|
|
271
270
|
True if correctly uploaded, False otherwise.
|
|
272
271
|
"""
|
|
272
|
+
|
|
273
273
|
filename = os.path.split(file_path)[1]
|
|
274
274
|
input_data_type = "offline_analysis:1.0" if result else input_data_type
|
|
275
275
|
|
|
@@ -280,8 +280,6 @@ class Project:
|
|
|
280
280
|
|
|
281
281
|
total_bytes = os.path.getsize(file_path)
|
|
282
282
|
|
|
283
|
-
split_data = self.__assert_split_data(split_data, ssid, add_to_container_id)
|
|
284
|
-
|
|
285
283
|
# making chunks of the file and sending one by one
|
|
286
284
|
logger = logging.getLogger(logger_name)
|
|
287
285
|
with open(file_path, "rb") as file_object:
|
|
@@ -298,6 +296,10 @@ class Project:
|
|
|
298
296
|
response = None
|
|
299
297
|
last_chunk = False
|
|
300
298
|
|
|
299
|
+
if ssid and split_data:
|
|
300
|
+
logger.warning("split-data argument will be ignored because" + " ssid has been specified")
|
|
301
|
+
split_data = False
|
|
302
|
+
|
|
301
303
|
while True:
|
|
302
304
|
data = file_object.read(chunk_size)
|
|
303
305
|
if not data:
|
|
@@ -372,7 +374,8 @@ class Project:
|
|
|
372
374
|
logger.error(error)
|
|
373
375
|
return False
|
|
374
376
|
|
|
375
|
-
message = "Your data was successfully uploaded.
|
|
377
|
+
message = "Your data was successfully uploaded."
|
|
378
|
+
message += "The uploaded file will be soon processed !"
|
|
376
379
|
logger.info(message)
|
|
377
380
|
return True
|
|
378
381
|
|
|
@@ -474,7 +477,7 @@ class Project:
|
|
|
474
477
|
self._account.auth, "file_manager/download_file", data=params, stream=True
|
|
475
478
|
) as response, open(local_filename, "wb") as f:
|
|
476
479
|
|
|
477
|
-
for chunk in response.iter_content(chunk_size=2**9 * 1024):
|
|
480
|
+
for chunk in response.iter_content(chunk_size=2 ** 9 * 1024):
|
|
478
481
|
f.write(chunk)
|
|
479
482
|
f.flush()
|
|
480
483
|
|
|
@@ -520,7 +523,7 @@ class Project:
|
|
|
520
523
|
self._account.auth, "file_manager/download_file", data=params, stream=True
|
|
521
524
|
) as response, open(zip_name, "wb") as f:
|
|
522
525
|
|
|
523
|
-
for chunk in response.iter_content(chunk_size=2**9 * 1024):
|
|
526
|
+
for chunk in response.iter_content(chunk_size=2 ** 9 * 1024):
|
|
524
527
|
f.write(chunk)
|
|
525
528
|
f.flush()
|
|
526
529
|
|
|
@@ -2042,7 +2045,7 @@ class Project:
|
|
|
2042
2045
|
"""
|
|
2043
2046
|
Parse QC (Quality Control) text output into a structured dictionary format.
|
|
2044
2047
|
|
|
2045
|
-
This function takes raw QC text output (from
|
|
2048
|
+
This function takes raw QC text output (typically from medical imaging quality checks)
|
|
2046
2049
|
and parses it into a structured format that separates passed and failed rules,
|
|
2047
2050
|
along with their associated files and conditions.
|
|
2048
2051
|
|
|
@@ -2101,15 +2104,18 @@ class Project:
|
|
|
2101
2104
|
|
|
2102
2105
|
_, text = self.get_qc_status_subject(patient_id=patient_id, subject_name=subject_name, ssid=ssid)
|
|
2103
2106
|
|
|
2104
|
-
result = {
|
|
2107
|
+
result = {
|
|
2108
|
+
"passed": [],
|
|
2109
|
+
"failed": []
|
|
2110
|
+
}
|
|
2105
2111
|
|
|
2106
2112
|
# Split into failed and passed sections
|
|
2107
|
-
sections = re.split(r
|
|
2113
|
+
sections = re.split(r'={10,}\n\n', text)
|
|
2108
2114
|
if len(sections) == 3:
|
|
2109
|
-
failed_section = sections[1].split(
|
|
2115
|
+
failed_section = sections[1].split('=' * 10)[0].strip()
|
|
2110
2116
|
passed_section = sections[2].strip()
|
|
2111
2117
|
else:
|
|
2112
|
-
section = sections[1].split(
|
|
2118
|
+
section = sections[1].split('=' * 10)[0].strip()
|
|
2113
2119
|
if "PASSED QC MESSAGES" in section:
|
|
2114
2120
|
passed_section = section
|
|
2115
2121
|
failed_section = ""
|
|
@@ -2117,13 +2123,85 @@ class Project:
|
|
|
2117
2123
|
failed_section = section
|
|
2118
2124
|
passed_section = ""
|
|
2119
2125
|
|
|
2120
|
-
|
|
2121
|
-
failed_rules = re.split(r
|
|
2122
|
-
|
|
2126
|
+
# Parse failed rules
|
|
2127
|
+
failed_rules = re.split(r'\n ❌ ', failed_section)
|
|
2128
|
+
for rule_text in failed_rules[1:]: # Skip first empty part
|
|
2129
|
+
rule_name = rule_text.split(' ❌')[0].strip()
|
|
2130
|
+
rule_data = {
|
|
2131
|
+
"rule": rule_name,
|
|
2132
|
+
"files": [],
|
|
2133
|
+
"failed_conditions": {}
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
# Extract all file comparisons for this rule
|
|
2137
|
+
file_comparisons = re.split(r'\t- Comparison with file:', rule_text)
|
|
2138
|
+
for comp in file_comparisons[1:]: # Skip first part
|
|
2139
|
+
file_name = comp.split('\n')[0].strip()
|
|
2140
|
+
conditions_match = re.search(
|
|
2141
|
+
r'Conditions:(.*?)(?=\n\t- Comparison|\n\n|$)',
|
|
2142
|
+
comp,
|
|
2143
|
+
re.DOTALL
|
|
2144
|
+
)
|
|
2145
|
+
if not conditions_match:
|
|
2146
|
+
continue
|
|
2147
|
+
|
|
2148
|
+
conditions_text = conditions_match.group(1).strip()
|
|
2149
|
+
# Parse conditions
|
|
2150
|
+
conditions = []
|
|
2151
|
+
for line in conditions_text.split('\n'):
|
|
2152
|
+
line = line.strip()
|
|
2153
|
+
if line.startswith('·'):
|
|
2154
|
+
status = '✔' if '✔' in line else '🚫'
|
|
2155
|
+
condition = re.sub(r'^· [✔🚫]\s*', '', line)
|
|
2156
|
+
conditions.append({
|
|
2157
|
+
"status": "passed" if status == '✔' else "failed",
|
|
2158
|
+
"condition": condition
|
|
2159
|
+
})
|
|
2160
|
+
|
|
2161
|
+
# Add to failed conditions summary
|
|
2162
|
+
for cond in conditions:
|
|
2163
|
+
if cond['status'] == 'failed':
|
|
2164
|
+
cond_text = cond['condition']
|
|
2165
|
+
if cond_text not in rule_data['failed_conditions']:
|
|
2166
|
+
rule_data['failed_conditions'][cond_text] = 0
|
|
2167
|
+
rule_data['failed_conditions'][cond_text] += 1
|
|
2168
|
+
|
|
2169
|
+
rule_data['files'].append({
|
|
2170
|
+
"file": file_name,
|
|
2171
|
+
"conditions": conditions
|
|
2172
|
+
})
|
|
2173
|
+
|
|
2174
|
+
result['failed'].append(rule_data)
|
|
2123
2175
|
|
|
2124
2176
|
# Parse passed rules
|
|
2125
|
-
passed_rules = re.split(r
|
|
2126
|
-
|
|
2177
|
+
passed_rules = re.split(r'\n ✅ ', passed_section)
|
|
2178
|
+
for rule_text in passed_rules[1:]: # Skip first empty part
|
|
2179
|
+
rule_name = rule_text.split(' ✅')[0].strip()
|
|
2180
|
+
rule_data = {
|
|
2181
|
+
"rule": rule_name,
|
|
2182
|
+
"sub_rule": None,
|
|
2183
|
+
"files": []
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
# Get sub-rule
|
|
2187
|
+
sub_rule_match = re.search(r'Sub-rule: (.*?)\n', rule_text)
|
|
2188
|
+
if sub_rule_match:
|
|
2189
|
+
rule_data['sub_rule'] = sub_rule_match.group(1).strip()
|
|
2190
|
+
|
|
2191
|
+
# Get files passed
|
|
2192
|
+
files_passed = re.search(r'List of files passed:(.*?)(?=\n\n|\Z)', rule_text, re.DOTALL)
|
|
2193
|
+
if files_passed:
|
|
2194
|
+
for line in files_passed.group(1).split('\n'):
|
|
2195
|
+
line = line.strip()
|
|
2196
|
+
if line.startswith('·'):
|
|
2197
|
+
file_match = re.match(r'· (.*?) \((\d+)/(\d+)\)', line)
|
|
2198
|
+
if file_match:
|
|
2199
|
+
rule_data['files'].append({
|
|
2200
|
+
"file": file_match.group(1).strip(),
|
|
2201
|
+
"passed_conditions": int(file_match.group(2)),
|
|
2202
|
+
})
|
|
2203
|
+
|
|
2204
|
+
result['passed'].append(rule_data)
|
|
2127
2205
|
|
|
2128
2206
|
return result
|
|
2129
2207
|
|
|
@@ -2194,14 +2272,19 @@ class Project:
|
|
|
2194
2272
|
|
|
2195
2273
|
# Initialize statistics
|
|
2196
2274
|
stats = {
|
|
2197
|
-
|
|
2198
|
-
|
|
2275
|
+
'passed_rules': 0,
|
|
2276
|
+
'failed_rules': 0,
|
|
2199
2277
|
"subjects_passed": 0,
|
|
2200
2278
|
"subjects_with_failed": 0,
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2279
|
+
'num_passed_files_distribution': defaultdict(int), # How many rules have N passed files
|
|
2280
|
+
'file_stats': {
|
|
2281
|
+
'total': 0,
|
|
2282
|
+
'passed': 0,
|
|
2283
|
+
'failed': 0,
|
|
2284
|
+
'pass_percentage': 0.0
|
|
2285
|
+
},
|
|
2286
|
+
'condition_failure_rates': defaultdict(lambda: {'count': 0, 'percentage': 0.0}),
|
|
2287
|
+
'rule_success_rates': defaultdict(lambda: {'passed': 0, 'failed': 0, 'success_rate': 0.0}),
|
|
2205
2288
|
}
|
|
2206
2289
|
|
|
2207
2290
|
total_failures = 0
|
|
@@ -2211,56 +2294,56 @@ class Project:
|
|
|
2211
2294
|
# sum subjects with some failed qc message
|
|
2212
2295
|
stats["subjects_with_failed"] = sum([1 for rules in qc_results_list if rules["failed"]])
|
|
2213
2296
|
# sum rules that have passed
|
|
2214
|
-
stats["passed_rules"] = sum([len(rules[
|
|
2297
|
+
stats["passed_rules"] = sum([len(rules['passed']) for rules in qc_results_list if rules["failed"]])
|
|
2215
2298
|
# sum rules that have failed
|
|
2216
|
-
stats["failed_rules"] = sum([len(rules[
|
|
2299
|
+
stats["failed_rules"] = sum([len(rules['failed']) for rules in qc_results_list if rules["failed"]])
|
|
2217
2300
|
|
|
2218
2301
|
for qc_results in qc_results_list:
|
|
2219
2302
|
|
|
2220
2303
|
# Count passed files distribution
|
|
2221
|
-
for rule in qc_results[
|
|
2222
|
-
num_files = len(rule[
|
|
2223
|
-
stats[
|
|
2224
|
-
stats[
|
|
2225
|
-
stats[
|
|
2226
|
-
rule_name = rule[
|
|
2227
|
-
stats[
|
|
2228
|
-
|
|
2229
|
-
for rule in qc_results[
|
|
2230
|
-
stats[
|
|
2231
|
-
stats[
|
|
2232
|
-
for condition, count in rule[
|
|
2304
|
+
for rule in qc_results['passed']:
|
|
2305
|
+
num_files = len(rule['files'])
|
|
2306
|
+
stats['num_passed_files_distribution'][num_files] += 1
|
|
2307
|
+
stats['file_stats']['passed'] += len(rule['files'])
|
|
2308
|
+
stats['file_stats']['total'] += len(rule['files'])
|
|
2309
|
+
rule_name = rule['rule']
|
|
2310
|
+
stats['rule_success_rates'][rule_name]['passed'] += 1
|
|
2311
|
+
|
|
2312
|
+
for rule in qc_results['failed']:
|
|
2313
|
+
stats['file_stats']['total'] += len(rule['files'])
|
|
2314
|
+
stats['file_stats']['failed'] += len(rule['files'])
|
|
2315
|
+
for condition, count in rule['failed_conditions'].items():
|
|
2233
2316
|
# Extract just the condition text without actual value
|
|
2234
|
-
clean_condition = re.sub(r
|
|
2235
|
-
stats[
|
|
2317
|
+
clean_condition = re.sub(r'\.\s*Actual value:.*$', '', condition)
|
|
2318
|
+
stats['condition_failure_rates'][clean_condition]['count'] += count
|
|
2236
2319
|
total_failures += count
|
|
2237
|
-
rule_name = rule[
|
|
2238
|
-
stats[
|
|
2320
|
+
rule_name = rule['rule']
|
|
2321
|
+
stats['rule_success_rates'][rule_name]['failed'] += 1
|
|
2239
2322
|
|
|
2240
|
-
if stats[
|
|
2241
|
-
stats[
|
|
2242
|
-
(stats[
|
|
2323
|
+
if stats['file_stats']['total'] > 0:
|
|
2324
|
+
stats['file_stats']['pass_percentage'] = round(
|
|
2325
|
+
(stats['file_stats']['passed'] / stats['file_stats']['total']) * 100, 2
|
|
2243
2326
|
)
|
|
2244
2327
|
|
|
2245
2328
|
# Calculate condition failure percentages
|
|
2246
|
-
for condition in stats[
|
|
2329
|
+
for condition in stats['condition_failure_rates']:
|
|
2247
2330
|
if total_failures > 0:
|
|
2248
|
-
stats[
|
|
2249
|
-
(stats[
|
|
2331
|
+
stats['condition_failure_rates'][condition]['percentage'] = round(
|
|
2332
|
+
(stats['condition_failure_rates'][condition]['count'] / total_failures) * 100, 2
|
|
2250
2333
|
)
|
|
2251
2334
|
|
|
2252
2335
|
# Calculate rule success rates
|
|
2253
|
-
for rule in stats[
|
|
2254
|
-
total = stats[
|
|
2336
|
+
for rule in stats['rule_success_rates']:
|
|
2337
|
+
total = stats['rule_success_rates'][rule]['passed'] + stats['rule_success_rates'][rule]['failed']
|
|
2255
2338
|
if total > 0:
|
|
2256
|
-
stats[
|
|
2257
|
-
(stats[
|
|
2339
|
+
stats['rule_success_rates'][rule]['success_rate'] = round(
|
|
2340
|
+
(stats['rule_success_rates'][rule]['passed'] / total) * 100, 2
|
|
2258
2341
|
)
|
|
2259
2342
|
|
|
2260
2343
|
# Convert defaultdict to regular dict for cleaner JSON output
|
|
2261
|
-
stats[
|
|
2262
|
-
stats[
|
|
2263
|
-
stats[
|
|
2344
|
+
stats['num_passed_files_distribution'] = dict(stats['num_passed_files_distribution'])
|
|
2345
|
+
stats['condition_failure_rates'] = dict(stats['condition_failure_rates'])
|
|
2346
|
+
stats['rule_success_rates'] = dict(stats['rule_success_rates'])
|
|
2264
2347
|
|
|
2265
2348
|
return stats
|
|
2266
2349
|
|
|
@@ -2590,107 +2673,3 @@ class Project:
|
|
|
2590
2673
|
value.replace(d_type + ";", "")
|
|
2591
2674
|
file_metadata[d_tag] = {"operation": "in-list", "value": value.replace(d_type + ";", "").split(";")}
|
|
2592
2675
|
return modality, tags, file_metadata
|
|
2593
|
-
|
|
2594
|
-
def __assert_split_data(self, split_data, ssid, add_to_container_id):
|
|
2595
|
-
"""
|
|
2596
|
-
Assert if the split_data parameter is possible to use in regards
|
|
2597
|
-
to the ssid and add_to_container_id parameters during upload.
|
|
2598
|
-
Changes its status to False if needed.
|
|
2599
|
-
|
|
2600
|
-
Parameters
|
|
2601
|
-
----------
|
|
2602
|
-
split_data : Bool
|
|
2603
|
-
split_data parameter from method 'upload_file'.
|
|
2604
|
-
ssid : str
|
|
2605
|
-
Session ID.
|
|
2606
|
-
add_to_container_id : int or bool
|
|
2607
|
-
Container ID or False
|
|
2608
|
-
|
|
2609
|
-
Returns
|
|
2610
|
-
-------
|
|
2611
|
-
split_data : Bool
|
|
2612
|
-
|
|
2613
|
-
"""
|
|
2614
|
-
|
|
2615
|
-
logger = logging.getLogger(logger_name)
|
|
2616
|
-
if ssid and split_data:
|
|
2617
|
-
logger.warning("split-data argument will be ignored because ssid has been specified")
|
|
2618
|
-
split_data = False
|
|
2619
|
-
|
|
2620
|
-
if add_to_container_id and split_data:
|
|
2621
|
-
logger.warning("split-data argument will be ignored because add_to_container_id has been specified")
|
|
2622
|
-
split_data = False
|
|
2623
|
-
|
|
2624
|
-
return split_data
|
|
2625
|
-
|
|
2626
|
-
def __parse_fail_rules(self, failed_rules, result):
|
|
2627
|
-
"""
|
|
2628
|
-
Parse fail rules.
|
|
2629
|
-
"""
|
|
2630
|
-
|
|
2631
|
-
for rule_text in failed_rules[1:]: # Skip first empty part
|
|
2632
|
-
rule_name = rule_text.split(" ❌")[0].strip()
|
|
2633
|
-
rule_data = {"rule": rule_name, "files": [], "failed_conditions": {}}
|
|
2634
|
-
|
|
2635
|
-
# Extract all file comparisons for this rule
|
|
2636
|
-
file_comparisons = re.split(r"\t- Comparison with file:", rule_text)
|
|
2637
|
-
for comp in file_comparisons[1:]: # Skip first part
|
|
2638
|
-
file_name = comp.split("\n")[0].strip()
|
|
2639
|
-
conditions_match = re.search(r"Conditions:(.*?)(?=\n\t- Comparison|\n\n|$)", comp, re.DOTALL)
|
|
2640
|
-
if not conditions_match:
|
|
2641
|
-
continue
|
|
2642
|
-
|
|
2643
|
-
conditions_text = conditions_match.group(1).strip()
|
|
2644
|
-
# Parse conditions
|
|
2645
|
-
conditions = []
|
|
2646
|
-
for line in conditions_text.split("\n"):
|
|
2647
|
-
line = line.strip()
|
|
2648
|
-
if line.startswith("·"):
|
|
2649
|
-
status = "✔" if "✔" in line else "🚫"
|
|
2650
|
-
condition = re.sub(r"^· [✔🚫]\s*", "", line)
|
|
2651
|
-
conditions.append({"status": "passed" if status == "✔" else "failed", "condition": condition})
|
|
2652
|
-
|
|
2653
|
-
# Add to failed conditions summary
|
|
2654
|
-
for cond in conditions:
|
|
2655
|
-
if cond["status"] == "failed":
|
|
2656
|
-
cond_text = cond["condition"]
|
|
2657
|
-
if cond_text not in rule_data["failed_conditions"]:
|
|
2658
|
-
rule_data["failed_conditions"][cond_text] = 0
|
|
2659
|
-
rule_data["failed_conditions"][cond_text] += 1
|
|
2660
|
-
|
|
2661
|
-
rule_data["files"].append({"file": file_name, "conditions": conditions})
|
|
2662
|
-
|
|
2663
|
-
result["failed"].append(rule_data)
|
|
2664
|
-
return result
|
|
2665
|
-
|
|
2666
|
-
def __parse_pass_rules(self, passed_rules, result):
|
|
2667
|
-
"""
|
|
2668
|
-
Parse pass rules.
|
|
2669
|
-
"""
|
|
2670
|
-
|
|
2671
|
-
for rule_text in passed_rules[1:]: # Skip first empty part
|
|
2672
|
-
rule_name = rule_text.split(" ✅")[0].strip()
|
|
2673
|
-
rule_data = {"rule": rule_name, "sub_rule": None, "files": []}
|
|
2674
|
-
|
|
2675
|
-
# Get sub-rule
|
|
2676
|
-
sub_rule_match = re.search(r"Sub-rule: (.*?)\n", rule_text)
|
|
2677
|
-
if sub_rule_match:
|
|
2678
|
-
rule_data["sub_rule"] = sub_rule_match.group(1).strip()
|
|
2679
|
-
|
|
2680
|
-
# Get files passed
|
|
2681
|
-
files_passed = re.search(r"List of files passed:(.*?)(?=\n\n|\Z)", rule_text, re.DOTALL)
|
|
2682
|
-
if files_passed:
|
|
2683
|
-
for line in files_passed.group(1).split("\n"):
|
|
2684
|
-
line = line.strip()
|
|
2685
|
-
if line.startswith("·"):
|
|
2686
|
-
file_match = re.match(r"· (.*?) \((\d+)/(\d+)\)", line)
|
|
2687
|
-
if file_match:
|
|
2688
|
-
rule_data["files"].append(
|
|
2689
|
-
{
|
|
2690
|
-
"file": file_match.group(1).strip(),
|
|
2691
|
-
"passed_conditions": int(file_match.group(2)),
|
|
2692
|
-
}
|
|
2693
|
-
)
|
|
2694
|
-
|
|
2695
|
-
result["passed"].append(rule_data)
|
|
2696
|
-
return result
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
qmenta/__init__.py,sha256=ED6jHcYiuYpr_0vjGz0zx2lrrmJT9sDJCzIljoDfmlM,65
|
|
2
2
|
qmenta/client/Account.py,sha256=7BOWHtRbHdfpBYQqv9v2m2Fag13pExZSxFsjDA7UsW0,9500
|
|
3
3
|
qmenta/client/File.py,sha256=iCrzrd7rIfjjW2AgMgUoK-ZF2wf-95wCcPKxKw6PGyg,4816
|
|
4
|
-
qmenta/client/Project.py,sha256=
|
|
4
|
+
qmenta/client/Project.py,sha256=vx1GabCiVG7gYqnkNmqhqRyzy7ml7eT4jjnUciBq_Gw,99565
|
|
5
5
|
qmenta/client/Subject.py,sha256=b5sg9UFtn11bmPM-xFXP8aehOm_HGxnhgT7IPKbrZnE,8688
|
|
6
6
|
qmenta/client/__init__.py,sha256=Mtqe4zf8n3wuwMXSALENQgp5atQY5VcsyXWs2hjBs28,133
|
|
7
7
|
qmenta/client/utils.py,sha256=vWUAW0r9yDetdlwNo86sdzKn03FNGvwa7D9UtOA3TEc,2419
|
|
8
|
-
qmenta_client-
|
|
9
|
-
qmenta_client-
|
|
10
|
-
qmenta_client-
|
|
8
|
+
qmenta_client-2.0.dist-info/METADATA,sha256=niv_y8-oKl7puOvmGkjnci_11YuMgUN7Qv2iO8umNOI,664
|
|
9
|
+
qmenta_client-2.0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
|
10
|
+
qmenta_client-2.0.dist-info/RECORD,,
|