Files
LiberPrayground/LP/InterruptToWeb.py
2021-03-01 20:30:38 +01:00

353 lines
13 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
from Alphabet import RUNES
from LPath import FILES_ALL, FILES_SOLVED, FILES_UNSOLVED, LPath
from InterruptDB import InterruptDB
from InterruptIndices import InterruptIndices
NORM_MIN = 0.40
NORM_MAX = 0.98
HIGH_MIN = 1.25
HIGH_MAX = 1.65
MEM_DB = None # stores a DBToMem object
#########################################
# DBToMem : Loads all dbs into memory so you don't need to re-read each
#########################################
class DBToMem(object):
def __init__(self):
lsc = InterruptDB.load_scores
self.indices = InterruptIndices()
self.db = {}
for ioc_kind in ['high', 'norm']:
prfx = 'db_' + ioc_kind
self.db[ioc_kind] = {
'main': lsc(prfx),
'mod': {typ: [
(mod, p, lsc(prfx + f'_mod_{typ}_{mod}.{p}'))
for mod in range(2, 4) for p in range(mod)]
for typ in 'ab'},
'mirror': {typ: lsc(prfx + f'_pattern_mirror_{typ}.0')
for typ in 'ab'},
'shift': {kl: lsc(prfx + f'_pattern_shift_{kl}.0')
for kl in range(4, 19)},
}
def vertical_kl(self, tbl, iocKey, fname, irp, kl_range):
pointer = self.db[iocKey]['main'][fname][irp]
maxscore = 0
bestkl = -1
for kl in kl_range:
try:
score = pointer[kl][0]
tbl[kl].append(score)
if score > maxscore:
maxscore = score
bestkl = kl
except KeyError:
tbl[kl].append('')
return bestkl
#########################################
# HTML helper class
#########################################
class HTML(object):
@staticmethod
def attr(attrib):
txt = ''
if attrib:
for k, v in attrib.items():
txt += f' {k}="{v}"'
return txt
@staticmethod
def mark(x, low=0, high=1):
if x <= low:
return ' class="m0"'
return f' class="m{int((min(high, x) - low) / (high - low) * 14) + 1}"'
@staticmethod
def p_warn(content):
return f'<p class="red">{content}</p>\n'
@staticmethod
def dt_dd(title, content, attrib=None):
return f'<dt>{title}</dt>\n<dd{HTML.attr(attrib)}>\n{content}</dd>\n'
@staticmethod
def num_stream(stream, score_min=0, score_max=1):
dot2 = isinstance(score_min, float) or isinstance(score_max, float)
txt = ''
for x in stream:
txt += '<div'
if isinstance(x, tuple):
txt += HTML.attr(x[0])
x = x[1]
if not isinstance(x, str):
txt += HTML.mark(x, score_min, score_max)
txt += f'>{x:.2f}</div>' if dot2 else f'>{x}</div>'
return txt + '\n'
@staticmethod
def num_table(table, num_ranges, thr=1, thc=1):
txt = '<table>\n'
for r, row in enumerate(table):
attr = ''
if isinstance(row, tuple):
attr = HTML.attr(row[0])
row = row[1]
txt += f'<tr{attr}>'
for c, val in enumerate(row):
td = 'th' if r < thr or c < thc else 'td'
attr = ''
if isinstance(val, tuple):
attr = HTML.attr(val[0])
val = val[1]
isnum = False
dot2 = False
if not isinstance(val, str):
for sc_min, sc_max, L, T, R, B in num_ranges:
if c >= L and c < R and r >= T and r < B:
isnum = True
attr += HTML.mark(val, sc_min, sc_max)
dot2 = isinstance(sc_min, float) \
or isinstance(sc_max, float)
break
if isnum:
if val <= 0:
txt += f'<{td}></{td}>'
elif dot2:
txt += f'<{td}{attr}>{val:.2f}</{td}>'
else:
txt += f'<{td}{attr}>{val}</{td}>'
else:
txt += f'<{td}{attr}>{val}</{td}>'
txt += '</tr>\n'
return txt + '</table>\n'
#########################################
# InterruptToWeb : Read interrupt DB and create html graphic / matrix
#########################################
class InterruptToWeb(object):
def __init__(self, template='templates/ioc.html'):
with open(LPath.results(template), 'r') as f:
self.template = f.read()
def table_reliable(self, iocKey):
head = [''] + [f'<div>{x}</div>' for x in FILES_ALL]
foot = ['Total'] + [MEM_DB.indices.total(x) for x in FILES_ALL]
tbl = [[irp] for irp in RUNES]
for name in FILES_ALL:
pointer = MEM_DB.db[iocKey]['main'][name]
for irp in range(29):
maxirp = set(maxirp for _, maxirp in pointer[irp].values())
worst_irp_c = min(maxirp)
if worst_irp_c == 0 and max(maxirp) != 0:
tbl[irp].append('?')
continue
_, num = MEM_DB.indices.consider(name, irp, worst_irp_c)
tbl[irp].append(num)
tbl.insert(0, ({'class': 'rotate'}, head))
tbl.append(({'class': 'small'}, foot))
return HTML.num_table(tbl, [(384, 812, 1, 1, 99, len(tbl) - 1)])
def table_interrupt(self, iocKey, irp, pmin, pmax):
head = [''] + [f'<div>{x}</div>' for x in FILES_ALL]
foot = ['best']
tbl = [[kl] for kl in range(33)]
for ni, name in enumerate(FILES_ALL):
bestkl = MEM_DB.vertical_kl(tbl, iocKey, name, irp, range(1, 33))
foot.append(bestkl)
tbl[0] = ({'class': 'rotate'}, head)
tbl.append(({'class': 'small'}, foot))
return HTML.num_table(tbl, [(pmin, pmax, 1, 1, 99, len(tbl) - 1)])
def make(self, iocKey, outfile, pmin, pmax):
nav = ''
txt = ''
for i in range(29):
nav += f'<a href="#tb-i{i}">{RUNES[i]}</a>\n'
txt += f'<h3 id="tb-i{i}">Interrupt {i}: <b>{RUNES[i]}</b></h3>'
txt += self.table_interrupt(iocKey, i, pmin, pmax)
html = self.template.replace('__NAVIGATION__', nav)
html = html.replace('__TAB_RELIABLE__', self.table_reliable(iocKey))
html = html.replace('__INTERRUPT_TABLES__', txt)
with open(LPath.results(outfile), 'w') as f:
f.write(html)
#########################################
# ChapterToWeb : Combine different analyses for a single chapter
#########################################
class ChapterToWeb(object):
def __init__(self, template='templates/pages.html'):
with open(LPath.results(template), 'r') as f:
self.template = f.read()
def sec_ioc(self, fname):
tbl = [[x] for x in range(33)]
foot = ['best']
for typ in ['high', 'norm']:
for irp in [0, 28]:
bestkl = MEM_DB.vertical_kl(tbl, typ, fname, irp, range(1, 33))
foot.append(bestkl)
for irp in [0, 28]:
for kl in range(1, 33):
score, maxirp = MEM_DB.db['high']['main'][fname][irp][kl]
_, num = MEM_DB.indices.consider(fname, irp, maxirp)
tbl[kl].append(num // kl)
tbl[0] = ['',
({'colspan': 2}, 'IoC-<a href="./ioc/high.html">high</a>'),
({'colspan': 2}, 'IoC-<a href="./ioc/norm.html">norm</a>'),
({'colspan': 2}, 'Runes / keylen')]
tbl.insert(1, [''] + [RUNES[irp] for irp in [0, 28]] * 3)
tbl.append(({'class': 'small'}, foot))
return HTML.num_table(tbl, [
(HIGH_MIN, HIGH_MAX, 1, 2, 3, len(tbl) - 1),
(NORM_MIN, NORM_MAX, 3, 2, 5, len(tbl) - 1),
(28, 100, 5, 2, 7, len(tbl) - 1)
], thr=2)
def sec_ioc_mod(self, fname):
def add_line():
res = []
for kl, (score, maxirp) in scores[fname][irp].items():
if kl > len(res):
res += [''] * (kl - len(res))
res[kl - 1] = score
if typ == 'a':
_, num = MEM_DB.indices.consider(fname, irp, maxirp)
num = num // mod + (1 if off < num % mod else 0)
else:
_, num = MEM_DB.indices.consider_mod_b(
fname, irp, maxirp, mod)
num = num[off]
return num, res
txt = '<dl>\n'
for typ, title, min_kl in [('a', 'Interrupt first, then mod', 1),
('b', 'Mod first, then interrupt', 2)]:
for kind, pmin, pmax in [('high', HIGH_MIN, HIGH_MAX),
('norm', NORM_MIN, NORM_MAX)]:
tbl = []
max_kl = 0
for irp in [0, 28]:
for mod, off, scores in MEM_DB.db[kind]['mod'][typ]:
num, line = add_line()
max_kl = max(max_kl, len(line))
line = line[min_kl - 1:]
tbl.append([f'{RUNES[irp]}.{mod}.{off}', num] + line)
head = [['', 'runes'] + [i for i in range(min_kl, max_kl + 1)]]
tbl = HTML.num_table(head + tbl, [(pmin, pmax, 2, 1, 99, 99)])
ttl = f'{title} (IoC-<a href="../ioc/{kind}.html">{kind}</a>):'
txt += HTML.dt_dd(ttl, tbl)
return txt + '</dl>\n'
def sec_ioc_pattern(self, fname):
def sub_table(title, key, variants, cols):
txt = ''
per_kl = key == 'shift'
for kind, pmin, pmax in [('high', HIGH_MIN, HIGH_MAX),
('norm', NORM_MIN, NORM_MAX)]:
tbl = []
for irp in [0, 28]:
for typ in variants:
pointer = MEM_DB.db[kind][key][typ][fname][irp]
scores = []
for kl in cols:
try:
scores.append(pointer[kl][0])
except KeyError:
break
_, num = MEM_DB.indices.consider(fname, irp, 20)
if per_kl:
actual_kl = int(typ)
num //= actual_kl
tbl.append([f'{RUNES[irp]}.{typ}', num] + scores)
head = [['', 'runes'] + [i for i in cols]]
colors = [(pmin, pmax, 2, 1, 99, 99)]
if per_kl:
colors.append((28, 100, 1, 1, 2, 99))
tbl = HTML.num_table(head + tbl, colors)
ttl = f'{title} (IoC-<a href="../ioc/{kind}.html">{kind}</a>):'
txt += HTML.dt_dd(ttl, tbl)
return txt
txt = '<dl>\n'
txt += sub_table('Mirror Pattern', 'mirror', 'ab', range(4, 19))
txt += sub_table('Shift Pattern', 'shift', range(4, 19), range(1, 18))
return txt + '</dl>\n'
def make(self, fname, outfile):
html = self.template.replace('__FNAME__', fname)
if fname.startswith('solved_'):
ref = f'<a href="./{fname[7:]}.html">“unsolved” page</a>'
html = html.replace('__SEC_IOC__', HTML.p_warn(
f'IoC is disabled on solved pages. Open the {ref} instead.'))
else:
html = html.replace('__SEC_IOC__', self.sec_ioc(fname))
if fname in FILES_UNSOLVED:
html = html.replace('__SEC_IOC_MOD__', self.sec_ioc_mod(fname))
html = html.replace('__SEC_IOC_PATTERN__',
self.sec_ioc_pattern(fname))
else:
html = html.replace('__SEC_IOC_MOD__', HTML.p_warn(
'Mod-IoC is disabled on solved pages'))
html = html.replace('__SEC_IOC_PATTERN__', HTML.p_warn(
'Pattern-IoC is disabled on solved pages'))
with open(LPath.results(outfile), 'w') as f:
f.write(html)
#########################################
# IndexToWeb : Creates the index page with links to all other analysis pages
#########################################
class IndexToWeb(object):
def __init__(self, template='templates/index.html'):
with open(LPath.results(template), 'r') as f:
self.template = f.read()
def make(self, the_links, outfile='index.html'):
html = self.template
for key, links in the_links.items():
html = html.replace(
key, ' '.join(f'<a href="./{x}">{y}</a>' for x, y in links))
with open(LPath.results(outfile), 'w') as f:
f.write(html)
#########################################
# main entry
#########################################
if __name__ == '__main__':
links = {
'__A_IOC__': [('ioc/high.html', 'Highest (bluntly)'),
('ioc/norm.html', 'Normal english (1.7767)')],
'__A_CHAPTER__': [(f'pages/{x}.html', x) for x in FILES_UNSOLVED],
'__A_SOLVED__': [(f'pages/{x}.html', x) for x in FILES_SOLVED],
}
MEM_DB = DBToMem()
iocweb = InterruptToWeb()
iocweb.make('high', 'ioc/high.html', HIGH_MIN, HIGH_MAX)
iocweb.make('norm', 'ioc/norm.html', NORM_MIN, NORM_MAX)
ctw = ChapterToWeb()
for x, y in links['__A_CHAPTER__'] + links['__A_SOLVED__']:
ctw.make(y, x)
IndexToWeb().make(links)