waffensachkunde

Waffensachkunde – Lernsoftware für die Sachkundeprüfung nach § 7 WaffG. Barrierefrei, offline, EUPL-1.2.

/ data-pipeline diagnose_columns.py

3,2 KB Rohdatei
data-pipeline/diagnose_columns.py — 86 Zeilen
1 # -*- coding: utf-8 -*-
2 """Diagnose: exakte x-Ausdehnung von Antworttext und Ankreuzkästchen."""
3 import io
4 import sys
5 from collections import Counter
6
7 import fitz
8
9 sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
10 PDF = r"C:\Antigravity\Waffensachkundeprüfung\Fragenkatalog_sachkunde_mitAntworten.pdf"
11 doc = fitz.open(PDF)
12
13 box_x0 = Counter()
14 box_x1 = Counter()
15 box_w = Counter()
16 total_boxes = 0
17 for pno in range(5, doc.page_count):
18 for d in doc[pno].get_drawings():
19 for item in d["items"]:
20 if item[0] == "re":
21 r = item[1]
22 if 6 < r.width < 20 and abs(r.width - r.height) < 4 and r.x0 > 400:
23 total_boxes += 1
24 box_x0[round(r.x0)] += 1
25 box_x1[round(r.x1)] += 1
26 box_w[round(r.width, 1)] += 1
27 print(f"Kästchen gesamt (x0>400): {total_boxes}")
28 print(f" x0-Verteilung: {sorted(box_x0.items())}")
29 print(f" x1-Verteilung: {sorted(box_x1.items())}")
30 print(f" Breiten: {sorted(box_w.items())}")
31
32 # Maximale Textausdehnung in der Antwortspalte, getrennt nach Seiten mit/ohne Kästchen
33 max_x1_with = Counter()
34 max_x1_without = Counter()
35 for pno in range(5, doc.page_count):
36 page = doc[pno]
37 boxes = []
38 for d in page.get_drawings():
39 for item in d["items"]:
40 if item[0] == "re":
41 r = item[1]
42 if 6 < r.width < 20 and abs(r.width - r.height) < 4 and r.x0 > 400:
43 boxes.append(r)
44 for x0, y0, x1, y1, text, *_ in page.get_text("words"):
45 if y0 < 92 or x0 < 292 or not text.strip():
46 continue
47 near_box = any(abs(b.y0 - y0) < 12 for b in boxes)
48 (max_x1_with if near_box else max_x1_without)[round(x1 / 5) * 5] += 1
49
50 print(f"\nTextende x1 (gerundet auf 5) in Antwortspalte, Zeilen MIT Kästchen:")
51 print(f" {sorted(max_x1_with.items())[-8:]}")
52 print(f"Textende x1, Zeilen OHNE Kästchen (Freitext):")
53 print(f" {sorted(max_x1_without.items())[-8:]}")
54
55 # Kollidiert Text jemals mit einem Kästchen?
56 collisions = 0
57 examples = []
58 for pno in range(5, doc.page_count):
59 page = doc[pno]
60 boxes = []
61 for d in page.get_drawings():
62 for item in d["items"]:
63 if item[0] == "re":
64 r = item[1]
65 if 6 < r.width < 20 and abs(r.width - r.height) < 4 and r.x0 > 400:
66 boxes.append(r)
67 if not boxes:
68 continue
69 for x0, y0, x1, y1, text, *_ in page.get_text("words"):
70 if y0 < 92 or not text.strip():
71 continue
72 for b in boxes:
73 if x0 < b.x1 and x1 > b.x0 and y0 < b.y1 and y1 > b.y0:
74 collisions += 1
75 if len(examples) < 5:
76 examples.append((pno + 1, text, round(x0), round(b.x0)))
77 print(f"\nText-Kästchen-Überlappungen: {collisions} Beispiele: {examples}")
78
79 # Wie viele Fragen hätten mit Schwelle 495 statt 505 ein Kästchen?
80 for threshold in (495, 500, 505):
81 n = sum(1 for pno in range(5, doc.page_count)
82 for d in doc[pno].get_drawings()
83 for item in d["items"]
84 if item[0] == "re" and 6 < item[1].width < 20
85 and abs(item[1].width - item[1].height) < 4 and item[1].x0 > threshold)
86 print(f"Kästchen bei Schwelle x0>{threshold}: {n}")