analyze_log: accept a direct log path as single argument
The two-argument form (date_time sud_name) is unchanged. When called with a single argument it is treated as a direct path to any log_*.json (e.g. a server-session log without a Sud name); the forecast panel is shown only if a matching forecast_*.json exists alongside it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+24
-13
@@ -1,14 +1,16 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Offline analysis of a log/forecast pair produced by the BrewPi server.
|
||||
"""Offline analysis of a log (and optional forecast) produced by the BrewPi server.
|
||||
|
||||
Usage:
|
||||
python analyze_log.py <date_time> <sud_name> [--log-dir <dir>]
|
||||
python analyze_log.py <path/to/log_*.json>
|
||||
|
||||
Example:
|
||||
python analyze_log.py 20260628T184903 Sud-0010
|
||||
The two-argument form loads logs/<date_time>_<sud_name>.json and its paired
|
||||
forecast file. The single-argument form accepts a direct path to any
|
||||
log_*.json (e.g. a server-session log without a Sud name); the forecast panel
|
||||
is shown only if a matching forecast_*.json exists alongside it.
|
||||
|
||||
Loads logs/<date_time>_{sud_name}.json (log and forecast) and renders the same
|
||||
two plots the PyQt GUI shows: the 3-panel realtime strip chart and the
|
||||
Renders the 3-panel realtime strip chart and, when available, the
|
||||
forecast-vs-actual comparison.
|
||||
"""
|
||||
|
||||
@@ -102,32 +104,41 @@ def plot_forecast(forecast, samples, name):
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser.add_argument('date_time', help='Timestamp part of the filename, e.g. 20260628T184903')
|
||||
parser.add_argument('sud_name', help='Sud name part of the filename, e.g. Sud-0010')
|
||||
parser.add_argument('date_time_or_path',
|
||||
help='Timestamp part of the filename (e.g. 20260628T184903) '
|
||||
'or a direct path to a log_*.json file')
|
||||
parser.add_argument('sud_name', nargs='?', default=None,
|
||||
help='Sud name part of the filename (e.g. Sud-0010); '
|
||||
'omit when passing a direct file path')
|
||||
default_log_dir = Path(__file__).parent.parent / 'logs'
|
||||
parser.add_argument('--log-dir', default=str(default_log_dir),
|
||||
help='Directory containing the log files (default: %(default)s)')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.sud_name is None:
|
||||
log_path = Path(args.date_time_or_path)
|
||||
stem = log_path.stem[len('log_'):] if log_path.stem.startswith('log_') else log_path.stem
|
||||
forecast_path = log_path.parent / 'forecast_{}.json'.format(stem)
|
||||
else:
|
||||
log_dir = Path(args.log_dir)
|
||||
stem = '{}_{}'.format(args.date_time, args.sud_name)
|
||||
stem = '{}_{}'.format(args.date_time_or_path, args.sud_name)
|
||||
log_path = log_dir / 'log_{}.json'.format(stem)
|
||||
forecast_path = log_dir / 'forecast_{}.json'.format(stem)
|
||||
|
||||
for path in (log_path, forecast_path):
|
||||
if not path.exists():
|
||||
print('Error: file not found: {}'.format(path), file=sys.stderr)
|
||||
if not log_path.exists():
|
||||
print('Error: file not found: {}'.format(log_path), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
with open(log_path) as f:
|
||||
log = json.load(f)
|
||||
with open(forecast_path) as f:
|
||||
forecast = json.load(f)
|
||||
|
||||
name = log.get('Name', stem)
|
||||
samples = log['Samples']
|
||||
|
||||
plot_realtime(samples, name)
|
||||
if forecast_path.exists():
|
||||
with open(forecast_path) as f:
|
||||
forecast = json.load(f)
|
||||
plot_forecast(forecast, samples, name)
|
||||
|
||||
plt.show()
|
||||
|
||||
Reference in New Issue
Block a user