emdbva 0.0.1.dev144__tar.gz → 0.0.1.dev145__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.
- {emdbva-0.0.1.dev144/emdbva.egg-info → emdbva-0.0.1.dev145}/PKG-INFO +1 -1
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145/emdbva.egg-info}/PKG-INFO +1 -1
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/emdbva.egg-info/SOURCES.txt +2 -0
- emdbva-0.0.1.dev145/va/audit/__init__.py +13 -0
- emdbva-0.0.1.dev145/va/audit/cif_revision.py +324 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/mainva.py +13 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/schemas/emdb_entry_full.schema.json +160 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/version.py +1 -1
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/LICENSE +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/MANIFEST.in +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/README.rst +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/emdbva.egg-info/dependency_links.txt +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/emdbva.egg-info/entry_points.txt +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/emdbva.egg-info/requires.txt +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/emdbva.egg-info/top_level.txt +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/setup.cfg +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/setup.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/PATHS_codon_airflow.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/__init__.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/__init__.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/bars.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/connected_percentage.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/contour_level_predicator.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/emda_mmcc.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/emringer.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/inclusion.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/map_data_validation.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/overlap_percentage.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/phaserandomization.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/phenix_cc.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/phenix_mm.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/projections.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/qscore.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/residue_locres.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/resmap.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/smoc.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/strudel.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/surfaces.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/metrics/threedfsc.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/preparation.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/prepareandrun_codon_airflow.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/qscores.csv +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/schemas/__init__.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/schemas/loader.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/utils/Checker.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/utils/ChimeraxViews.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/utils/MapProcessor.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/utils/Model.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/utils/__init__.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/utils/cl_weights.pth +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/utils/log_utils.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/utils/misc.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/utils/rescolor.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/utils/stars.py +0 -0
- {emdbva-0.0.1.dev144 → emdbva-0.0.1.dev145}/va/validationanalysis.py +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Audit helpers used to make conservative VA execution decisions."""
|
|
2
|
+
|
|
3
|
+
from va.audit.cif_revision import (
|
|
4
|
+
audit_model_cifs_for_preparation,
|
|
5
|
+
should_run_va_for_cif,
|
|
6
|
+
write_cif_revision_audit_report,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
'audit_model_cifs_for_preparation',
|
|
11
|
+
'should_run_va_for_cif',
|
|
12
|
+
'write_cif_revision_audit_report',
|
|
13
|
+
]
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"""Gemmi-based audit revision checks for model CIF files."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
import gemmi
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
_CATEGORY = '_pdbx_audit_revision_details'
|
|
11
|
+
_FIELDS = (
|
|
12
|
+
'ordinal',
|
|
13
|
+
'revision_ordinal',
|
|
14
|
+
'data_content_type',
|
|
15
|
+
'provider',
|
|
16
|
+
'type',
|
|
17
|
+
'description',
|
|
18
|
+
'details',
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
DEFAULT_SKIP_RULES = [
|
|
22
|
+
{
|
|
23
|
+
'type': 'Remediation',
|
|
24
|
+
'description': 'Metalloprotein remediation',
|
|
25
|
+
'decision': 'skip',
|
|
26
|
+
'reason_code': 'metalloprotein_remediation',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
'type': 'Remediation',
|
|
30
|
+
'description': 'Carbohydrate remediation',
|
|
31
|
+
'decision': 'skip',
|
|
32
|
+
'reason_code': 'carbohydrate_remediation',
|
|
33
|
+
},
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _normalise_value(value):
|
|
38
|
+
"""Normalize one Gemmi CIF value for display and comparison."""
|
|
39
|
+
if value is None:
|
|
40
|
+
return None
|
|
41
|
+
value = str(value).strip()
|
|
42
|
+
if len(value) >= 2 and value[0] == value[-1] and value[0] in "'\"":
|
|
43
|
+
value = value[1:-1].strip()
|
|
44
|
+
value = re.sub(r'\s+', ' ', value)
|
|
45
|
+
if value in ('?', '.') or not value:
|
|
46
|
+
return None
|
|
47
|
+
return value
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _normalise_for_match(value):
|
|
51
|
+
"""Return a normalized case-insensitive comparison value."""
|
|
52
|
+
value = _normalise_value(value)
|
|
53
|
+
return value.casefold() if value is not None else None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _first_block(document):
|
|
57
|
+
"""Return the first data block across supported Gemmi versions."""
|
|
58
|
+
try:
|
|
59
|
+
return document[0]
|
|
60
|
+
except (TypeError, IndexError):
|
|
61
|
+
return document.sole_block()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _loop_from_block(block):
|
|
65
|
+
"""Return the audit loop or ``None`` when the category is not looped."""
|
|
66
|
+
for field in _FIELDS:
|
|
67
|
+
try:
|
|
68
|
+
column = block.find_loop(f'{_CATEGORY}.{field}')
|
|
69
|
+
except Exception:
|
|
70
|
+
column = None
|
|
71
|
+
if column is not None:
|
|
72
|
+
return column.get_loop() if hasattr(column, 'get_loop') else column
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _rows_from_loop(loop):
|
|
77
|
+
"""Convert a Gemmi loop into normalized row dictionaries."""
|
|
78
|
+
tags = [str(tag) for tag in getattr(loop, 'tags', [])]
|
|
79
|
+
values = list(getattr(loop, 'values', []))
|
|
80
|
+
if not tags:
|
|
81
|
+
return []
|
|
82
|
+
|
|
83
|
+
width = len(tags)
|
|
84
|
+
rows = []
|
|
85
|
+
for offset in range(0, len(values), width):
|
|
86
|
+
row = {}
|
|
87
|
+
for field in _FIELDS:
|
|
88
|
+
tag = f'{_CATEGORY}.{field}'
|
|
89
|
+
try:
|
|
90
|
+
index = tags.index(tag)
|
|
91
|
+
except ValueError:
|
|
92
|
+
row[field] = None
|
|
93
|
+
continue
|
|
94
|
+
value_index = offset + index
|
|
95
|
+
row[field] = _normalise_value(values[value_index]) if value_index < len(values) else None
|
|
96
|
+
rows.append(row)
|
|
97
|
+
return rows
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _rows_from_scalars(block):
|
|
101
|
+
"""Read a single audit row when the category is not represented as a loop."""
|
|
102
|
+
row = {}
|
|
103
|
+
found = False
|
|
104
|
+
for field in _FIELDS:
|
|
105
|
+
try:
|
|
106
|
+
value = block.find_value(f'{_CATEGORY}.{field}')
|
|
107
|
+
except Exception:
|
|
108
|
+
value = None
|
|
109
|
+
row[field] = _normalise_value(value)
|
|
110
|
+
found = found or row[field] is not None
|
|
111
|
+
return [row] if found else []
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _issue(code, message, path):
|
|
115
|
+
"""Build a non-fatal audit issue."""
|
|
116
|
+
return {
|
|
117
|
+
'code': code,
|
|
118
|
+
'severity': 'warning',
|
|
119
|
+
'path': path,
|
|
120
|
+
'message': message,
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _rule_matches(row, rule):
|
|
125
|
+
"""Return whether a rule exactly matches the relevant row fields."""
|
|
126
|
+
return (
|
|
127
|
+
_normalise_for_match(row.get('type')) == _normalise_for_match(rule.get('type'))
|
|
128
|
+
and _normalise_for_match(row.get('description')) == _normalise_for_match(rule.get('description'))
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _base_decision(rows, issues):
|
|
133
|
+
"""Return the common report structure for parsed audit rows."""
|
|
134
|
+
return {
|
|
135
|
+
'run_validation': True,
|
|
136
|
+
'decision': 'run',
|
|
137
|
+
'reason_code': 'unmatched_revision',
|
|
138
|
+
'message': 'No known VA-skip audit rule matched; VA will run.',
|
|
139
|
+
'matched_rules': [],
|
|
140
|
+
'issues': issues,
|
|
141
|
+
'audit_revision_details': {
|
|
142
|
+
'row_count': len(rows),
|
|
143
|
+
'rows': rows,
|
|
144
|
+
},
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def should_run_va_for_cif(cif_path, rules=None):
|
|
149
|
+
"""Return a conservative VA execution decision for one model CIF.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
cif_path (str): Path to a model CIF/mmCIF file.
|
|
153
|
+
rules (list or None): Optional replacement list of skip-rule mappings.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
dict: A stable run/skip decision with parsed audit rows and warnings.
|
|
157
|
+
"""
|
|
158
|
+
if not cif_path or not os.path.isfile(cif_path):
|
|
159
|
+
return {
|
|
160
|
+
'run_validation': True,
|
|
161
|
+
'decision': 'run',
|
|
162
|
+
'reason_code': 'missing_cif',
|
|
163
|
+
'message': 'Model CIF file is missing; VA will run without audit gating.',
|
|
164
|
+
'matched_rules': [],
|
|
165
|
+
'issues': [_issue('missing_cif', f'CIF file does not exist: {cif_path}', str(cif_path))],
|
|
166
|
+
'audit_revision_details': {'row_count': 0, 'rows': []},
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
document = gemmi.cif.read_file(cif_path)
|
|
171
|
+
block = _first_block(document)
|
|
172
|
+
loop = _loop_from_block(block)
|
|
173
|
+
rows = _rows_from_loop(loop) if loop is not None else _rows_from_scalars(block)
|
|
174
|
+
except Exception as exc:
|
|
175
|
+
return {
|
|
176
|
+
'run_validation': True,
|
|
177
|
+
'decision': 'run',
|
|
178
|
+
'reason_code': 'parse_error',
|
|
179
|
+
'message': 'CIF audit data could not be parsed; VA will run.',
|
|
180
|
+
'matched_rules': [],
|
|
181
|
+
'issues': [_issue('parse_error', str(exc), str(cif_path))],
|
|
182
|
+
'audit_revision_details': {'row_count': 0, 'rows': []},
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if not rows:
|
|
186
|
+
return {
|
|
187
|
+
'run_validation': True,
|
|
188
|
+
'decision': 'run',
|
|
189
|
+
'reason_code': 'missing_audit_category',
|
|
190
|
+
'message': 'No audit revision details were found; VA will run.',
|
|
191
|
+
'matched_rules': [],
|
|
192
|
+
'issues': [_issue(
|
|
193
|
+
'missing_audit_category',
|
|
194
|
+
f'{_CATEGORY} is absent or has no rows.',
|
|
195
|
+
_CATEGORY,
|
|
196
|
+
)],
|
|
197
|
+
'audit_revision_details': {'row_count': 0, 'rows': []},
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
report = _base_decision(rows, [])
|
|
201
|
+
active_rules = DEFAULT_SKIP_RULES if rules is None else rules
|
|
202
|
+
matched_rules = []
|
|
203
|
+
matched_row_indexes = set()
|
|
204
|
+
for index, row in enumerate(rows):
|
|
205
|
+
for rule in active_rules:
|
|
206
|
+
if _rule_matches(row, rule):
|
|
207
|
+
matched_rules.append(dict(rule))
|
|
208
|
+
matched_row_indexes.add(index)
|
|
209
|
+
|
|
210
|
+
initial_type = _normalise_for_match('Initial release')
|
|
211
|
+
unrelated_rows = [
|
|
212
|
+
row for index, row in enumerate(rows)
|
|
213
|
+
if index not in matched_row_indexes
|
|
214
|
+
and _normalise_for_match(row.get('type')) not in (None, initial_type)
|
|
215
|
+
]
|
|
216
|
+
|
|
217
|
+
if matched_rules and not unrelated_rows:
|
|
218
|
+
report.update({
|
|
219
|
+
'run_validation': False,
|
|
220
|
+
'decision': 'skip',
|
|
221
|
+
'reason_code': 'known_remediation_skip',
|
|
222
|
+
'message': 'A known remediation-only audit rule matched; VA validation will be skipped.',
|
|
223
|
+
'matched_rules': matched_rules,
|
|
224
|
+
})
|
|
225
|
+
return report
|
|
226
|
+
|
|
227
|
+
if len(rows) == 1 and _normalise_for_match(rows[0].get('type')) == initial_type:
|
|
228
|
+
report.update({
|
|
229
|
+
'reason_code': 'initial_release_only',
|
|
230
|
+
'message': 'Only an initial release audit revision was found; VA will run.',
|
|
231
|
+
})
|
|
232
|
+
return report
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _model_cif_paths(prepobj):
|
|
236
|
+
"""Resolve model CIF paths using the locations used by ``read_model``."""
|
|
237
|
+
model_names = getattr(prepobj, 'model', None) or []
|
|
238
|
+
if isinstance(model_names, str):
|
|
239
|
+
model_names = [model_names]
|
|
240
|
+
|
|
241
|
+
paths = []
|
|
242
|
+
for model_name in model_names:
|
|
243
|
+
if not model_name:
|
|
244
|
+
continue
|
|
245
|
+
if os.path.isabs(model_name):
|
|
246
|
+
paths.append(model_name)
|
|
247
|
+
continue
|
|
248
|
+
working_path = os.path.join(prepobj.vadir, model_name)
|
|
249
|
+
paths.append(working_path if os.path.isfile(working_path) else model_name)
|
|
250
|
+
return paths
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _aggregate_model_reports(model_paths, model_reports):
|
|
254
|
+
"""Aggregate per-model decisions using the conservative all-skip policy."""
|
|
255
|
+
all_skip = bool(model_reports) and all(
|
|
256
|
+
report.get('decision') == 'skip' for report in model_reports
|
|
257
|
+
)
|
|
258
|
+
issues = [issue for report in model_reports for issue in report.get('issues', [])]
|
|
259
|
+
matched_rules = [rule for report in model_reports for rule in report.get('matched_rules', [])]
|
|
260
|
+
rows = [
|
|
261
|
+
row for report in model_reports
|
|
262
|
+
for row in report.get('audit_revision_details', {}).get('rows', [])
|
|
263
|
+
]
|
|
264
|
+
return {
|
|
265
|
+
'run_validation': not all_skip,
|
|
266
|
+
'decision': 'skip' if all_skip else 'run',
|
|
267
|
+
'reason_code': 'known_remediation_skip' if all_skip else 'model_audit_reviewed',
|
|
268
|
+
'message': (
|
|
269
|
+
'All model CIFs matched known remediation rules; VA validation will be skipped.'
|
|
270
|
+
if all_skip else 'At least one model CIF did not clearly match a skip rule; VA will run.'
|
|
271
|
+
),
|
|
272
|
+
'matched_rules': matched_rules,
|
|
273
|
+
'issues': issues,
|
|
274
|
+
'audit_revision_details': {'row_count': len(rows), 'rows': rows},
|
|
275
|
+
'model_count': len(model_paths),
|
|
276
|
+
'model_reports': model_reports,
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def audit_model_cifs_for_preparation(prepobj):
|
|
281
|
+
"""Audit all configured model CIFs for a prepared VA entry.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
prepobj: Prepared VA input object with ``model`` and ``vadir`` attributes.
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
dict: Aggregate audit decision and per-model reports. Missing or
|
|
288
|
+
malformed CIF data produces a run decision with warnings.
|
|
289
|
+
"""
|
|
290
|
+
model_paths = _model_cif_paths(prepobj)
|
|
291
|
+
if not model_paths:
|
|
292
|
+
return {
|
|
293
|
+
'run_validation': True,
|
|
294
|
+
'decision': 'run',
|
|
295
|
+
'reason_code': 'no_model',
|
|
296
|
+
'message': 'No model was supplied; CIF audit gating was not applied.',
|
|
297
|
+
'matched_rules': [],
|
|
298
|
+
'issues': [],
|
|
299
|
+
'audit_revision_details': {'row_count': 0, 'rows': []},
|
|
300
|
+
'model_count': 0,
|
|
301
|
+
'model_reports': [],
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
model_reports = [should_run_va_for_cif(path) for path in model_paths]
|
|
305
|
+
return _aggregate_model_reports(model_paths, model_reports)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def write_cif_revision_audit_report(prepobj, report):
|
|
309
|
+
"""Write the audit fragment consumed by the existing JSON merger.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
prepobj: Prepared VA input object with ``vadir`` and ``mapname``.
|
|
313
|
+
report (dict): Aggregate CIF audit decision.
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
str: Path to the written audit report fragment.
|
|
317
|
+
"""
|
|
318
|
+
report_path = os.path.join(
|
|
319
|
+
prepobj.vadir,
|
|
320
|
+
'{}_cif_revision_audit.json'.format(os.path.basename(prepobj.mapname)),
|
|
321
|
+
)
|
|
322
|
+
with open(report_path, 'w', encoding='utf-8') as output:
|
|
323
|
+
json.dump({'audit_validation': {'cif_revision_audit': report}}, output, indent=2)
|
|
324
|
+
return report_path
|
|
@@ -40,6 +40,10 @@ from va.validationanalysis import ValidationAnalysis
|
|
|
40
40
|
from va.preparation import PreParation as prep
|
|
41
41
|
from va.metrics.strudel import run_strudel
|
|
42
42
|
from va.utils.log_utils import LogRedirector
|
|
43
|
+
from va.audit.cif_revision import (
|
|
44
|
+
audit_model_cifs_for_preparation,
|
|
45
|
+
write_cif_revision_audit_report,
|
|
46
|
+
)
|
|
43
47
|
import threading
|
|
44
48
|
try:
|
|
45
49
|
from PATHS import MAP_SERVER_PATH
|
|
@@ -335,6 +339,7 @@ def reconfigure_logging(temp_logger, buffer, log_file):
|
|
|
335
339
|
|
|
336
340
|
temp_logger.info(f"Logging reconfigured to use file: {log_file}")
|
|
337
341
|
|
|
342
|
+
|
|
338
343
|
# @profile
|
|
339
344
|
def main():
|
|
340
345
|
"""Run the main VA command-line entry point.
|
|
@@ -348,6 +353,14 @@ def main():
|
|
|
348
353
|
# Preparation
|
|
349
354
|
# temp_logger, buffer = setup_temporary_logging()
|
|
350
355
|
prepobj = prep()
|
|
356
|
+
|
|
357
|
+
if not prepobj.args.positions:
|
|
358
|
+
audit_report = audit_model_cifs_for_preparation(prepobj)
|
|
359
|
+
write_cif_revision_audit_report(prepobj, audit_report)
|
|
360
|
+
if not audit_report['run_validation']:
|
|
361
|
+
print(audit_report['message'])
|
|
362
|
+
return None
|
|
363
|
+
|
|
351
364
|
# path_to_logs = f'{prepobj.vadir}/{prepobj.mapname}_va.log'
|
|
352
365
|
# reconfigure_logging(temp_logger, buffer, path_to_logs)
|
|
353
366
|
position = prepobj.args.positions
|
|
@@ -1031,6 +1031,163 @@
|
|
|
1031
1031
|
],
|
|
1032
1032
|
"additionalProperties": true
|
|
1033
1033
|
},
|
|
1034
|
+
"CifRevisionAuditRow": {
|
|
1035
|
+
"type": "object",
|
|
1036
|
+
"properties": {
|
|
1037
|
+
"ordinal": {
|
|
1038
|
+
"type": [
|
|
1039
|
+
"string",
|
|
1040
|
+
"null"
|
|
1041
|
+
]
|
|
1042
|
+
},
|
|
1043
|
+
"revision_ordinal": {
|
|
1044
|
+
"type": [
|
|
1045
|
+
"string",
|
|
1046
|
+
"null"
|
|
1047
|
+
]
|
|
1048
|
+
},
|
|
1049
|
+
"data_content_type": {
|
|
1050
|
+
"type": [
|
|
1051
|
+
"string",
|
|
1052
|
+
"null"
|
|
1053
|
+
]
|
|
1054
|
+
},
|
|
1055
|
+
"provider": {
|
|
1056
|
+
"type": [
|
|
1057
|
+
"string",
|
|
1058
|
+
"null"
|
|
1059
|
+
]
|
|
1060
|
+
},
|
|
1061
|
+
"type": {
|
|
1062
|
+
"type": [
|
|
1063
|
+
"string",
|
|
1064
|
+
"null"
|
|
1065
|
+
]
|
|
1066
|
+
},
|
|
1067
|
+
"description": {
|
|
1068
|
+
"type": [
|
|
1069
|
+
"string",
|
|
1070
|
+
"null"
|
|
1071
|
+
]
|
|
1072
|
+
},
|
|
1073
|
+
"details": {
|
|
1074
|
+
"type": [
|
|
1075
|
+
"string",
|
|
1076
|
+
"null"
|
|
1077
|
+
]
|
|
1078
|
+
}
|
|
1079
|
+
},
|
|
1080
|
+
"additionalProperties": true
|
|
1081
|
+
},
|
|
1082
|
+
"CifRevisionAuditRule": {
|
|
1083
|
+
"type": "object",
|
|
1084
|
+
"properties": {
|
|
1085
|
+
"type": {
|
|
1086
|
+
"type": "string"
|
|
1087
|
+
},
|
|
1088
|
+
"description": {
|
|
1089
|
+
"type": "string"
|
|
1090
|
+
},
|
|
1091
|
+
"decision": {
|
|
1092
|
+
"type": "string"
|
|
1093
|
+
},
|
|
1094
|
+
"reason_code": {
|
|
1095
|
+
"type": "string"
|
|
1096
|
+
}
|
|
1097
|
+
},
|
|
1098
|
+
"additionalProperties": true
|
|
1099
|
+
},
|
|
1100
|
+
"CifRevisionAuditIssue": {
|
|
1101
|
+
"type": "object",
|
|
1102
|
+
"properties": {
|
|
1103
|
+
"severity": {
|
|
1104
|
+
"type": "string"
|
|
1105
|
+
},
|
|
1106
|
+
"code": {
|
|
1107
|
+
"type": "string"
|
|
1108
|
+
},
|
|
1109
|
+
"message": {
|
|
1110
|
+
"type": "string"
|
|
1111
|
+
},
|
|
1112
|
+
"details": {
|
|
1113
|
+
"type": [
|
|
1114
|
+
"string",
|
|
1115
|
+
"object",
|
|
1116
|
+
"array",
|
|
1117
|
+
"null"
|
|
1118
|
+
]
|
|
1119
|
+
}
|
|
1120
|
+
},
|
|
1121
|
+
"additionalProperties": true
|
|
1122
|
+
},
|
|
1123
|
+
"CifRevisionAuditReport": {
|
|
1124
|
+
"type": "object",
|
|
1125
|
+
"properties": {
|
|
1126
|
+
"run_validation": {
|
|
1127
|
+
"type": "boolean"
|
|
1128
|
+
},
|
|
1129
|
+
"decision": {
|
|
1130
|
+
"type": "string",
|
|
1131
|
+
"enum": [
|
|
1132
|
+
"run",
|
|
1133
|
+
"skip",
|
|
1134
|
+
"unknown"
|
|
1135
|
+
]
|
|
1136
|
+
},
|
|
1137
|
+
"reason_code": {
|
|
1138
|
+
"type": "string"
|
|
1139
|
+
},
|
|
1140
|
+
"message": {
|
|
1141
|
+
"type": "string"
|
|
1142
|
+
},
|
|
1143
|
+
"matched_rules": {
|
|
1144
|
+
"type": "array",
|
|
1145
|
+
"items": {
|
|
1146
|
+
"$ref": "#/definitions/CifRevisionAuditRule"
|
|
1147
|
+
}
|
|
1148
|
+
},
|
|
1149
|
+
"issues": {
|
|
1150
|
+
"type": "array",
|
|
1151
|
+
"items": {
|
|
1152
|
+
"$ref": "#/definitions/CifRevisionAuditIssue"
|
|
1153
|
+
}
|
|
1154
|
+
},
|
|
1155
|
+
"audit_revision_details": {
|
|
1156
|
+
"type": "object",
|
|
1157
|
+
"properties": {
|
|
1158
|
+
"row_count": {
|
|
1159
|
+
"type": "integer"
|
|
1160
|
+
},
|
|
1161
|
+
"rows": {
|
|
1162
|
+
"type": "array",
|
|
1163
|
+
"items": {
|
|
1164
|
+
"$ref": "#/definitions/CifRevisionAuditRow"
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
},
|
|
1168
|
+
"additionalProperties": true
|
|
1169
|
+
},
|
|
1170
|
+
"model_count": {
|
|
1171
|
+
"type": "integer"
|
|
1172
|
+
},
|
|
1173
|
+
"model_reports": {
|
|
1174
|
+
"type": "array",
|
|
1175
|
+
"items": {
|
|
1176
|
+
"$ref": "#/definitions/CifRevisionAuditReport"
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
},
|
|
1180
|
+
"additionalProperties": true
|
|
1181
|
+
},
|
|
1182
|
+
"AuditValidation": {
|
|
1183
|
+
"type": "object",
|
|
1184
|
+
"properties": {
|
|
1185
|
+
"cif_revision_audit": {
|
|
1186
|
+
"$ref": "#/definitions/CifRevisionAuditReport"
|
|
1187
|
+
}
|
|
1188
|
+
},
|
|
1189
|
+
"additionalProperties": true
|
|
1190
|
+
},
|
|
1034
1191
|
"DataValidation": {
|
|
1035
1192
|
"type": "object",
|
|
1036
1193
|
"description": "Legacy or diagnostic validation container retained for compatibility.",
|
|
@@ -2708,6 +2865,9 @@
|
|
|
2708
2865
|
"data_integrity": {
|
|
2709
2866
|
"$ref": "#/definitions/DataIntegrity"
|
|
2710
2867
|
},
|
|
2868
|
+
"audit_validation": {
|
|
2869
|
+
"$ref": "#/definitions/AuditValidation"
|
|
2870
|
+
},
|
|
2711
2871
|
"data_validation": {
|
|
2712
2872
|
"$ref": "#/definitions/DataValidation"
|
|
2713
2873
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|