"""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", [])]