Add numberdealers-ng error-checking style and change report layout

This commit is contained in:
Midgard 2024-05-20 20:33:24 +02:00
parent e6dc6b06cb
commit dd2435e0ab
Signed by: midgard
GPG key ID: 511C112F1331BBB4
3 changed files with 159 additions and 58 deletions

View file

@ -3,6 +3,7 @@
import re import re
import json import json
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum
from typing import Optional, List from typing import Optional, List
from .users import USERS from .users import USERS
@ -49,7 +50,11 @@ NUMBER_EMOJI = {
URL_PREFIX = "https://mattermost.zeus.gent/zeus/pl/" URL_PREFIX = "https://mattermost.zeus.gent/zeus/pl/"
def parse(message_json_lines): class ErrorStyle(Enum):
NUMBERDEALERS = 1
NUMBERDEALERS_NG = 2
def parse(message_json_lines, error_style: ErrorStyle):
second_last_number = None second_last_number = None
second_last_message = None second_last_message = None
last_number = None last_number = None
@ -57,6 +62,7 @@ def parse(message_json_lines):
numbers = [] numbers = []
errors = [] errors = []
start_number = None start_number = None
expected = None
for line in message_json_lines: for line in message_json_lines:
line = json.loads(line) line = json.loads(line)
# Ignore non-message posts (e.g. join/leave) # Ignore non-message posts (e.g. join/leave)
@ -119,33 +125,44 @@ def parse(message_json_lines):
start_number = number start_number = number
last_number = number - 1 last_number = number - 1
second_last_number = number - 2 second_last_number = number - 2
if error_style == ErrorStyle.NUMBERDEALERS_NG:
expected = number
numbers.append(message_obj) numbers.append(message_obj)
if number != last_number + 1: if error_style == ErrorStyle.NUMBERDEALERS_NG:
if number == second_last_number + 2 and last_number != second_last_number + 1: if number == expected:
errors.pop() expected = number + 1
errors.append(
ShouldHaveBeen(last_message, second_last_message, number-1)
)
elif number == last_number:
errors.append(
Duplicate(message_obj, last_message, last_number+1)
)
elif number == second_last_number + 1 and last_number != second_last_number + 1:
errors.pop()
errors.append(
Stray(last_message, second_last_message, last_number+1)
)
elif last_number == second_last_number + 1 and number == last_number + 2:
errors.pop()
errors.append(
Skipped(last_message, second_last_message, number-1)
)
else: else:
errors.append( errors.append(
Jump(message_obj, last_message, last_number+1) ShouldHaveBeen(message_obj, last_message, expected)
) )
else:
if number != last_number + 1:
if number == second_last_number + 2 and last_number != second_last_number + 1:
errors.pop()
errors.append(
ShouldHaveBeen(last_message, second_last_message, number-1)
)
elif number == last_number:
errors.append(
Duplicate(message_obj, last_message, last_number+1)
)
elif number == second_last_number + 1 and last_number != second_last_number + 1:
errors.pop()
errors.append(
Stray(last_message, second_last_message, last_number+1)
)
elif last_number == second_last_number + 1 and number == last_number + 2:
errors.pop()
errors.append(
Skipped(last_message, second_last_message, number-1)
)
else:
errors.append(
Jump(message_obj, last_message, last_number+1)
)
second_last_number = last_number second_last_number = last_number
second_last_message = last_message second_last_message = last_message
last_number = number last_number = number
@ -158,7 +175,7 @@ def main():
import sys import sys
from datetime import datetime, timezone from datetime import datetime, timezone
numbers, _errors = parse(sys.stdin) numbers, _errors = parse(sys.stdin, ErrorStyle.NUMBERDEALERS_NG)
for number in numbers: for number in numbers:
moment = datetime.fromtimestamp(number.create_at / 1000, timezone.utc) moment = datetime.fromtimestamp(number.create_at / 1000, timezone.utc)
moment_str = str(moment).replace("+00:00", "") moment_str = str(moment).replace("+00:00", "")

View file

@ -14,21 +14,21 @@ def mention(message: parse_numberdealers.Message):
def str_from_error(err): def str_from_error(err):
if isinstance(err, parse_numberdealers.UnrecognizedNumber): if isinstance(err, parse_numberdealers.UnrecognizedNumber):
msg = f"- Unrecognized post {link(err.message.message, err.message)}" msg = f"🚨 Unrecognized post {link(err.message.message, err.message)}"
elif isinstance(err, parse_numberdealers.EditedMessage): elif isinstance(err, parse_numberdealers.EditedMessage):
msg = f"- Edited post {link(err.message.message, err.message)}" msg = f"🚨 Edited post {link(err.message.message, err.message)}"
elif isinstance(err, parse_numberdealers.NonNumberMessage): elif isinstance(err, parse_numberdealers.NonNumberMessage):
msg = f"- Non-number message {link(err.message.message, err.message)}" msg = f"🚨 Non-number message {link(err.message.message, err.message)}"
elif isinstance(err, parse_numberdealers.ShouldHaveBeen): elif isinstance(err, parse_numberdealers.ShouldHaveBeen):
msg = f"- {link(err.message.recognized_number, err.message)} should have been {err.expected_number}" msg = f"🚨 {link(err.message.recognized_number, err.message)} should have been {err.expected_number}"
elif isinstance(err, parse_numberdealers.Duplicate): elif isinstance(err, parse_numberdealers.Duplicate):
msg = f"- Duplicate {link(err.message.recognized_number, err.message)}" msg = f"🚨 Duplicate {link(err.message.recognized_number, err.message)}"
elif isinstance(err, parse_numberdealers.Stray): elif isinstance(err, parse_numberdealers.Stray):
msg = f"- Stray {link(err.message.recognized_number, err.message)}" msg = f"🚨 Stray {link(err.message.recognized_number, err.message)}"
elif isinstance(err, parse_numberdealers.Skipped): elif isinstance(err, parse_numberdealers.Skipped):
msg = f"- {link('Skipped', err.message)} {err.expected_number}" msg = f"🚨 {link('Skipped', err.message)} {err.expected_number}"
elif isinstance(err, parse_numberdealers.Jump): elif isinstance(err, parse_numberdealers.Jump):
msg = f"- Going from {link(err.previous_message.recognized_number, err.previous_message)}" \ msg = f"🚨 Going from {link(err.previous_message.recognized_number, err.previous_message)}" \
f" to {link(err.message.recognized_number, err.message)}" f" to {link(err.message.recognized_number, err.message)}"
return msg + mention(err.message) return msg + mention(err.message)
@ -36,15 +36,20 @@ def str_from_error(err):
def report_errors(errors): def report_errors(errors):
if errors: if errors:
print("🚨 Errors: 🚨") return list(map(str_from_error, errors))
print("\n".join(map(str_from_error, errors)))
else: else:
print("No errors! 🎉") return []
def main(): def main():
import sys import sys
numbers, errors = parse_numberdealers.parse(sys.stdin)
error_style = {
"--numberdealers": parse_numberdealers.ErrorStyle.NUMBERDEALERS,
"--numberdealers-ng": parse_numberdealers.ErrorStyle.NUMBERDEALERS_NG
}[sys.argv[1]]
numbers, errors = parse_numberdealers.parse(sys.stdin, error_style)
if numbers == [] and errors == []: if numbers == [] and errors == []:
print("No input data") print("No input data")
@ -53,7 +58,10 @@ def main():
print("No valid number messages!") print("No valid number messages!")
else: else:
print(f"Checked from {numbers[0].recognized_number} up to {numbers[-1].recognized_number}") print(f"Checked from {numbers[0].recognized_number} up to {numbers[-1].recognized_number}")
report_errors(errors) if not errors:
print("No errors! 🎉")
else:
print("\n".join(report_errors(errors)))
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -1,34 +1,110 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import sys import sys
from itertools import zip_longest
from numberdealers import parse_numberdealers, times, report_errors, numbers_per_user from numberdealers import parse_numberdealers, times, report_errors, numbers_per_user
from numberdealers.times import format_time
from mdtables import Table, Column
def main(): def main():
channel = sys.argv[1] numberdealers_tail = sys.argv[1]
numbers, errors = parse_numberdealers.parse(sys.stdin) numberdealers_history = sys.argv[2]
numberdealers_ng = sys.argv[3]
if numbers == [] and errors == []: with \
print("No input data") open(numberdealers_tail, "r") as nd_tail, \
return open(numberdealers_history, "r") as nd_history, \
elif numbers == []: open(numberdealers_ng, "r") as nd_ng:
print("No valid number messages!") numbers_tail, errors_tail = parse_numberdealers.parse(nd_tail, parse_numberdealers.ErrorStyle.NUMBERDEALERS)
return numbers_history, _errors = parse_numberdealers.parse(nd_history, parse_numberdealers.ErrorStyle.NUMBERDEALERS)
numbers_ng, errors_ng = parse_numberdealers.parse(nd_ng, parse_numberdealers.ErrorStyle.NUMBERDEALERS_NG)
assert numbers_tail
assert numbers_history
assert numbers_ng
table = Table(
Column('', alignment='right'),
Column('~NumberDealers'),
Column('~numberdealers-ng')
)
table.row(
"Stats for",
f"{numbers_history[0].recognized_number} up to {numbers_history[-1].recognized_number}",
f"{numbers_ng[0].recognized_number} up to {numbers_ng[-1].recognized_number}",
)
label = "Errors"
for error_line, error_line_ng in zip(
report_errors.report_errors(errors_tail) or [f"No errors (after {numbers_tail[-1].recognized_number})! 🎉"],
report_errors.report_errors(errors_ng) or ["No errors! 🎉"]
):
table.row(
label,
error_line,
error_line_ng
)
label = ""
table.row("", "", "")
a = times.analyze_times(numbers_history)
b = times.analyze_times(numbers_ng)
table.row( "μ", format_time(a.avg), format_time(b.avg))
table.row( "σ", format_time(a.stdev), format_time(b.stdev))
table.row("", "", "")
table.row( "min", format_time(a.min), format_time(b.min))
table.row( "P5", format_time(a.perc5), format_time(b.perc5))
table.row("median", format_time(a.med), format_time(b.med))
table.row( "P95", format_time(a.perc95), format_time(b.perc95))
table.row( "max", format_time(a.max), format_time(b.max))
print(table)
print()
print("```")
_stats_history = numbers_per_user.analyze_users(numbers_history)
stats_history = sorted(_stats_history.items(), key=lambda x: x[1], reverse=True)
_stats_ng = numbers_per_user.analyze_users(numbers_ng)
stats_ng = sorted(_stats_ng.items(), key=lambda x: x[1], reverse=True)
print(f"{'~NumberDealers':31s} ~numberdealers-ng")
i = 0
others_count_hist = 0
others_names_hist = 0
others_count_ng = 0
others_names_ng = 0
for hist, ng in zip_longest(stats_history, stats_ng):
if hist is not None:
username_hist, count_hist = hist
else:
username_hist, count_hist = "", ""
if ng is not None:
username_ng, count_ng = ng
else:
username_ng, count_ng = "", ""
if i < 7:
print(f"{str(count_hist):>5s} {username_hist:25s} {str(count_ng):>5s} {username_ng}")
else:
if hist is not None:
others_count_hist += count_hist
others_names_hist += 1
if ng is not None:
others_count_ng += count_ng
others_names_ng += 1
i += 1
if others_names_hist > 0 or others_names_ng > 0:
if others_names_hist > 0:
others_hist = f"{others_count_hist: 5d} [{others_names_hist} others]"
if others_names_ng > 0:
others_ng = f"{others_count_ng: 5d} [{others_names_ng} others]"
print(f"{others_hist:31s} {others_ng}")
print("```")
print(f"##### Checked ~{channel} from {numbers[0].recognized_number} up to {numbers[-1].recognized_number}")
if "--no-errors" not in sys.argv[2:]:
print()
report_errors.report_errors(errors)
if "--no-times" not in sys.argv[2:]:
print()
print("```")
times.report_times(numbers)
print("```")
if "--no-users" not in sys.argv[2:]:
print()
print("```")
numbers_per_user.report_users(numbers)
print("```")
if __name__ == "__main__": if __name__ == "__main__":