File indexing completed on 2026-11-09 10:47:34
0001 """Human-facing presentation of recorded log content; no record mutations."""
0002 import json
0003 import math
0004
0005
0006 TECHNICAL_KEYS = {
0007 'action', 'outcome', 'verdict', 'severity', 'summary', 'narration', 'reason',
0008 'subject_type', 'subject_key', 'username', 'duration_ms', 'sublevel',
0009 'live_default', 'notify', 'registered', 'corun_page_group_id',
0010 }
0011 LABELS = {'noise': 'Channel noise', 'real_problems': 'Recurring problems'}
0012
0013
0014 def _text(value):
0015 return value.strip() if isinstance(value, str) else ''
0016
0017
0018 def _label(key):
0019 return LABELS.get(key, str(key).replace('_', ' ').capitalize())
0020
0021
0022 def _value(value, key=''):
0023 """Keep mappings and lists readable without Python repr or lost values."""
0024 if isinstance(value, dict):
0025 return {'kind': 'mapping', 'items': [
0026 {'label': _label(k), 'value': _value(v, k)} for k, v in value.items()
0027 ]}
0028 if isinstance(value, list):
0029 return {'kind': 'list', 'items': [_value(v) for v in value]}
0030 link = key if key in {'execution_id', 'run_id'} and value and not isinstance(value, bool) else ''
0031 if isinstance(value, bool):
0032 value = 'Yes' if value else 'No'
0033 elif value is None:
0034 value = 'Not recorded'
0035 return {'kind': 'text', 'text': value, 'link': link}
0036
0037
0038 def present_log(log):
0039 extra = log.extra_data if isinstance(log.extra_data, dict) else {}
0040
0041
0042 if (extra.get('action') == 'storage_door_cycle'
0043 and extra.get('outcome') in ('error', 'timeout')
0044 and (extra.get('outcome') == 'timeout'
0045 or _text(extra.get('reason')).startswith('killed by signal '))
0046 and not _text(extra.get('summary'))):
0047 from .panda.storage_doors import failure_context
0048 extra = {**failure_context('', _text(extra.get('reason')) or 'cause not recorded'),
0049 **extra}
0050 summary = (_text(extra.get('summary')) or _text(extra.get('narration'))
0051 or _text(log.message))
0052 passages = []
0053 seen = {' '.join(summary.split())}
0054 for key, label in [('reason', 'Reason'), ('narration', 'Details')]:
0055 text = _text(extra.get(key))
0056 normalized = ' '.join(text.split())
0057 if text and normalized not in seen:
0058 passages.append({'label': label, 'text': text})
0059 seen.add(normalized)
0060 fields = [
0061 {'label': _label(key), 'value': _value(value, key)}
0062 for key, value in extra.items()
0063 if key not in TECHNICAL_KEYS and value not in (None, '', [], {})
0064 ]
0065 duration = extra.get('duration_ms')
0066 if isinstance(duration, (int, float)) and not isinstance(duration, bool) and math.isfinite(duration):
0067 duration = f'{duration:g} ms' if duration < 1000 else f'{duration / 1000:g} s'
0068 else:
0069 duration = ''
0070 action = _text(extra.get('action'))
0071 state = (_text(extra.get('verdict')) or _text(extra.get('severity'))
0072 or _text(extra.get('outcome')))
0073 return {
0074 'title': _label(action) if action else 'Log entry',
0075 'summary': summary, 'passages': passages, 'fields': fields,
0076 'state': state, 'duration': duration,
0077 'subject': _text(extra.get('subject_key')),
0078 'subject_type': _label(extra.get('subject_type', '')),
0079 'username': _text(extra.get('username')),
0080 'raw_data': json.dumps(log.extra_data, indent=2, ensure_ascii=False, default=str),
0081 }