63 lines
1.8 KiB
Python
63 lines
1.8 KiB
Python
"""Task loading and prefix substitution."""
|
|
import json
|
|
import os
|
|
|
|
|
|
_B36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
|
|
|
|
def _b36(n, width):
|
|
out = ""
|
|
while n:
|
|
n, r = divmod(n, 36)
|
|
out = _B36[r] + out
|
|
return out.rjust(width, "0")[-width:]
|
|
|
|
|
|
def prefix_for(run_no, task_id):
|
|
"""Z + run (4 chars base36) + task (3 chars base36) + '_'.
|
|
|
|
Run numbers below 36**3 keep a digit at position 2; the proxy uses this to tell
|
|
harness objects from other Z objects. Old prefixes (Z201001_) match the same pattern.
|
|
"""
|
|
task_no = int("".join(ch for ch in task_id if ch.isdigit()) or 0)
|
|
return f"Z{_b36(int(run_no), 4)}{_b36(task_no, 3)}_"
|
|
|
|
|
|
class Task:
|
|
def __init__(self, task_dir, prefix):
|
|
self.dir = task_dir
|
|
self.prefix = prefix
|
|
self.meta = json.loads(self._read("task.json"))
|
|
self.id = self.meta["id"]
|
|
|
|
def _read(self, rel):
|
|
with open(os.path.join(self.dir, rel)) as f:
|
|
return f.read()
|
|
|
|
def sub(self, text):
|
|
return text.replace("{{P}}", self.prefix).replace("{{p}}", self.prefix.lower())
|
|
|
|
def text(self, rel):
|
|
return self.sub(self._read(rel))
|
|
|
|
def objects(self, key):
|
|
"""List of dicts with substituted names and loaded source (if 'file')."""
|
|
out = []
|
|
for o in self.meta.get(key, []):
|
|
o = {k: self.sub(v) if isinstance(v, str) else v for k, v in o.items()}
|
|
if "file" in o:
|
|
o["source"] = self.text(o["file"])
|
|
if "testclasses_file" in o:
|
|
o["testclasses_source"] = self.text(o["testclasses_file"])
|
|
out.append(o)
|
|
return out
|
|
|
|
@property
|
|
def spec(self):
|
|
return self.text("spec.md")
|
|
|
|
@property
|
|
def out_of_scope(self):
|
|
return [self.sub(n) for n in self.meta.get("out_of_scope", [])]
|