diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79cc67c..d841460 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,6 +79,9 @@ jobs: CI: true run: npm run test:coverage + - name: Build shared library and verify codebook contracts + run: npm run build_cc && npm run test:codebook + - name: Coding-scheme validation script run: npm run test:scheme diff --git a/docs/codebook-acceptance-2026-10-13.md b/docs/codebook-acceptance-2026-10-13.md new file mode 100644 index 0000000..b0cd8e8 --- /dev/null +++ b/docs/codebook-acceptance-2026-10-13.md @@ -0,0 +1,122 @@ +# Codebook-Abnahme vom 13.10.2026 + +Dieser Bericht beschreibt den früheren Paketstand `dcee6cc…`. Die aktuelle Formatkorrektur und Aspect-Abnahme steht im [ergänzenden Bericht](codebook-aspect-acceptance-2026-10-13.md). + +Die P2-Regression bei geschützten und wiederholten Leerzeichen ist behoben. +Die bisherige Studio-Ausgabe bleibt in den stabilen Referenzfällen erhalten; +die direkten Studio- und die asynchronen Kodierbox-Exporte liefern identische Dokumentinhalte. + +## Automatisierte Nachweise + +| Prüfung | Ergebnis | +| --- | --- | +| Generator | 1.206 Tests bestanden, keine übersprungen | +| Neue Editor-Matrix | 384 Kombinationen, jeweils ESM und CommonJS, allgemeine und Code-Instruktionen | +| Ursprüngliches Studio | 32 bestehende + 20 neue unabhängige JSON-/DOCX-Referenzfälle | +| Installierte Hosts | 4.654 Exportpaare: JSON und alle relevanten DOCX-Bestandteile identisch | +| Studio | 49 API- und 66 Frontendtests bestanden | +| Kodierbox | 58 Backend- und 60 Frontendtests bestanden | +| Builds | Bibliothek und alle vier Host-Builds erfolgreich | +| Lint | Keine Fehler; Bibliothek 18, Studio API 6 bestehende Warnungen | + +Alle 20 neuen Leerzeichenfälle waren vor dem Fix rot und danach grün. +Die vollständigen Host-Suiten wurden für diesen Fix nicht erneut ausgeführt. +Referenz: ursprünglicher Studio-Commit `2504265721a59c7c65425072951ce82ab16f0fb8`. +Die Laufzeitänderungen wurden mit Bibliothek `aa404bc2`, Studio `b12fd461` +und Kodierbox `92b88f30` geprüft; spätere Ergänzungen betreffen Tests, synthetische +Abnahmedokumente und Dokumentation. + +Paket: identischer lokal gepackter 4.1.0-Tarball in beiden Anwendungen. +SHA256: `dcee6cc39436baf91cf2558afedfbc202e044ca14dbf72c7bfdf4b381e5c0ccd`. +Alle installierten Paketdateien wurden mit dem Tarball verglichen; beide +Root-Locks und Studios API-Runtime-Lock enthalten dessen neue Integrität. + +## Echte Browserabläufe + +Beide gebauten Frontends wurden über einen lokalen Static-/API-Proxy mit ihren +unveränderten produktiven NestJS-Bundles ausgeführt. Die PostgreSQL-Datenbanken +wurden mit den vollständigen Repository-Migrationen frisch angelegt. Studio +verwendete Liquibase 4.33, Kodierbox die vorgesehene Version 4.28. Kodierbox nutzte +echtes Redis/Bull und einen isolierten Keycloak mit zwei synthetischen Identitäten. +Die Fixture-Einrichtung erfolgte über echte APIs; Kodierbox-VOCS und die +Jobdefinition wurden als erfundene Datensätze direkt in die Testdatenbank gesetzt. +Die erfolgreichen Exportabläufe enthielten keine API-/Auth-/Queue-Mocks. + +- **Studio:** JSON-/DOCX-Download, leere Auswahl gesperrt, Suche erhält Gesamtauswahl, + Dialog schließt nach Download, anonymer Zugriff 401, fremder Workspace 403, + Missing-Code 0 über reales Profil, Score-Abwahl und Schulungsfilter geprüft. +- **Kodierbox:** JSON-/DOCX über echte Jobanlage, Statusabfrage und Download; + Jobdefinitionspicker beschränkt auf A/ALIAS_00, ausgewähltes Missingprofil und + Score-Abwahl stimmen mit dem Download überein. Doppelauslösung ist gesperrt; + erneuter Versuch und Ende des Pollings beim Schließen funktionieren. +- **Fehlerzweige:** Studio-Exportfehler sowie Kodierbox-Anlage-, Status- und + Downloadfehler wurden gezielt im Browsernetzwerk injiziert. Meldung und + Freigabe für erneuten Versuch wurden geprüft; erfolgreicher Retry lief wieder + gegen die echte Queue. +- **Reale Zugriffsprüfungen:** Kodierbox weist anonyme und nicht zugewiesene Nutzer + ab (bestehender WorkspaceGuard: 401), fremde Job-Workspaces mit 403, fremde + ausgewählte Einheiten mit 404 und ungültiges Format/Boolean mit 400. Ein + erfolgreicher Download räumt sein Ergebnis auf; Folgeabruf liefert 404. +- Beide erfolgreichen Browser-Downloads sind in JSON und allen normalisierten + DOCX-Bestandteilen identisch. NBSP, schmales NBSP und doppelte Abstände bleiben + erhalten. Keine unbehandelten Browser-Ausnahmen in den bestandenen Abläufen. + +Testumgebung: PostgreSQL 16.11, Redis 7.4, Keycloak 26.7.3. Das ist keine +Produktions- oder Deployment-Abnahme; Kodierboxs PostgreSQL-14-Produktionsversion +wurde damit nicht separat geprüft. Der vorhandene Keycloak-JS-Adapter 23.0.7 +benötigte im Testrealm den dokumentierten Nonce-Kompatibilitätsmapper +([Keycloak Upgrade Guide](https://www.keycloak.org/docs/26.7.5/upgrading/index.html)). +Alle isolierten Testdienste wurden anschließend beendet und entfernt. + +## Microsoft Office + +Die tatsächlichen DOCX-Dateien wurden von **Microsoft Office Web Viewer / Word** +gerendert, ohne Office-Anmeldung. Es wurden ausschließlich erfundene, im +Bibliotheksbranch versionierte Testdokumente verwendet. + +| Dokument | Seiten | +| --- | ---: | +| Aktuelles Rich-Text-Dokument mit Scores | 3 | +| Ursprüngliches Studio mit Scores | 3 | +| Aktuelles Rich-Text-Dokument ohne Scores | 3 | +| Ursprüngliches Studio ohne Scores | 3 | +| Tatsächlicher Studio-Browser-Download | 3 | +| Tatsächlicher Kodierbox-Browser-Download | 3 | +| 48 Markierungskombinationen plus Formel/Listen/Bild | 2 | +| **Gesamt** | **20** | + +Tabellenspalten, Umbrüche, Bilder, Formeln, Markierungen und Fußzeilen/Seitenzahlen +wurden anhand der gerenderten Seiten geprüft. Alle sechs Seiteninhalte der +aktuellen/ursprünglichen Studio-Paare sind pixelgleich (Seitenbereich oberhalb +der Fußzeile; die vereinbarte Umbenennung zu „IQB Codebook“ wird separat geprüft). +Die beiden echten Browser-Dokumente sind auf allen drei vollständigen Seiten +pixelgleich. Es traten keine sichtbaren abgeschnittenen Inhalte oder defekten +Formeln/Bilder auf. Dies ist ein echter Microsoft-Word-Web-Nachweis; Windows- +oder macOS-Desktop-Word wurde nicht ausgeführt. + +## Aspect-Abgleich und Grenzen + +Quellabgleich mit `iqb-berlin/verona-modules-aspect@fb3f5982`: Die 384 Tests +umfassen alle Kombinationen der gemeinsamen vier Textmarkierungen, drei gültigen +Grundlinienzustände, zwei Verschachtelungsreihenfolgen und vier Ausrichtungen. +Der Codebook-Vertrag liest Kodierinstruktionen aus VOCS, nicht vollständige +Aspect-Aufgaben mit interaktiven Player-Elementen. Aspect-Formelknoten verwenden +einen anderen Vertrag als die IQB-Formeln des Instruktionseditors. Ein kompletter +Aspect-Editor-/Player-Browserlauf wurde nicht durchgeführt. + +Die bestehenden Studio-Grenzen bei CSS-Listenstilen, verschachtelten +Listeneinrückungen, Bild-Floats sowie innerhalb eines Absatzes wechselnder Farbe/ +Schriftgröße bleiben dokumentiert. Eine uneingeschränkte HTML-/CSS-Layouttreue +oder Unterstützung aller Aspect-Player-Knoten wird nicht behauptet. + +## Veröffentlichung und CI + +Alle drei Branches `shared-codebook` wurden committed und gepusht. Die offenen +PRs #239 (Bibliothek) und #1041 (Kodierbox) werden mit diesen Ergebnissen aktualisiert. +Studio-PR #1728 ist ohne Merge geschlossen; der Studio-Branch wurde aktualisiert. +Bibliotheks-CI war für `2925485` vollständig grün. Die CI der abschließenden +Dokumentationscommits wird separat angegeben. Kodierboxs zuletzt geprüfte +GitLab-Pipeline [103668](https://scm.cms.hu-berlin.de/iqb/coding-box/-/pipelines/103668) +für `92b88f30` ist fehlgeschlagen; die Jobursache bleibt hinter Bot-Schutz/ +Anmeldung unbestätigt. Lokale grüne Tests ersetzen diesen offenen CI-Status nicht. +Kein Merge, Release, Paket-Publish oder Rollout wurde ausgeführt. diff --git a/docs/codebook-aspect-acceptance-2026-10-13.md b/docs/codebook-aspect-acceptance-2026-10-13.md new file mode 100644 index 0000000..3b6ff65 --- /dev/null +++ b/docs/codebook-aspect-acceptance-2026-10-13.md @@ -0,0 +1,82 @@ +# Browser- und Word-Abnahme der Codebooks + +Die bestätigten Formatierungsfehler sind behoben. Der echte Aspect Editor und Player, der gemeinsame Instruktionseditor sowie Studio und Kodierbox wurden mit synthetischen Daten geprüft. Microsoft Office Web Viewer / Word hat 262 Seiten gerendert; die Aufnahmen jeder Seite sind mit deren tatsächlicher Fußzeile abgeglichen. + +## Umfang und Ergebnisse + +| Prüfung | Ergebnis | +| --- | --- | +| Aspect: HTML laden, Unit über die Oberfläche speichern, echter Verona-Player | 2.740 Formatfälle, 40 native Formeln | +| Gemeinsamer Editor: HTML laden, speichern und wiederöffnen | 2.738 unterstützte Formatfälle | +| Vollständige gültige Textmarkierungsmatrix je Editor | 2.496 = 16 Kombinationen aus fett/kursiv/unterstrichen/durchgestrichen × normal/tief/hoch × vier Ausrichtungen × 13 Schriftgrößen | +| Weitere Matrixfälle | 16 Farbgrenzen, neun Listenstile auf fünf Ebenen, 120 Absatzkombinationen, 16 Überschriften, Bildpositionen, Formeln, Sonderzeichen, Umbrüche und Zitate; Aspect zusätzlich Inlinebild und Trennlinien | +| Tatsächliche Toolbar-Bedienung | 57 Aspect- und 52 gemeinsame Editorfälle; jeweils alle 38 Sonderzeichen-Schaltflächen; gemeinsamer Formeldialog zusätzlich separat geprüft | +| HTML-Austausch in beiden Richtungen | Je 2.738 Fälle und 40 Formeln erhalten; Speichern/Wiederöffnen und echter Player erfolgreich | +| Austausch bis Word | In beiden Richtungen **alle normalisierten DOCX-Bestandteile identisch** zur tatsächlich in Word gerenderten Matrix | +| Generator | 1.245 Tests bestanden, keine übersprungen; ESM und CommonJS, mit und ohne Scores | +| Editor-Regressionsprüfungen | Gemeinsamer Editor 10 Tests; Aspect 16 Tests bestanden | +| Installationsvergleich | 4.654 Studio-/Kodierbox-Exportpaare: JSON und alle normalisierten DOCX-Bestandteile identisch | +| Host-Tests | Studio 49 API + 66 Frontend; Kodierbox 58 Backend + 60 Frontend bestanden | +| Builds | Bibliothek, frischer Aspect Editor/Player und alle vier Host-Builds erfolgreich | +| Installation | Identischer 4.1.0-Tarball in beiden Hosts; zusätzlich saubere Studio-Runtime-Installation mit 515 Paketen; alle 20 Paketdateien in allen drei Installationen bytegleich | + +Die große Matrix läuft über die tatsächlichen Tiptap-Parser und Serialisierungen. Die Bedienfälle prüfen zusätzlich echte Schaltflächen, Auswahllisten, Bilddateiauswahl und Speicherdialoge. Der gemeinsame MathLive-Dialog wird über seinen öffentlichen Eingabewert und das reale Input-Ereignis bedient. Die erfolgreichen Editor-, Player-, Authentifizierungs-, API- und Queue-Abläufe verwenden ihre echten Implementierungen. + +## Behobene Fehler + +- Normale Mehrfachabstände wurden beim Laden und Wiederöffnen reduziert. Normale, geschützte und schmale geschützte Leerzeichen bleiben erhalten; JSON-Inhalte und ausdrücklich gesetzte Parseroptionen funktionieren weiter. +- Farben, Schriftgrößen und Hervorhebungen wurden auf den ganzen Absatz ausgedehnt. Sie gelten jetzt nur für die jeweilige markierte Textstelle. CSS-Pixel und Punktangaben werden korrekt in Word-Halbpunkte umgerechnet. +- Absatzabstände, Einzüge, hängende Einzüge und formatierte Listen gingen im Word-Export verloren. Die gesetzten Attribute und Listenmarker bleiben erhalten. +- Einrückung und hängender Einzug funktionierten nach „Alles auswählen“ nicht; im gemeinsamen Editor scheiterte Absatzabstand an Textknoten. Beide Bedienpfade sind repariert. +- Inlinebilder wurden aus dem Text herausgezogen oder doppelt ausgegeben; gesetzte linke/rechte Bildpositionen gingen verloren. Word erhält die Platzierung einschließlich Textumlauf-Ankern. +- Aspect-Formeln wurden aus veraltetem MathLive-Anzeige-HTML gelesen; reine Formelinstruktionen konnten herausgefiltert werden. Beide Formel-HTML-Formate werden aus ihrem LaTeX verarbeitet, und der Instruktionsfilter erkennt reine Aspect-Formeln. +- Die Editoren erkannten beim HTML-Austausch jeweils nur ihren eigenen Formelknoten. Beide lesen nun beide Formate und speichern im eigenen kanonischen Format weiter. +- Über- und Unterstriche wurden als kurzer Akzent und teils an der falschen Position dargestellt. Native Word-Balken umfassen jetzt den ganzen Ausdruck. Bruchstriche, Wurzelzeichen und große Operatoren erhalten dieselbe Farbe und Größe wie die Formelzeichen; der Vorher-/Nachher-Nachweis wurde in Word tatsächlich geprüft. +- Schriftgröße, Farbe und Textmarkierungen um Formeln gingen im nativen Word-Mathematikformat verloren. Die umgebenden Eigenschaften werden auf native Mathematikläufe übertragen; der Formelcache bleibt unverändert und überträgt keine fremde Formatierung. +- Formelabsätze wurden immer links ausgerichtet; die gewählte Ausrichtung bleibt erhalten. Zeilenumbrüche innerhalb von LaTeX-Attributen werden nicht mehr in HTML-Break-Tags umgewandelt. +- Aspect erlaubte keine erneute Auswahl derselben Bilddatei. Das Dateifeld wird nach dem Auslesen geleert; die vier Bildpositionen wurden mit derselben Datei tatsächlich eingefügt. + +## Erhalt der bisherigen Studio-Ausgabe + +Die unabhängigen Referenzen stammen weiterhin vom ursprünglichen Studio-Commit `2504265721a59c7c65425072951ce82ab16f0fb8`. Die 32 bestehenden Rohschema-Referenzen und 20 Leerzeichenfälle wurden nicht aus der neuen Implementierung neu erzeugt. + +Grundlayout, Auswahlregeln, Scores, Missing-Codes, Beziehungen, Bilder und unveränderte Formatfälle bleiben gegen die alten vollständigen JSON-/DOCX-Referenzen abgesichert. Eine **bewusste Korrektur** betrifft den alten `Farbe`-Referenzabsatz: explizite `18pt` wurden früher als 18 Word-Halbpunkte, also 9pt, behandelt; nun werden korrekt 36 Halbpunkte ausgegeben. Der Vergleich normalisiert ausschließlich diesen streng geprüften Unterschied und die gleichwertige Hex-Schreibweise der Hervorhebung zurück. Alle anderen Dokumentbestandteile bleiben Gegenstand des ursprünglichen Hashvergleichs. Neue Tests prüfen die korrigierten Werte unmittelbar. + +Die frühere pixelgleiche Abnahme des Pakets `dcee6cc…` ist ein historischer Nachweis. Für das jetzige Paket gelten die oben beschriebenen gezielten Darstellungsfixes und die neue Word-Abnahme; eine pauschale Pixelgleichheit zu allen alten Fehlformatierungen wird nicht behauptet. + +## Microsoft Word + +| Dokument | Seiten | +| --- | ---: | +| Aspect: vollständige Formatmatrix | 114 | +| Gemeinsamer Editor: vollständige Formatmatrix | 114 | +| Aspect: Übersicht aller unterschiedlichen Formatmerkmale | 12 | +| Gemeinsamer Editor: Formatübersicht | 12 | +| Tatsächlich bediente Toolbarfälle und 13 kombinierte Formelschriftgrößen | 6 | +| Aktuelle Studio-Referenz mit korrigierter expliziter Schriftgröße | 3 | +| Farbige zusammengesetzte Formeln, breite Über-/Unterstriche | 1 | +| **Gesamt** | **262** | + +Alle Seiten wurden aus dem echten Microsoft-Viewer erfasst. Dessen wiederverwendete Bildkacheln wurden anhand ihrer Seitenposition erfasst; ein zusätzlicher OCR-Abgleich bestätigt die erwartete Seitennummer jeder Aufnahme. Die Formatübersichten und repräsentative Matrixseiten wurden visuell geprüft. Die vollständigen Matrixinhalte, Markierungen, Größen und Ausrichtungen sind zusätzlich im DOCX geprüft. Beide Richtungen des Editor-Austauschs ergeben exakt dieselben Word-Dokumentbestandteile wie die gerenderte gemeinsame Matrix. + +Die Galerie enthält sämtliche Seiten in Originalauflösung. Verwendet wurden ausschließlich erfundene, versionierte Abnahmedokumente; kein Office-Konto und keine Nutzerdaten waren nötig. Dies ist eine Microsoft-Word-Web-Abnahme. Desktop-Word unter Windows oder macOS wurde nicht ausgeführt. + +## Echte Host-Downloads + +Studio und Kodierbox exportieren drei synthetische Aufgaben mit insgesamt 5.479 Code-Instruktionen: beide vollständigen Editor-Matrizen und eine kombinierte formatierte Aspect-Formel. Beide gebauten Frontends laufen mit den produktiven NestJS-Bundles. Studio verwendet eine frisch migrierte PostgreSQL-Datenbank; Kodierbox zusätzlich echtes Redis/Bull und Keycloak. + +Studio prüft JSON/DOCX, leere Auswahl, Auswahl während der Suche, Dialogabschluss, 401/403 sowie sichtbare Fehler und erneuten Versuch. Kodierbox prüft echte Jobanlage, Status und Download, doppelte Auslösung, Anlage-/Status-/Downloadfehler, erneuten Versuch und das Ende des Pollings beim Schließen. Ausschließlich Fehlerzweige werden gezielt im Browsernetzwerk injiziert. Erfolgreiche Downloads sind in JSON und jedem normalisierten DOCX-Bestandteil identisch; geschützte und wiederholte Leerzeichen bleiben erhalten. + +Testdienste: PostgreSQL 16.11 mit Repository-Migrationen, Studio Liquibase 4.33 und Kodierbox 4.28, Redis 7.4 und Keycloak 26.7.3. Die vorhandene Keycloak-JS-Version 23.0.7 benötigt im isolierten Testrealm den Nonce-Kompatibilitätsmapper. Die Prüfung ist lokal; sie bestätigt keine Produktion oder Bereitstellung. Die vollständigen Host-Testsuiten wurden nicht erneut ausgeführt. + +Der Dokumentvergleich normalisiert ausschließlich die Datumsausgabe in der Fußzeile, unabhängig vom Ausführungstag. Ein eigener Test stellt sicher, dass Datumsangaben und Markenbezeichnungen im eigentlichen Nutzerinhalt erhalten bleiben. Zusätzlich sind explizite Listenstarts bei 0 und 7 abgesichert. + +## Paket und Abgrenzung + +Paket-SHA256: `0e355c3896799ae765cb6e90b00147da0359c56f5fdc90d36b7caf40fbe59ab8`. + +Die Abnahme betrifft Rich Text in Textinhalten und manuellen Kodieranweisungen. Interaktive Antwortfelder sind eigenständige Player-Elemente. Farben wurden über repräsentative Hex-/RGB-/Namenswerte, Bildpositionen mit synthetischen PNG-Dateien geprüft. Formatierte Listen erhalten im statischen Word-Codebook explizite Marker; sie werden dort nicht automatisch neu nummeriert. + +Lokale Tests, Browser-/Word-Abnahme, Push und entfernte CI sind getrennte Nachweise. Der aktuelle CI-Stand wird bei den Pull Requests dokumentiert; aus lokalen Erfolgen wird keine grüne GitLab-Pipeline abgeleitet. + +Versionierte synthetische Word-Dateien: `scripts/fixtures/word-acceptance/` im coding-components-Repository. Reproduzierbare Generator- und Formatprüfungen: `npm run build_cc && npm run test:codebook`; Host-Vergleich: `scripts/compare-codebook-installations.mjs`. Die vollständige lokale Galerie und maschinenlesbaren Ausführungsnachweise ergänzen diese Zusammenfassung. diff --git a/docs/shared-codebook.md b/docs/shared-codebook.md new file mode 100644 index 0000000..ce6b390 --- /dev/null +++ b/docs/shared-codebook.md @@ -0,0 +1,173 @@ +# Gemeinsames Codebook: Integrationsvertrag + +## Aktuelle Format- und Aspect-Abnahme + +Die [Browser- und Word-Abnahme vom 13.10.2026](codebook-aspect-acceptance-2026-10-13.md) ergänzt die frühere Leerzeichen-Abnahme um bestätigte Formatierungsfixes und den bidirektionalen Formel-Austausch. Aktuelles Paket-SHA256: `0e355c3896799ae765cb6e90b00147da0359c56f5fdc90d36b7caf40fbe59ab8`. 1.245 Generator-Tests, 4.654 installierte Exportpaare und 262 tatsächliche Microsoft-Word-Web-Seiten sind geprüft. Die explizite 18pt-Schriftgröße des alten `Farbe`-Referenzfalls wird bewusst korrigiert; alle übrigen alten Dokumentbestandteile bleiben streng verglichen. Angaben zu früheren Paketständen weiter unten sind historische Nachweise. + +Studio ist die Referenz für Bedienung und Dokumentlayout. Die drei Einstiegspunkte +`codebook-models`, `codebook-generator` und `codebook-export` trennen Daten, +frameworkfreie Dokumenterstellung und Angular-Oberfläche. Die Bibliothek enthält +keinen Jobvertrag, keine HTTP-Aufrufe und keinen Download-Dienst. + +## Anwendungen + +Studio lädt Aufgaben und Missingprofile, übergibt die aktive Aufgabe und seine +Speichersperre und verarbeitet `exportRequested` mit seinem direkten API-Aufruf. +Schulungsbedarf ist eine gemeinsame Auswahl und wird in beiden Anwendungen durch +denselben Generator anhand von CODER_TRAINING_REQUIRED gefiltert. +Die Kodierbox ergänzt ihren Jobdefinitionsfilter über `[codebookFilters]` und zeigt ihre +Jobzustände über `[codebookStatus]`. Das sind Projektionsflächen ohne Jobsemantik. +`loading` und `busy` sind allgemeine Sperrzustände. Nur die Kodierbox startet Jobs, +fragt Status ab und lädt Ergebnisse. Beim Schließen endet die Statusabfrage; +der Serverjob wird nicht abgebrochen. + +Die Übersetzungen der Oberfläche stehen im Host unter `codebook.*`. Beide Hosts +verwenden dieselben Studio-Beschriftungen und Tooltips. `selectionChanged`, +`exportRequested` und `cancel` sind die einzigen Ausgaben. Der Exportwunsch enthält +Aufgaben-IDs, fachliche Optionen und Missingprofil-ID. Die Profile tragen eine +numerische ID und eine Beschriftung; 0 bedeutet kein Profil. +`showGroupColumn` ist im Studio aktiv, in Kodierbox deaktiviert. +Während `loading` und `busy` ist jede Auswahl gesperrt. Verliert eine Aufgabe ihre +Verfügbarkeit, meldet selectionChanged auch die bereinigte Auswahl zurück. +Variablenbündel bleiben als bestehender Kodierbox-API-Vertrag erhalten, werden +aber im Codebook-Dialog nicht mehr angeboten (Issue #176). + +## Bewusste fachliche Korrekturen + +* Nur manuell: Variablen mit regulären manuell beschriebenen Codes; nur diese Codes. +* Nur geschlossen: Variablen mit RESIDUAL_AUTO oder INTENDED_INCOMPLETE; alle Codes. +* Beide: Vereinigung dieser Variablengruppen; alle Codes. +* Beide aus: keine Einschränkung nach dieser Kategorie. +* Nur mit Codes wird nach der Codeauswahl ausgewertet. +* Leere HTML-Strukturen, unsichtbare Zeichen und Leerzeichen sind keine Instruktion; + unterstützte Bilder und Formeln sind eine Instruktion. Allgemeine Instruktionen + allein machen keinen Code manuell. +* Code 0 und Missing 0 bleiben erhalten. Missing-Codes behalten ihren JSON-Datentyp + aus dem Profil (Zahl oder Zeichenkette); Kodierbox wandelt Zahlen nicht mehr in + Zeichenketten um. Leere Exporte liefern `[]` bzw. ein gültiges + DOCX. Fehlerhafte Schemen melden die betroffene Aufgabe. +* Aufgaben werden nach Schlüssel, Variablen nach Alias/ID sortiert; Codefolge bleibt. +* Schulungsbedarf: alle, nur mit oder nur ohne CODER_TRAINING_REQUIRED; kombiniert + mit der gemeinsamen Variablen-/Codeauswahl. +* BASE_NO_VALUE bleibt ausgeschlossen; abgeleitete Variablen folgen der Option. +* Itembeziehungen sind explizite `{id, variableId}`-Paare. Der Anwendungsadapter + normalisiert Quell-IDs auf die im Dokument verwendeten Aliase. +* Die Kennzeichnung lautet IQB Codebook. Studio-Formatierung, Tabellen, Seitenfelder, + eingebettete Bilder und Formelkonvertierung bleiben erhalten. Klartextinstruktionen + und Leerzeichen an Formatgrenzen werden nicht mehr verschluckt. Verschachtelte + Textmarkierungen bleiben kombiniert erhalten. Direkte Listentexte und Klartext + vor, zwischen und nach HTML-Absätzen werden in Quellreihenfolge übernommen; + bestehende Absatzinhalte, Bilder und Formeln erscheinen weiterhin genau einmal. + +## Paket und Laufzeit + +`npm run build_cc` erzeugt Angular-ESM sowie einen zusätzlichen CommonJS-Einstieg +für Server. Der Generator braucht weder Angular noch DOM/Browser-Globals. +Der CommonJS-Build integriert den ausschließlich als ESM gelieferten mathml2omml- +Konverter. Dessen Quellcode bleibt über Source Maps und das npm-Original verfügbar. + +Die Regeltextkonvertierung ist intern auf @iqb/responses 5.2.2 festgelegt. +Das ist ausdrücklich kein Upgrade der Kodierbox-Kodierengine: deren responses +5.2.0 und response-Spezifikation 1.5.1 bleiben unverändert. Die Bibliothek akzeptiert +response-Spezifikation 1.5.1 sowie 2.x als Peer. + +## Lokale Integration und Veröffentlichung + +Die beiden Integrationen referenzieren dieselbe gepackte Datei unter +`vendor/iqb-ngx-coding-components-4.1.0.tgz`. Das ist ein lokal prüfbarer Release- +Kandidat, noch keine veröffentlichte npm-Version. Zum Release wird genau der +abgenommene Bibliotheksstand veröffentlicht; danach werden die Host-Abhängigkeiten +auf die Registry-Version umgestellt und Locks neu geprüft. Die lokalen Tarballs +können dann entfernt werden. Beide Anwendungen gemeinsam abnehmen und ausrollen. +Rollback: vorherige Bibliotheks-/Anwendungsversion; keine Datenmigration. + +## Prüfungen + +* `npm run test:codebook`: Node-Vertragstests für alle Filterkombinationen, Daten, + JSON, DOCX-Struktur, Bilder, Formeln, Itembeziehungen und CJS/ESM-Gleichheit. +* `npm run test:cc`: Angular-Tests einschließlich Auswahl/Suche/Sperrzustände. +* Beide Hosts testen ihre echten Adapter und bestehenden Formelregressionen. +* Die künstlichen Fälle in `scripts/fixtures/codebook-synthetic.mjs` enthalten keine + Aufgabeninhalte aus den Beispiel-ZIPs. Die Matrix durchläuft alle 256 Kombinationen + der acht Ja/Nein-Optionen, drei Schulungsfilter, drei Missing-Konstellationen und + beide Formate: 4.608 Exporte. Alle neun Quelltypen werden in der Datenauswahl geprüft. +* 32 zusätzliche Referenzfälle vergleichen Rohschema → JSON und sämtliche relevanten + DOCX-Bestandteile mit dem ursprünglichen Studio. Die Referenz wird ausschließlich + aus dem dokumentierten alten Studio-Commit erzeugt, niemals aus dem neuen Generator. +* Weitere 20 Studio-Referenzfälle erhalten NBSP, schmale geschützte Leerzeichen und + wiederholte Abstände in allgemeinen und Code-Instruktionen, jeweils mit und ohne + Score-Spalte. HTML-Zeichenreferenzen, Unicode und fett formatierter Text werden + über ESM und CommonJS auf exakte Inhalte und alle DOCX-Bestandteile geprüft. +* `node scripts/compare-codebook-installations.mjs STUDIO_ROOT BOX_ROOT OUTPUT_DIR` + prüft dieselbe Matrix über Studios DownloadWorkspacesClass und Kodierboxs echten + Generation-Service samt Adapter mit Datenquellen im jeweiligen Anwendungsformat. + Nur Datenbank-/Profilzugriffe werden im Speicher bereitgestellt. Der Vergleich + schließt Bilder, Beziehungen, Styles, Fußzeilen und Missing-Datentypen ein. + Zusätzlich laufen die 20 Abstandsreferenzen in beiden Formaten gegen das alte + Studio; zusammen werden 4.654 Exportpaare verglichen. + Das ist ausdrücklich kein HTTP-, Browser-, Berechtigungs- oder Redis-End-to-End-Test. +* Studio-Frontend prüft direkten Export, Ladezustände, Fehlermeldung und Speichersperre. +* Kodierbox prüft Scope-Filter, wiederholte Starts, langsame Statusabfragen und Schließen. +* Frontend- und Backend-Prüfungen auf dem Entwicklungsrechner nacheinander ausführen. +* Vor Ausrollen bleiben visuelle Mehrseiten-DOCX-Abnahme und Host-Browserabläufe + einschließlich Netzwerkprüfung verpflichtend. + +### Microsoft Office + +Microsoft Office Web Viewer / Word hat am 13.10.2026 sieben ausschließlich +synthetische Dokumente mit insgesamt 20 Seiten gerendert. Die Host-Browser- +Downloads sind auf allen drei vollständigen Seiten pixelgleich. Die alten und +neuen Studio-Rich-Text-Dokumente sind mit/ohne Scores in ihren sechs +Seiteninhalten pixelgleich; die vereinbarte Fußzeile wird separat geprüft. +Details und Grenzen: [Abnahmeprotokoll](./codebook-acceptance-2026-10-13.md). + +XML-Gleichheit und LibreOffice-Rendering allein ersetzen keinen Office-Nachweis. +Für einen zusätzlichen Windows-Desktop-Word-Abnahmeweg legt der +Installationsvergleich künstliche Exporte +sechs DOCX-Dateien und `word-expectations.json` im Ausgabeverzeichnis ab: je Anwendung +einen Filter-/Missing-0-Fall sowie Rich-Text-Fälle mit und ohne Score-Spalte. Die +Rich-Text-Fälle werden zusätzlich direkt gegen die alte Studio-Referenz geprüft. +Auf einem Windows-Rechner mit Desktop-Word: + +```powershell +./scripts/verify-codebook-word-windows.ps1 -Directory C:\Codebook-Pruefung +``` + +Das Skript öffnet alle Dateien regulär und schreibgeschützt, aktualisiert Felder, +prüft Textgleichheit, Seitenzahl, Tabellen, ungeteilte Zeilen, Bilder, Formeln und +Fußzeilen und exportiert sechs PDFs. Es speichert Word-Version und Build im Ergebnis. +Anschließend alle gerenderten Dokumente vollständig visuell prüfen: insbesondere +Spaltenbreiten, Umbrüche, Formeln, Bilder, Schriften und „Seite X von Y“. +`visualAcceptanceComplete` bleibt bis zur tatsächlichen visuellen Abnahme false. +Eine nicht ausgeführte Windows-Prüfung darf nicht als bestanden gemeldet werden. + +### Editor-/Player-Abgleich und Formatkombinationen + +Die Instruktionen des coding-components-Editors sind der Eingabevertrag des +Codebook-Exports. Die Extension-Liste wurde mit Aspect Editor/Player bei +`iqb-berlin/verona-modules-aspect@fb3f5982` abgeglichen. Der Export liest +Kodierschemata (`manualInstruction`), nicht die vollständige Aspect-Aufgabe mit +interaktiven Player-Elementen. + +`codebook-editor-matrix.test.mjs` prüft alle 384 Kombinationen aus vier +Textmarkierungen (Fett, Kursiv, Unterstrichen, Durchgestrichen), drei gültigen +Grundlinienzuständen, zwei Verschachtelungsreihenfolgen und vier Ausrichtungen. +Jeder Fall prüft allgemeine und Code-Instruktion, geschützte und wiederholte +Leerzeichen, Formatierungsgrenzen sowie identische ESM-/CommonJS-Dokumentteile. +Diese Matrix ergänzt die 52 unabhängigen Alt-Studio-Referenzfälle. + +`node scripts/create-codebook-editor-office-fixture.mjs` erzeugt ein ausschließlich +synthetisches Dokument mit allen 48 Markierungskombinationen sowie gemischten +Formel-, Listen- und Bildfällen zur visuellen Office-Abnahme. + +Grenzen des bestehenden Studio-Renderers bleiben sichtbar: CSS-Listenstile, +verschachtelte Listeneinrückungen, Bild-Floats und pro Textlauf wechselnde +Farbe/Schriftgröße werden nicht vollständig wie im Browser abgebildet. Der +Abgleich behauptet deshalb keine vollständige HTML-/CSS-Layouttreue. +Aspect-spezifische Formelknoten (`aspect-nodeview-math-formula[formula]`) und +interaktive Cloze-Knoten sind ein anderer Vertrag als +`span.iqb-math-formula[data-latex]` bzw. `[[iqb-math:…]]` in Kodierinstruktionen. +Der Aspect-Player rendert seine gespeicherten Formeln über +`projects/common/utils/math-formula-markup.ts`; der Codebook-Export nutzt OMML. +Ein vollständiger Aspect-Editor-/Player-Lauf ist damit nicht durch diese +Codebook-Matrix ersetzt. diff --git a/package-lock.json b/package-lock.json index 47d4fc1..3aa994a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,13 +65,19 @@ "@tiptap/extension-underline": "^2.27.2", "@tiptap/pm": "^2.27.2", "@types/jasmine": "~4.3.0", + "@types/katex": "^0.16.8", "@types/node": "^20.4.4", "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "^7.18.0", + "buffer": "^6.0.3", + "cheerio": "^1.1.2", + "docx": "^8.5.0", + "esbuild": "0.28.1", "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-typescript": "^18.0.0", "eslint-plugin-import": "^2.32.0", + "image-size": "^1.2.1", "iqb-dev-components": "^1.4.1", "jasmine-core": "~4.6.0", "karma": "~6.4.0", @@ -79,6 +85,7 @@ "karma-coverage": "~2.2.0", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.1.0", + "mathml2omml": "^0.5.0", "ng-packagr": "^21.2.7", "ngx-build-plus": "^20.0.0", "ngx-tiptap": "^12.0.0", @@ -4605,9 +4612,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4761,9 +4765,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4781,9 +4782,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -4801,9 +4799,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4821,9 +4816,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4841,9 +4833,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4861,9 +4850,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4881,9 +4867,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5464,9 +5447,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5485,9 +5465,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5506,9 +5483,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5527,9 +5501,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5548,9 +5519,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5569,9 +5537,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5590,9 +5555,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5611,9 +5573,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5835,9 +5794,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5859,9 +5815,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5883,9 +5836,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5907,9 +5857,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5931,9 +5878,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5955,9 +5899,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6347,9 +6288,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6367,9 +6305,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6387,9 +6322,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6407,9 +6339,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6630,9 +6559,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6647,9 +6573,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6664,9 +6587,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6681,9 +6601,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6698,9 +6615,6 @@ "loong64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6715,9 +6629,6 @@ "loong64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6732,9 +6643,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6749,9 +6657,6 @@ "ppc64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6766,9 +6671,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6783,9 +6685,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6800,9 +6699,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6817,9 +6713,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6834,9 +6727,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -7849,6 +7739,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/katex": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/linkify-it": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", @@ -8955,6 +8852,27 @@ "dev": true, "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", @@ -9143,6 +9061,31 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -9380,6 +9323,106 @@ "dev": true, "license": "MIT" }, + "node_modules/cheerio": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.1.2.tgz", + "integrity": "sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.1", + "htmlparser2": "^10.0.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^7.12.0", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=20.18.1" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cheerio-select/node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cheerio-select/node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cheerio/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/cheerio/node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/chokidar": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", @@ -10376,6 +10419,42 @@ "node": ">=6.0.0" } }, + "node_modules/docx": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/docx/-/docx-8.5.0.tgz", + "integrity": "sha512-4SbcbedPXTciySXiSnNNLuJXpvxFe5nqivbiEHXyL8P/w0wx2uW7YXNjnYgjW0e2e6vy+L/tMISU/oAiXCl57Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "^20.3.1", + "jszip": "^3.10.1", + "nanoid": "^5.0.4", + "xml": "^1.0.1", + "xml-js": "^1.6.8" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/docx/node_modules/nanoid": { + "version": "5.1.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.16.tgz", + "integrity": "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, "node_modules/dom-serialize": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", @@ -10504,6 +10583,33 @@ "node": ">= 0.8" } }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/encoding-sniffer/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/engine.io": { "version": "6.6.10", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.10.tgz", @@ -12519,6 +12625,27 @@ "postcss": "^8.1.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -12581,6 +12708,29 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/image-size": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", + "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", + "dev": true, + "license": "MIT", + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true, + "license": "MIT" + }, "node_modules/immutable": { "version": "5.1.9", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", @@ -13599,6 +13749,59 @@ ], "license": "MIT" }, + "node_modules/jszip": { + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.2.tgz", + "integrity": "sha512-3l+rb15IOWtUhU0H5MFqES/T6Kh7abYwjosBey/vD6hDt8zoEffkSC5Ws5SGtgVw3gBx2NEbhTeSW1+kWkpyTQ==", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/karma": { "version": "6.4.4", "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz", @@ -14246,6 +14449,16 @@ } } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -14743,6 +14956,13 @@ "node": ">= 18" } }, + "node_modules/mathml2omml": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/mathml2omml/-/mathml2omml-0.5.0.tgz", + "integrity": "sha512-4eLs37a+TH+CL/M5XZZrlc75SNRJNPiZzIaeSSvH0UFCRaCdSYRHWvrlA7MUbeQks/z4sVhfg+GlcZhVVUHzqg==", + "dev": true, + "license": "LGPL-3.0-or-later" + }, "node_modules/mdurl": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.1.0.tgz", @@ -16067,6 +16287,13 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -16156,6 +16383,85 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parse5-parser-stream/node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/parse5-sax-parser": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-8.0.0.tgz", @@ -16980,6 +17286,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "~2.0.3" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -17711,7 +18027,6 @@ "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", "dev": true, "license": "BlueOak-1.0.0", - "optional": true, "engines": { "node": ">=11.0.0" } @@ -18023,6 +18338,13 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -20397,6 +20719,43 @@ "node": ">=0.8.0" } }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -20612,6 +20971,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", + "dev": true, + "license": "MIT" + }, + "node_modules/xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": "^1.2.4" + }, + "bin": { + "xml-js": "bin/cli.js" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index c41a855..df1e38b 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "build:elements": "ng build --project coding-components -c elements", "serve:elements": "npx http-server dist/coding-components-elements -p 8081", "buildxx": "ng run coding-components:build", - "build_cc": "rm -rf dist/ngx-coding-components && ng build ngx-coding-components", + "build_cc": "rm -rf dist/ngx-coding-components && ng build ngx-coding-components && node scripts/build-codebook-node.mjs", "build": "npm run build_cc && npm run start", "build_sc": "rm -f dist/*.* && rm -rf dist/schemer && ng build --project schemer --output-hashing=none", "pack_sc": "node scripts/wrap_and_pack.js schemer $npm_package_config_schemer_version projects/schemer/src/html_wrapper/index.html", @@ -32,6 +32,7 @@ "test:schemer:coverage": "ng test schemer --watch=false --browsers=ChromeHeadless --code-coverage", "test:coverage": "npm run test:cc:coverage && npm run test:schemer:coverage", "test:scheme": "node --test scripts/validate-coding-scheme.test.js", + "test:codebook": "node --test scripts/codebook-contract.test.mjs scripts/codebook-matrix.test.mjs scripts/codebook-whitespace.test.mjs scripts/codebook-editor-matrix.test.mjs scripts/codebook-rich-layout.test.mjs scripts/codebook-rich-matrix.test.mjs scripts/codebook-docx-test-utils.test.mjs", "test:schemer:responsive": "playwright test --config playwright.schemer.config.cjs" }, "dependencies": { @@ -122,13 +123,18 @@ "@tiptap/extension-underline": "^2.27.2", "@tiptap/pm": "^2.27.2", "@types/jasmine": "~4.3.0", + "@types/katex": "^0.16.8", "@types/node": "^20.4.4", "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "^7.18.0", + "buffer": "^6.0.3", + "cheerio": "^1.1.2", + "docx": "^8.5.0", "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-typescript": "^18.0.0", "eslint-plugin-import": "^2.32.0", + "image-size": "^1.2.1", "iqb-dev-components": "^1.4.1", "jasmine-core": "~4.6.0", "karma": "~6.4.0", @@ -136,11 +142,13 @@ "karma-coverage": "~2.2.0", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.1.0", + "mathml2omml": "^0.5.0", "ng-packagr": "^21.2.7", "ngx-build-plus": "^20.0.0", "ngx-tiptap": "^12.0.0", "ts-node": "^10.9.1", - "typescript": "5.9.3" + "typescript": "5.9.3", + "esbuild": "0.28.1" }, "overrides": { "@babel/core": "7.29.7", diff --git a/projects/ngx-coding-components/codebook-export/codebook-export.component.html b/projects/ngx-coding-components/codebook-export/codebook-export.component.html new file mode 100644 index 0000000..3a9c1bb --- /dev/null +++ b/projects/ngx-coding-components/codebook-export/codebook-export.component.html @@ -0,0 +1,163 @@ +
+

{{ 'codebook.workspace.export-coding-book' | translate }}

+ +
+
+
+ @if (selection.length === 0) {

{{ 'codebook.workspace.select-unit' | translate }}

} + @if (selection.length === 1) {

{{ 'codebook.workspace.unit-selected' | translate }}

} + @if (selection.length > 1) {

{{ selection.length }} {{ 'codebook.workspace.units-selected' | translate }}

} +
+ + {{ 'codebook.search-filter.filter-units' | translate }} + + + + + + + + + + + + + + {{ 'codebook.workspace.unit-key' | translate }} + {{ unit.key }} + + + {{ 'codebook.workspace.unit-name' | translate }} + {{ unit.unitName }} + + + {{ 'codebook.workspace.group' | translate }} + {{ unit.groupName }} + + + + +
+ {{ 'codebook.coding.selected-units' | translate: { count: selection.length } }} +
+
+
+
+ +
+ + {{ 'codebook.coding.training-requirement-filter' | translate }} + + {{ 'codebook.coding.training-requirement-all' | translate }} + {{ 'codebook.coding.training-requirement-required' | translate }} + {{ 'codebook.coding.training-requirement-not-required' | translate }} + + +
+
+

{{'codebook.coding.codebook-content' | translate}}

+
+ + {{'codebook.coding.has-only-vars-with-codes' | translate}} + + + {{'codebook.coding.has-only-manual-coding' | translate}} + + + {{'codebook.coding.has-closed-vars' | translate}} + + + {{'codebook.coding.has-derived-vars' | translate}} + +
+
+ +
+

{{'codebook.coding.codebook-formatting' | translate}}

+
+ + {{'codebook.coding.has-general-instructions' | translate}} + + + {{'codebook.coding.show-score' | translate}} + + + {{'codebook.coding.code-label-to-upper' | translate}} + + + {{'codebook.coding.hide-item-var-relation' | translate}} + +
+
+ +
+

{{ 'codebook.workspace.coding-missing-profiles' | translate }}

+ + — + @for (missingsProfile of missingsProfiles; track missingsProfile.id) { + + {{missingsProfile.label}} + + } + +
+ +
+

{{'codebook.coding.export-format' | translate}}

+ + JSON + DOCX + +
+ + @if(workspaceChanges) { + {{'codebook.coding.error-save-changes' | translate}} + } +
+
+ +
+
+ + + + + + +
diff --git a/projects/ngx-coding-components/codebook-export/codebook-export.component.scss b/projects/ngx-coding-components/codebook-export/codebook-export.component.scss new file mode 100644 index 0000000..95a6ab1 --- /dev/null +++ b/projects/ngx-coding-components/codebook-export/codebook-export.component.scss @@ -0,0 +1,44 @@ +mat-select { + width: 100%; +} + +.settings { + margin-left: 20px; + min-width: 300px; +} + +.settings-group { + margin-bottom: 24px; + width: 100%; + + h4 { + margin-bottom: 8px; + border-bottom: 1px solid #eee; + padding-bottom: 4px; + color: #666; + } +} + + +.selection-summary { + margin-top: 12px; + padding: 8px; + background-color: #f5f5f5; + border-radius: 4px; + font-size: 0.9em; + color: #555; +} + +:host { display: block; } +.fx-column-start-stretch { display: flex; flex-direction: column; } +.fx-row-start-stretch, .fx-row-space-between-stretch { display: flex; align-items: stretch; gap: 16px; } +.fx-row-space-between-stretch { width: 100%; } +.fx-column-start-start { display: flex; flex-direction: column; align-items: flex-start; } +.fx-row-space-between-stretch > .fx-column-start-stretch { flex: 1; min-width: 320px; } +mat-table { width: 100%; max-height: 55vh; overflow: auto; } +mat-form-field { width: 100%; } +.mat-column-select { flex: 0 0 48px; } +@media (max-width: 760px) { + .fx-row-space-between-stretch { flex-direction: column; } + .settings { margin-left: 0; min-width: 0; } +} diff --git a/projects/ngx-coding-components/codebook-export/codebook-export.component.ts b/projects/ngx-coding-components/codebook-export/codebook-export.component.ts new file mode 100644 index 0000000..ad70524 --- /dev/null +++ b/projects/ngx-coding-components/codebook-export/codebook-export.component.ts @@ -0,0 +1,127 @@ +import { + AfterViewInit, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild +} from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { MatTableDataSource, MatTableModule } from '@angular/material/table'; +import { MatSort, MatSortModule } from '@angular/material/sort'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatButtonModule } from '@angular/material/button'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatSelectModule } from '@angular/material/select'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { TranslateModule } from '@ngx-translate/core'; +import type { + CodeBookContentSetting, CodebookExportConfig, MissingsProfile, UnitSelectionItem +} from '@iqb/ngx-coding-components/codebook-models'; + +export const DEFAULT_CODEBOOK_OPTIONS: CodeBookContentSetting = { + exportFormat: 'docx', + missingsProfile: '', + hasOnlyManualCoding: true, + hasGeneralInstructions: true, + hasDerivedVars: true, + hasOnlyVarsWithCodes: true, + hasClosedVars: true, + codeLabelToUpper: true, + showScore: true, + hideItemVarRelation: true, + trainingRequirement: 'all' +}; + +/** Studio's export form. Loading data, downloading and job handling belong to the host. */ +@Component({ + selector: 'ngx-codebook-export', + standalone: true, + imports: [FormsModule, MatTableModule, MatSortModule, MatCheckboxModule, MatDialogModule, + MatButtonModule, MatTooltipModule, MatSelectModule, MatRadioModule, MatFormFieldModule, + MatInputModule, TranslateModule], + templateUrl: './codebook-export.component.html', + styleUrls: ['./codebook-export.component.scss'] +}) +export class CodebookExportComponent implements OnChanges, AfterViewInit { + @Input() availableUnits: UnitSelectionItem[] = []; + @Input() missingsProfiles: MissingsProfile[] = []; + @Input() selectedUnitIds: number[] = []; + @Input() selectedMissingsProfileId = 0; + @Input() defaultContentOptions: Partial = {}; + @Input() showGroupColumn = true; + @Input() loading = false; + @Input() busy = false; + @Input() workspaceChanges = false; + @Output() selectionChanged = new EventEmitter(); + @Output() exportRequested = new EventEmitter(); + @Output() cancel = new EventEmitter(); + @ViewChild(MatSort) sort!: MatSort; + + dataSource = new MatTableDataSource([]); + columns = ['select', 'key', 'name', 'group']; + selection: number[] = []; + filter = ''; + contentOptions = { ...DEFAULT_CODEBOOK_OPTIONS }; + + ngOnChanges(changes: SimpleChanges): void { + if (changes['showGroupColumn']) { + this.columns = this.showGroupColumn ? ['select', 'key', 'name', 'group'] : ['select', 'key', 'name']; + } + if (changes['defaultContentOptions']) { + // Only shared options cross this boundary; host-specific filters stay with the host. + this.contentOptions = Object.fromEntries(Object.entries(DEFAULT_CODEBOOK_OPTIONS).map(([key, value]) => ( + [key, this.defaultContentOptions[key as keyof CodeBookContentSetting] ?? value] + ))) as unknown as CodeBookContentSetting; + } + if (changes['availableUnits']) { + this.dataSource.data = this.availableUnits; + this.dataSource.filterPredicate = (unit, filter) => [unit.key, unit.unitName, this.showGroupColumn ? unit.groupName || '' : ''] + .some(value => value.toLowerCase().includes(filter)); + this.dataSource.sortingDataAccessor = (unit, column) => ({ key: unit.key, name: unit.unitName, group: unit.groupName || '' }[column] || ''); + } + if (changes['missingsProfiles'] && !this.missingsProfiles.some(profile => profile.id === this.selectedMissingsProfileId)) { + this.selectedMissingsProfileId = 0; + } + if (changes['selectedUnitIds']) this.selection = [...this.selectedUnitIds]; + const validIds = new Set(this.availableUnits.filter(unit => !unit.disabled).map(unit => unit.unitId)); + const validSelection = this.selection.filter(id => validIds.has(id)); + if (validSelection.length !== this.selection.length) { + this.selection = validSelection; + this.selectionChanged.emit([...this.selection]); + } + } + + ngAfterViewInit(): void { this.dataSource.sort = this.sort; } + + get allSelected(): boolean { + const selectable = this.availableUnits.filter(unit => !unit.disabled); + return selectable.length > 0 && selectable.every(unit => this.selection.includes(unit.unitId)); + } + + get exportDisabled(): boolean { + return !this.selection.length || this.loading || this.busy || this.workspaceChanges; + } + + applyFilter(): void { this.dataSource.filter = this.filter.trim().toLowerCase(); } + + toggle(unit: UnitSelectionItem): void { + if (unit.disabled || this.loading || this.busy) return; + this.selection = this.selection.includes(unit.unitId) ? this.selection.filter(id => id !== unit.unitId) : [...this.selection, unit.unitId]; + this.selectionChanged.emit([...this.selection]); + } + + toggleAll(): void { + if (this.loading || this.busy) return; + this.selection = this.allSelected ? [] : this.availableUnits.filter(unit => !unit.disabled).map(unit => unit.unitId); + this.selectionChanged.emit([...this.selection]); + } + + exportCodingBook(): void { + if (this.exportDisabled) return; + const profile = this.missingsProfiles.find(item => item.id === this.selectedMissingsProfileId); + this.exportRequested.emit({ + selectedUnits: [...this.selection], + missingsProfileId: this.selectedMissingsProfileId, + contentOptions: { ...this.contentOptions, missingsProfile: profile?.label || '' } + }); + } +} diff --git a/projects/ngx-coding-components/codebook-export/ng-package.json b/projects/ngx-coding-components/codebook-export/ng-package.json new file mode 100644 index 0000000..ed27894 --- /dev/null +++ b/projects/ngx-coding-components/codebook-export/ng-package.json @@ -0,0 +1,6 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "lib": { + "entryFile": "public-api.ts" + } +} diff --git a/projects/ngx-coding-components/codebook-export/public-api.ts b/projects/ngx-coding-components/codebook-export/public-api.ts new file mode 100644 index 0000000..67bb635 --- /dev/null +++ b/projects/ngx-coding-components/codebook-export/public-api.ts @@ -0,0 +1,2 @@ +export * from './codebook-export.component'; +export type * from '@iqb/ngx-coding-components/codebook-models'; diff --git a/projects/ngx-coding-components/codebook-generator/codebook-docx-generator.ts b/projects/ngx-coding-components/codebook-generator/codebook-docx-generator.ts new file mode 100644 index 0000000..e4b2b2d --- /dev/null +++ b/projects/ngx-coding-components/codebook-generator/codebook-docx-generator.ts @@ -0,0 +1,1090 @@ +import { + AlignmentType, + Document, + HeadingLevel, + ImageRun, + NumberFormat, + HorizontalPositionAlign, + HorizontalPositionRelativeFrom, + VerticalPositionRelativeFrom, + TextWrappingType, + TextWrappingSide, + TabStopType, + Tab, + Packer, + Paragraph, + Table, + TableCell, + TableRow, + TextRun, + Footer, + WidthType, + PageNumber, + ITableCellBorders, + ImportedXmlComponent, + ParagraphChild, + IRunOptions +} from 'docx'; + +import { + CodeBookContentSetting, + CodebookUnitDto, + BookVariable, + ItemMetadata +} from '@iqb/ngx-coding-components/codebook-models'; +import * as cheerio from 'cheerio'; +import { Buffer } from 'buffer'; +import type { Element, AnyNode } from 'domhandler'; +import { BasicAcceptedElems } from 'cheerio'; + +import { imageSize } from 'image-size'; + +// katex is CommonJS with no statically detectable named exports: under ESM `import * as katex` +// yields only `{ default }`, and every `katex.x` is undefined. The default import works in both. +import katex from 'katex'; +import { mml2omml } from 'mathml2omml'; +import { WebColors } from './web-colors'; +// Type-only, like every deep import into a package: were it ever emitted as a runtime require, +// an `exports` map in that package would kill the boot -- see the swagger import in +// setting.controller.ts. +type FileChild = Paragraph | Table; +type InstructionParagraph = { + element: Element; + marker?: string; + listDepth?: number; + listIndent?: number; + quoteDepth?: number; + imageWidth?: number; +}; + +export class CodebookDocxGenerator { + private static ommlCache: Record = {}; + + static async generateDocx( + codingBookUnits: CodebookUnitDto[], + contentSetting: CodeBookContentSetting + ): Promise { + const units: FileChild[] = []; + const missingDefinitions = new Map(); + codingBookUnits.forEach(unit => { + unit.missings.forEach(missing => missingDefinitions.set(JSON.stringify(missing), missing)); + if (unit.variables.length) { + units.push(...this.createDocXForUnit(unit.items || [], unit.variables, contentSetting, this.getUnitHeader(unit))); + } + }); + const missings = this.getMissings({ + key: '', name: '', variables: [], missings: [...missingDefinitions.values()] + }); + return Packer.toBlob(this.setDocXDocument(units, missings)); + } + + private static getUnitHeader(variableCoding: CodebookUnitDto): Paragraph { + return new Paragraph({ + border: { + bottom: { + color: '#000000', + style: 'single', + size: 10 + }, + top: { + color: '#000000', + style: 'single', + size: 10 + } + }, + spacing: { + after: 200 + }, + text: `${variableCoding.key} ${variableCoding.name}`, + heading: HeadingLevel.HEADING_1, + alignment: AlignmentType.CENTER + }); + } + + private static getMissings(variableCoding: CodebookUnitDto): Paragraph[] { + const missings: Paragraph[] = []; + try { + variableCoding.missings.forEach(missing => { + if (missing.code !== undefined && missing.code !== null && `${missing.code}` !== '' && missing.label && missing.description) { + missings.push( + new Paragraph({ + children: [ + new TextRun({ + text: `${missing.code} ${missing.label}`, + bold: true + }) + ], + spacing: { + after: 20 + } + }) + ); + missings.push( + new Paragraph({ + text: `${missing.description}`, + spacing: { + after: 100 + } + }) + ); + } else { + missings.push( + new Paragraph({ + text: 'kein valides Missing ', + spacing: { + after: 200 + } + }) + ); + } + }); + } catch { + missings.push( + new Paragraph({ + text: 'kein validen Missings gefunden', + spacing: { + after: 200 + } + }) + ); + } + return missings; + } + + private static get TableBoarders(): ITableCellBorders { + return { + top: { + size: 1, + color: '#000000', + style: 'single' + }, + bottom: { + size: 1, + color: '#000000', + style: 'single' + }, + left: { + size: 1, + color: '#000000', + style: 'single' + }, + right: { + size: 1, + color: '#000000', + style: 'single' + } + }; + } + + private static getCodeRows( + variable: BookVariable, + contentSetting: CodeBookContentSetting + ): TableRow[] { + return variable.codes.map( + code => new TableRow({ + cantSplit: true, + children: [ + CodebookDocxGenerator.createCodeCell( + CodebookDocxGenerator.createCellChildren(code.id), + CodebookDocxGenerator.getColumnWidths(contentSetting)[0] + ), + CodebookDocxGenerator.createCodeCell( + CodebookDocxGenerator.createCellChildren(code.label), + CodebookDocxGenerator.getColumnWidths(contentSetting)[1] + ), + ...(contentSetting.showScore ? + [ + CodebookDocxGenerator.createCodeCell( + CodebookDocxGenerator.createCellChildren(code.score ?? ''), + CodebookDocxGenerator.getColumnWidths(contentSetting)[2] + ) + ] : + []), + CodebookDocxGenerator.createCodeCell( + [...CodebookDocxGenerator.htmlToDocx(code.description, contentSetting)], + CodebookDocxGenerator.getColumnWidths(contentSetting)[ + CodebookDocxGenerator.getColumnWidths(contentSetting).length - 1 + ] + ) + ] + }) + ); + } + + private static createCellChildren(value: string): Paragraph[] { + return [ + new Paragraph({ + text: value, + spacing: { + before: 100, + after: 100 + }, + indent: { start: 100, end: 100 } + }) + ]; + } + + private static createCodeCell( + children: Paragraph[], + width: number + ): TableCell { + return new TableCell({ + borders: CodebookDocxGenerator.TableBoarders, + children: children, + // Need for Word, but not for Writer + width: { + size: width, + type: WidthType.PERCENTAGE + } + }); + } + + private static setDocXDocument( + units: FileChild[], + missings: Paragraph[] + ): Document { + return new Document({ + background: { + color: '#FFFFFF' + }, + sections: [ + { + properties: { + page: { + pageNumbers: { + start: 1, + formatType: NumberFormat.DECIMAL + } + } + }, + footers: { + default: new Footer({ + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + children: [ + new TextRun('IQB Codebook '), + new TextRun(new Date().toLocaleDateString()) + ] + }), + new Paragraph({ + alignment: AlignmentType.CENTER, + children: [ + new TextRun({ + color: '000000', + children: [' Seite ', PageNumber.CURRENT] + }), + new TextRun({ + color: '000000', + children: [' von ', PageNumber.TOTAL_PAGES] + }) + ] + }) + ] + }) + }, + children: [...missings, ...units] + } + ] + }); + } + + private static getVariableHeader(variable: BookVariable): Paragraph { + return new Paragraph({ + text: `${variable.id} ${variable.label}`, + heading: HeadingLevel.HEADING_2, + alignment: AlignmentType.LEFT, + spacing: { + before: 200, + after: 200 + } + }); + } + + private static getVariableItems( + variable: BookVariable, + varItems: ItemMetadata[] + ): Paragraph[] { + const filteredVarItems = varItems.filter( + item => item.variableId === variable.id + ); + let itemString = ''; + filteredVarItems.forEach(item => { + itemString += `${item.id} `; + }); + if (filteredVarItems.length === 0) { + return []; + } + + return [new Paragraph({ + text: `Item(s): ${itemString}`, + heading: HeadingLevel.HEADING_3, + alignment: AlignmentType.LEFT, + spacing: { + before: 200, + after: 200 + } + })]; + } + + private static getGeneralInstructions( + contentSetting: CodeBookContentSetting, + codeBookVariable: BookVariable + ): Paragraph[] { + return contentSetting.hasGeneralInstructions ? + CodebookDocxGenerator.htmlToDocx( + codeBookVariable.generalInstruction, + contentSetting + ) : + []; + } + + private static getCodeTable( + codeBookVariable: BookVariable, + contentSetting: CodeBookContentSetting + ): Table { + return new Table({ + rows: CodebookDocxGenerator.getCodeRows(codeBookVariable, contentSetting), + width: { + size: 100, + type: WidthType.PERCENTAGE + }, + columnWidths: CodebookDocxGenerator.getColumnWidths(contentSetting) // Need for Writer, but not for Word + }); + } + + private static getColumnWidths( + contentSetting: CodeBookContentSetting + ): number[] { + return contentSetting.showScore ? [8, 24, 8, 60] : [8, 24, 68]; + } + + private static getVariables( + codeBookVariable: BookVariable[], + contentSetting: CodeBookContentSetting, + varItems: ItemMetadata[] + ): FileChild[] { + const variables: FileChild[] = []; + codeBookVariable.forEach(variable => { + variables.push( + ...[ + CodebookDocxGenerator.getVariableHeader(variable), + ...(contentSetting.hideItemVarRelation ? [] : CodebookDocxGenerator.getVariableItems(variable, varItems)), + ...CodebookDocxGenerator.getGeneralInstructions(contentSetting, variable), + CodebookDocxGenerator.getCodeTable(variable, contentSetting) + ] + ); + }); + return variables; + } + + private static createDocXForUnit( + varItems: ItemMetadata[], + codeBookVariable: BookVariable[], + contentSetting: CodeBookContentSetting, + unitHeader: Paragraph + ): FileChild[] { + return [ + unitHeader, + ...CodebookDocxGenerator.getVariables(codeBookVariable, contentSetting, varItems), + new Paragraph({ + text: '', + spacing: { + before: 100, + after: 100 + } + }) + ]; + } + + private static getChildren( + cheerioAPI: cheerio.CheerioAPI, + elem: BasicAcceptedElems + ): AnyNode[] { + return cheerioAPI(elem).toArray(); + } + + private static processInlineElements( + nodes: AnyNode[], + children: ParagraphChild[], + colorParsed: string, + backgroundColor: string, + size: string, + formatting: Pick = {}, + imageWidth = 334 + ): void { + const runStyle = { + ...formatting, + color: colorParsed, + shading: { fill: backgroundColor }, + size: CodebookDocxGenerator.getFontSize(size) + }; + nodes.forEach(node => { + if (node.type === 'text') { + // Preserve non-breaking spaces and repeated spacing from Studio instructions. + node.data.split('\n').forEach((line, index) => { + if (index) children.push(new TextRun({ break: 1, ...runStyle })); + if (line) children.push(new TextRun({ text: line, ...runStyle })); + }); + return; + } + if (node.type !== 'tag') return; + const element = node as Element; + const tagName = element.name.toLowerCase(); + const styles = CodebookDocxGenerator.inlineStyles(element); + const nestedColor = CodebookDocxGenerator.cssColor(styles['color'], colorParsed); + const nestedBackground = CodebookDocxGenerator.cssColor(styles['background-color'], backgroundColor); + const nestedSize = styles['font-size'] || size; + if ((tagName === 'span' && element.attribs?.['class']?.split(/\s+/).includes('iqb-math-formula')) || + tagName === 'aspect-nodeview-math-formula') { + const storedLatex = element.attribs?.['data-latex'] || element.attribs?.['formula'] || ''; + const latex = CodebookDocxGenerator.decodeLatex(storedLatex).trim(); + if (latex) { + const mathStyle: IRunOptions = { + ...formatting, + size: CodebookDocxGenerator.getFontSize(nestedSize), + color: nestedColor, + shading: { fill: nestedBackground } + }; + children.push( + CodebookDocxGenerator.latexToOmml(latex, mathStyle) || new TextRun({ text: latex, ...mathStyle }) + ); + } + return; + } + if (tagName === 'img') { + const fontPixels = (CodebookDocxGenerator.getFontSize(size) * 2) / 3; + children.push(CodebookDocxGenerator.createImageRun(element, imageWidth, fontPixels)); + return; + } + if (tagName === 'br') { + children.push(new TextRun({ break: 1, ...runStyle })); + return; + } + const nestedFormatting = { ...formatting }; + if (tagName === 'strong' || tagName === 'b') nestedFormatting.bold = true; + if (tagName === 'em' || tagName === 'i') nestedFormatting.italics = true; + if (tagName === 'u') nestedFormatting.underline = {}; + if (tagName === 's' || tagName === 'strike') nestedFormatting.strike = true; + if (tagName === 'sub') { nestedFormatting.subScript = true; nestedFormatting.superScript = false; } + if (tagName === 'sup') { nestedFormatting.superScript = true; nestedFormatting.subScript = false; } + CodebookDocxGenerator.processInlineElements( + element.children, children, nestedColor, nestedBackground, nestedSize, nestedFormatting, imageWidth + ); + }); + } + + private static getImageSize(imageBuffer: Buffer): { width: number; height: number } { + const size = imageSize(imageBuffer as unknown as Uint8Array); + if (!size.width || !size.height) throw new Error('Ungültige Bildgröße'); + return { width: size.width, height: size.height }; + } + + private static getTransformation( + actualSize: { width: number; height: number }, + max: number + ): { width: number; height: number } { + const transformedSize: { width: number; height: number } = { + width: actualSize.width, + height: actualSize.height + }; + const maxWidth = max; + const maxHeight = max; + if (actualSize.width > maxWidth || actualSize.height > maxHeight) { + const ratio = Math.min( + maxWidth / actualSize.width, + maxHeight / actualSize.height + ); + transformedSize.width = actualSize.width * ratio; + transformedSize.height = actualSize.height * ratio; + } + return transformedSize; + } + + private static createImagePragraph( + elem: Element, + contentSetting: CodeBookContentSetting + ): Paragraph { + return new Paragraph({ + spacing: { + before: 100, + after: 100 + }, + indent: { + start: 100, + end: 100 + }, + children: [ + CodebookDocxGenerator.createImageRun(elem, contentSetting.showScore ? 334 : 382) + ] + }); + } + + private static createImageRun(elem: Element, maxWidth: number, fontPixels = 20): ImageRun { + const source = elem.attribs['src'] || ''; + const imageBuffer = Buffer.from(source.substring(source.indexOf(',') + 1), 'base64'); + const actual = CodebookDocxGenerator.getImageSize(imageBuffer); + const styles = CodebookDocxGenerator.inlineStyles(elem); + let height = actual.height; + if (styles['height'] === '1em') height = fontPixels; + else if (/^\d+(\.\d+)?px$/.test(styles['height'] || '')) height = parseFloat(styles['height']); + const width = /^\d+(\.\d+)?px$/.test(styles['width'] || '') ? + parseFloat(styles['width']) : (actual.width * height) / actual.height; + const side = styles['float']; + return new ImageRun({ + data: imageBuffer, + transformation: CodebookDocxGenerator.getTransformation({ width, height }, maxWidth), + ...(side === 'left' || side === 'right' ? { + floating: { + horizontalPosition: { + relative: HorizontalPositionRelativeFrom.COLUMN, + align: side === 'left' ? HorizontalPositionAlign.LEFT : HorizontalPositionAlign.RIGHT + }, + verticalPosition: { relative: VerticalPositionRelativeFrom.PARAGRAPH, offset: 0 }, + layoutInCell: true, + allowOverlap: false, + wrap: { + type: TextWrappingType.SQUARE, + side: side === 'left' ? TextWrappingSide.RIGHT : TextWrappingSide.LEFT, + margins: { [side === 'left' ? 'right' : 'left']: 9525 * 10 } + } + } + } : {}) + }); + } + + private static inlineStyles(elem: Element): Record { + return Object.fromEntries((elem.attribs['style'] || '').split(';').flatMap(declaration => { + const colon = declaration.indexOf(':'); + if (colon < 0) return []; + const property = declaration.substring(0, colon).trim().toLowerCase(); + const value = declaration.substring(colon + 1).trim(); + return [[property, value]]; + })); + } + + private static cssColor(value: string | undefined, fallback: string): string { + if (!value || value === 'inherit' || value === 'initial' || value === 'currentColor') return fallback; + if (/^#[0-9a-f]{6}$/i.test(value)) return value.substring(1).toUpperCase(); + if (/^#[0-9a-f]{3}$/i.test(value)) { + return value.substring(1).split('').map(part => part + part).join('') + .toUpperCase(); + } + if (value.startsWith('rgb')) { + const rgb = CodebookDocxGenerator.parseCssRgbString(value); + if (rgb?.length === 3 && rgb.every(component => Number.isFinite(component))) { + return CodebookDocxGenerator.toHex(rgb[0], rgb[1], rgb[2]).toUpperCase(); + } + } + return WebColors.getHexFromWebColor(value.toLowerCase())?.replace(/^#/, '').toUpperCase() || fallback; + } + + private static getTextAlignment( + cheerioAPI: cheerio.CheerioAPI, + elem: Element + ): string { + return cheerioAPI(elem).css('text-align') || 'left'; + } + + private static stripFromLeadingEmptyParagraph(html: string): string { + return html.replace(/^

<\/p>/, ''); + } + + private static prepareHtml(html: string): string { + // Newlines in LaTeX/HTML attributes are data, not paragraph breaks. + return CodebookDocxGenerator.stripFromLeadingEmptyParagraph(html); + } + + private static decodeLatex(latex: string): string { + if (!latex) return ''; + try { + return decodeURIComponent(latex); + } catch { + return latex; + } + } + + private static latexToOmml(latex: string, style?: IRunOptions): ImportedXmlComponent | null { + const trimmed = latex.trim(); + if (!trimmed) return null; + try { + if (!CodebookDocxGenerator.ommlCache[trimmed]) { + const mathml = katex.renderToString(trimmed, { + output: 'mathml', + throwOnError: false, + strict: 'ignore' + }); + CodebookDocxGenerator.ommlCache[trimmed] = CodebookDocxGenerator.normalizeStretchyBars( + mathml, mml2omml(mathml) + ); + } + const omml = CodebookDocxGenerator.styleMathRuns(CodebookDocxGenerator.ommlCache[trimmed], style); + return CodebookDocxGenerator.unwrapImportedXmlRoot( + ImportedXmlComponent.fromXmlString( + CodebookDocxGenerator.sanitizeOmmlXml(omml) + ) + ); + } catch { + return null; + } + } + + /** The converter treats wide over/underlines as fixed-width accents and loses their position. */ + private static normalizeStretchyBars(mathml: string, omml: string): string { + const math = cheerio.load(mathml, { xml: true }, false); + const positions: Array<'top' | 'bot' | undefined> = []; + math('mover,munder').each((_, node) => { + const operator = math(node).children().last(); + if (operator[0]?.name === 'mo' && operator.text() === '\u203e') { + let position: 'top' | 'bot' | undefined; + if (operator.attr('stretchy') !== 'false') position = node.name === 'munder' ? 'bot' : 'top'; + positions.push(position); + } + }); + if (!positions.some(position => position)) return omml; + const $ = cheerio.load(omml, { xml: true }, false); + let index = 0; + $('m\\:acc').each((_, node) => { + const accent = $(node); + if (accent.children('m\\:accPr').children('m\\:chr').attr('m:val') !== '\u203e') return; + const position = positions[index]; + index += 1; + if (!position) return; + const bar = $(''); + const properties = $('').append(``); + properties.append(accent.children('m\\:accPr').children('m\\:ctrlPr')); + bar.append(properties).append(accent.children('m\\:e')); + accent.replaceWith(bar); + }); + return $.xml(); + } + + /** Apply surrounding editor marks without changing cached LaTeX or unstyled Studio math. */ + private static styleMathRuns(omml: string, style?: IRunOptions): string { + if (!style) return omml; + const properties: Record> = {}; + if (style.bold) properties['b'] = {}; + if (style.italics) properties['i'] = {}; + if (style.underline) properties['u'] = { 'w:val': 'single' }; + if (style.strike) properties['strike'] = {}; + if (style.size !== 20) { + properties['sz'] = { 'w:val': String(style.size) }; + properties['szCs'] = { 'w:val': String(style.size) }; + } + if (style.color && !['#000000', '000000'].includes(style.color)) { + properties['color'] = { 'w:val': style.color.replace(/^#/, '') }; + } + const fill = style.shading?.fill; + if (fill && !['#FFFFFF', 'FFFFFF'].includes(fill)) properties['shd'] = { 'w:fill': fill.replace(/^#/, '') }; + if (!Object.keys(properties).length) return omml; + const $ = cheerio.load(omml, { xml: true }, false); + const applyProperties = (parent: cheerio.Cheerio): void => { + let runProperties = parent.children('w\\:rPr').first(); + if (!runProperties.length) { + parent.append(''); + runProperties = parent.children('w\\:rPr').first(); + } + Object.entries(properties).forEach(([name, attributes]) => { + runProperties.children(`w\\:${name}`).remove(); + runProperties.append(``); + runProperties.children(`w\\:${name}`).last().attr(attributes); + }); + const order = ['b', 'bCs', 'i', 'iCs', 'strike', 'color', 'sz', 'szCs', 'u', 'shd']; + const children = runProperties.children().toArray().sort((a, b) => ( + order.indexOf(a.name.substring(2)) - order.indexOf(b.name.substring(2)) + )); + runProperties.empty().append(children); + }; + $('m\\:r').each((_, run) => { + const element = $(run); + applyProperties(element); + element.prepend(element.children('w\\:rPr').remove()); + const mathProperties = element.children('m\\:rPr').remove(); + if (mathProperties.length) element.prepend(mathProperties); + }); + // Math control properties also style fraction bars, radicals and large operators. + ['acc', 'bar', 'borderBox', 'box', 'd', 'eqArr', 'f', 'func', 'groupChr', + 'limLow', 'limUpp', 'm', 'nary', 'phant', 'rad', 'sPre', 'sSub', 'sSubSup', 'sSup'].forEach(name => { + $(`m\\:${name}`).each((_, node) => { + const element = $(node); + let structureProperties = element.children(`m\\:${name}Pr`).first(); + if (!structureProperties.length) { + element.prepend(``); + structureProperties = element.children(`m\\:${name}Pr`).first(); + } + let control = structureProperties.children('m\\:ctrlPr').first(); + if (!control.length) { + structureProperties.append(''); + control = structureProperties.children('m\\:ctrlPr').first(); + } + applyProperties(control); + }); + }); + return $.xml(); + } + + private static unwrapImportedXmlRoot( + component: ImportedXmlComponent + ): ImportedXmlComponent { + const importedComponent = component as ImportedXmlComponent & { + root?: Array; + rootKey?: string; + }; + if (importedComponent.rootKey !== undefined) { + return component; + } + + const firstChild = importedComponent.root?.[0]; + return typeof firstChild === 'object' && firstChild !== null ? + firstChild as ImportedXmlComponent : + component; + } + + private static isMathComponent( + child: ParagraphChild + ): child is ImportedXmlComponent { + if (typeof child !== 'object' || child === null) { + return false; + } + const importedChild = child as ImportedXmlComponent & { + rootKey?: string; + }; + return importedChild.rootKey === 'm:oMath' || + importedChild.rootKey === 'm:oMathPara'; + } + + private static createLeftAlignedMathParagraph( + mathChildren: ImportedXmlComponent[], + alignment = 'left' + ): ImportedXmlComponent { + const mathParagraph = new ImportedXmlComponent('m:oMathPara'); + const mathParagraphProperties = new ImportedXmlComponent('m:oMathParaPr'); + mathParagraphProperties.push( + new ImportedXmlComponent('m:jc', { 'm:val': ['center', 'right'].includes(alignment) ? alignment : 'left' }) + ); + mathParagraph.push(mathParagraphProperties); + mathChildren.forEach(mathChild => mathParagraph.push(mathChild)); + return mathParagraph; + } + + private static normalizeParagraphChildren( + children: ParagraphChild[], + alignment = 'left' + ): ParagraphChild[] { + if (!children.length) { + return children; + } + + const mathChildren = children.filter( + CodebookDocxGenerator.isMathComponent + ) as ImportedXmlComponent[]; + if (mathChildren.length === children.length) { + return [CodebookDocxGenerator.createLeftAlignedMathParagraph(mathChildren, alignment)]; + } + + return children; + } + + private static sanitizeOmmlXml(omml: string): string { + if (!omml) return omml; + return omml.replace( + /(]*>)([\s\S]*?)(<\/m:t>)/g, + (_, prefix: string, rawText: string, suffix: string) => `${prefix}${CodebookDocxGenerator.escapeXmlText(rawText)}${suffix}` + ); + } + + private static escapeXmlText(text: string): string { + return text + .replace( + /&(?!(?:amp|lt|gt|quot|apos|#\d+|#x[0-9a-fA-F]+);)/g, + '&' + ) + .replace(//g, '>'); + } + + private static normalizeMathTokens(html: string): string { + return html.replace( + /\[\[iqb-math:([\s\S]*?)]]/g, + (_, encodedFormula: string) => { + const latex = this.decodeLatex(encodedFormula); + const escapedLatex = latex.replace(/"/g, '"'); + return ``; + } + ); + } + + /** Put loose text and inline marks into paragraphs without duplicating existing blocks. */ + private static wrapInlineParagraphs(cheerioAPI: cheerio.CheerioAPI, parent: AnyNode): void { + let inlineNodes: AnyNode[] = []; + const flush = () => { + if (inlineNodes.some(node => node.type === 'tag' || (node.type === 'text' && node.data.trim()))) { + cheerioAPI(inlineNodes).wrapAll('

'); + } + inlineNodes = []; + }; + cheerioAPI(parent).contents().toArray().forEach(node => { + if (node.type === 'tag' && ( + /^(p|h[1-4]|img|hr|ul|ol|li|div|blockquote)$/.test(node.name) || + cheerioAPI(node).find('p,h1,h2,h3,h4,img,hr,ul,ol').length > 0 + )) { + flush(); + if (!/^(p|h[1-4]|img|hr)$/.test(node.name)) { + CodebookDocxGenerator.wrapInlineParagraphs(cheerioAPI, node); + } + } else if (node.type === 'text' || node.type === 'tag') { + inlineNodes.push(node); + } + }); + flush(); + } + + private static htmlToDocx( + html: string, + contentSetting: CodeBookContentSetting + ) { + if (!html.trim()) return []; + const normalizedHtml = CodebookDocxGenerator.normalizeMathTokens(html); + const blockHtml = /<(p|h[1-4]|img|hr|ul|ol)\b/i.test(normalizedHtml) ? normalizedHtml : `

${normalizedHtml}

`; + const cheerioAPI = cheerio.load( + CodebookDocxGenerator.prepareHtml(blockHtml), + null, + false + ); + CodebookDocxGenerator.wrapInlineParagraphs(cheerioAPI, cheerioAPI.root()[0]); + const elements: Paragraph[] = []; + type Context = { + listDepth: number; listNesting: number; listIndent: number; quoteDepth: number; marker?: string; + legacyBullet?: boolean; size: string; color: string; background: string; + }; + const walk = (parent: AnyNode, context: Context): void => { + cheerioAPI(parent).contents().toArray().forEach(node => { + if (node.type !== 'tag') return; + const elem = node as Element; + const styles = CodebookDocxGenerator.inlineStyles(elem); + const size = styles['font-size'] || context.size; + const color = CodebookDocxGenerator.cssColor(styles['color'], context.color); + const background = CodebookDocxGenerator.cssColor(styles['background-color'], context.background); + const inherited = { + ...context, size, color, background + }; + try { + if (elem.name === 'img') { + elements.push(CodebookDocxGenerator.createImagePragraph(elem, contentSetting)); + } else if (/^(p|h[1-4])$/.test(elem.name)) { + const headingSize = { + h1: '20px', h2: '18px', h3: '16px', h4: '16px' + }[elem.name]; + elements.push(CodebookDocxGenerator.createParagraph( + cheerioAPI, + elem.children, + CodebookDocxGenerator.getTextAlignment(cheerioAPI, elem), + color, + background, + styles['font-size'] || headingSize || size, + !!context.legacyBullet, + { + element: elem, + marker: context.marker, + listDepth: context.listDepth, + listIndent: context.listIndent, + quoteDepth: context.quoteDepth, + imageWidth: contentSetting.showScore ? 334 : 382 + } + )); + context.marker = undefined; + context.legacyBullet = false; + } else if (elem.name === 'ul' || elem.name === 'ol') { + const style = styles['list-style-type'] || styles['list-style'] || (elem.name === 'ol' ? 'decimal' : 'disc'); + const legacy = elem.name === 'ul' && !styles['list-style-type'] && + !styles['list-style'] && context.listNesting === 0; + const parsedStart = Number.parseInt(elem.attribs['start'], 10); + const start = Number.isFinite(parsedStart) ? parsedStart : 1; + cheerioAPI(elem).children('li').toArray().forEach((li, index) => { + const padding = parseFloat(CodebookDocxGenerator.inlineStyles(li)['padding-left']) || 0; + walk(li, { + ...inherited, + marker: legacy ? undefined : CodebookDocxGenerator.listMarker(style, start + index), + legacyBullet: legacy, + listDepth: legacy ? 0 : context.listNesting + 1, + listNesting: context.listNesting + 1, + listIndent: context.listIndent + Math.round(padding * 15) + }); + }); + } else if (elem.name === 'hr') { + const short = styles['width'] === '20%' || elem.attribs['short'] === 'true'; + elements.push(new Paragraph({ + border: { bottom: { color: '000000', style: 'single', size: 6 } }, + indent: short ? { end: Math.round((contentSetting.showScore ? 334 : 382) * 15 * 0.8) } : undefined, + spacing: { before: 100, after: 100 } + })); + } else { + walk(elem, { ...inherited, quoteDepth: context.quoteDepth + Number(elem.name === 'blockquote') }); + } + } catch { + elements.push(new Paragraph({ text: 'HTML konnte nicht verarbeitet werden.' })); + } + }); + }; + walk(cheerioAPI.root()[0], { + listDepth: 0, + listNesting: 0, + listIndent: 0, + quoteDepth: 0, + size: '10pt', + color: '#000000', + background: '#FFFFFF' + }); + return elements.filter(e => e !== undefined && e !== null); + } + + /** Literal markers retain all CSS list styles, including Greek, in a static Word export. */ + private static listMarker(style: string, value: number): string { + const bullets: Record = { disc: '•', circle: '○', square: '▪' }; + if (bullets[style]) return bullets[style]; + if (style.includes('roman')) { + let remainder = Math.min(10000, Math.max(1, Math.floor(value))); + let roman = ''; + const numbers: [number, string][] = [[1000, 'M'], [900, 'CM'], [500, 'D'], [400, 'CD'], + [100, 'C'], [90, 'XC'], [50, 'L'], [40, 'XL'], [10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I']]; + numbers.forEach(([number, symbol]) => { + while (remainder >= number) { + roman += symbol; + remainder -= number; + } + }); + return `${style.startsWith('lower') ? roman.toLowerCase() : roman}.`; + } + if (['lower-latin', 'upper-latin', 'lower-alpha', 'upper-alpha', 'lower-greek'].includes(style)) { + const alphabet = style === 'lower-greek' ? 'αβγδεζηθικλμνξοπρστυφχψω' : 'abcdefghijklmnopqrstuvwxyz'; + let number = Math.max(1, Math.floor(value)); + let marker = ''; + while (number > 0) { + number -= 1; + marker = alphabet[number % alphabet.length] + marker; + number = Math.floor(number / alphabet.length); + } + return `${style.startsWith('upper') ? marker.toUpperCase() : marker}.`; + } + return `${value}.`; + } + + private static getFontSize(size: string): number { + const sizeTypes = [ + 'xx-small', + 'x-small', + 'small', + 'medium', + 'large', + 'x-large', + 'xx-large' + ]; + if (sizeTypes.includes(size)) return 20; + const number = parseFloat(size); + if (!Number.isFinite(number) || number <= 0) return 20; + // CSS pixels are 1/96 inch; Word stores font sizes in half-points (1/144 inch). + return Math.max(1, Math.min(1638, Math.round(number * (size.endsWith('px') ? 1.5 : 2)))); + } + + private static getAlignment( + textAlignment: string + ): typeof AlignmentType[keyof typeof AlignmentType] { + switch (textAlignment) { + case 'center': + return AlignmentType.CENTER; + case 'right': + return AlignmentType.RIGHT; + case 'justify': + return AlignmentType.JUSTIFIED; + default: + return AlignmentType.LEFT; + } + } + + private static createParagraph( + cheerioAPI: cheerio.CheerioAPI, + elem: BasicAcceptedElems, + textAlignment: string, + colorParsed: string, + backgroundColor: string, + size: string, + isListParagraph: boolean, + options?: InstructionParagraph + ): Paragraph { + const children: ParagraphChild[] = []; + if (options?.marker) { + children.push(new TextRun({ + children: [options.marker, new Tab()], size: CodebookDocxGenerator.getFontSize(size) + })); + } + const childNodes = CodebookDocxGenerator.getChildren(cheerioAPI, elem); + CodebookDocxGenerator.processInlineElements( + childNodes, + children, + colorParsed, + backgroundColor, + size, + options?.element.name.match(/^h[1-3]$/) ? { bold: true } : {}, + options?.imageWidth + ); + const normalizedChildren = CodebookDocxGenerator.normalizeParagraphChildren( + children, + textAlignment + ); + const styles = options ? CodebookDocxGenerator.inlineStyles(options.element) : {}; + const pixels = (value: string | undefined) => { + const amount = parseFloat(value || ''); + return Number.isFinite(amount) ? Math.round(amount * (value?.endsWith('pt') ? 20 : 15)) : undefined; + }; + const listStart = (options?.listDepth || 0) * 600 + (options?.listIndent || 0); + const start = (pixels(styles['padding-left']) ?? (isListParagraph ? 0 : 100)) + listStart + (options?.quoteDepth || 0) * 600; + const textIndent = pixels(styles['text-indent']) || 0; + const indent = options?.marker ? { start, hanging: 300 } : { + start, + end: 100, + ...(textIndent < 0 ? { hanging: -textIndent } : {}), + ...(textIndent > 0 ? { firstLine: textIndent } : {}) + }; + return new Paragraph({ + alignment: CodebookDocxGenerator.getAlignment(textAlignment), + spacing: { + before: pixels(styles['margin-top']) ?? 100, + after: pixels(styles['margin-bottom']) ?? 100 + }, + indent: isListParagraph ? undefined : indent, + bullet: isListParagraph ? { level: 0 } : undefined, + ...(options?.marker ? { tabStops: [{ type: TabStopType.LEFT, position: start }] } : {}), + children: normalizedChildren + }); + } + + private static parseCssRgbString(input: string) { + const parts = input + ?.replace(/rgba?\(([^)]+)\)/, '$1') + .split(/[,\s/]+/) + .filter(Boolean); + if (parts?.length < 3) { + return; + } + + const parseValue = (value: string, max: number) => { + // eslint-disable-next-line no-param-reassign + value = value.trim(); + + if (value.endsWith('%')) { + return Math.min((Number.parseFloat(value) * max) / 100, max); + } + return Math.min(Number.parseFloat(value), max); + }; + const red = parseValue(parts[0], 255); + const green = parseValue(parts[1], 255); + const blue = parseValue(parts[2], 255); + + // eslint-disable-next-line consistent-return + return [red, green, blue]; + } + + // Convert RGB color to HEX https://github.com/sindresorhus/rgb-hex + // eslint-disable-next-line no-bitwise + private static toHex = (red: number, green: number, blue: number) => (blue | (green << 8) | (red << 16) | (1 << 24)).toString(16).slice(1); +} diff --git a/projects/ngx-coding-components/codebook-generator/codebook-generation-error.ts b/projects/ngx-coding-components/codebook-generator/codebook-generation-error.ts new file mode 100644 index 0000000..d6f9f32 --- /dev/null +++ b/projects/ngx-coding-components/codebook-generator/codebook-generation-error.ts @@ -0,0 +1,3 @@ +export class CodebookGenerationError extends Error { + override name = 'CodebookGenerationError'; +} diff --git a/projects/ngx-coding-components/codebook-generator/codebook-generator.ts b/projects/ngx-coding-components/codebook-generator/codebook-generator.ts new file mode 100644 index 0000000..cfa4ab9 --- /dev/null +++ b/projects/ngx-coding-components/codebook-generator/codebook-generator.ts @@ -0,0 +1,92 @@ +import { ToTextFactory } from '@iqb/responses'; +import { CodingScheme, CodeData, VariableCodingData } from '@iqbspecs/coding-scheme'; +import type { + BookVariable, CodeBookContentSetting, CodebookUnitDto, CodeInfo, Missing, UnitPropertiesForCodebook +} from '@iqb/ngx-coding-components/codebook-models'; +import { CodebookGenerationError } from './codebook-generation-error'; +import { hasCodebookManualInstruction } from './manual-instruction'; + +export class CodebookGenerator { + static getCodebookData(units: UnitPropertiesForCodebook[], options: CodeBookContentSetting, missings: Missing[]): CodebookUnitDto[] { + return [...units].sort((a, b) => (a.key < b.key ? -1 : Number(a.key > b.key))).map(unit => { + let variables: VariableCodingData[] = []; + if (unit.scheme) { + try { + const data = JSON.parse(unit.scheme); + if (!Array.isArray(data.variableCodings) || data.variableCodings.some((variable: VariableCodingData) => !variable || typeof variable.id !== 'string' || + (variable.codes !== undefined && (!Array.isArray(variable.codes) || variable.codes.some(code => !code || typeof code !== 'object'))))) { + throw new Error('Ungültige variableCodings'); + } + variables = new CodingScheme(unit.scheme).variableCodings; + } catch { + throw new CodebookGenerationError(`Ungültiges Kodierschema für Aufgabe ${unit.key}.`); + } + } + const bookVariables = variables.map(variable => this.getVariable(variable, options)) + .filter((variable): variable is BookVariable => variable !== null) + .sort((a, b) => (a.id < b.id ? -1 : Number(a.id > b.id))); + return { + key: unit.key, name: unit.name, variables: bookVariables, missings, items: unit.metadata?.items || [] + }; + }); + } + + static async generateCodebook(units: UnitPropertiesForCodebook[], options: CodeBookContentSetting, missings: Missing[]): Promise { + const data = this.getCodebookData(units, options, missings); + if (options.exportFormat === 'docx') { + const { CodebookDocxGenerator } = await import('./codebook-docx-generator'); + return CodebookDocxGenerator.generateDocx(data, options); + } + return new Blob([JSON.stringify(data.map(({ + key, name, variables, missings: unitMissings + }) => ({ + key, name, variables, missings: unitMissings + })))], { type: 'application/json' }); + } + + private static isClosed(code: CodeData): boolean { + return code.type === 'RESIDUAL_AUTO' || code.type === 'INTENDED_INCOMPLETE'; + } + + private static isManual(code: CodeData): boolean { + return !this.isClosed(code) && hasCodebookManualInstruction(code.manualInstruction); + } + + private static getVariable(variable: VariableCodingData, options: CodeBookContentSetting): BookVariable | null { + const trainingRequired = variable.processing?.includes('CODER_TRAINING_REQUIRED') ?? false; + if (options.trainingRequirement === 'required' && !trainingRequired) return null; + if (options.trainingRequirement === 'not-required' && trainingRequired) return null; + if (variable.sourceType === 'BASE_NO_VALUE' || (variable.sourceType !== 'BASE' && !options.hasDerivedVars)) return null; + const codes = (variable.codes || []).filter(code => code.id !== undefined && code.id !== null); + const manual = codes.some(code => this.isManual(code)); + const closed = codes.some(code => this.isClosed(code)); + if ((options.hasOnlyManualCoding || options.hasClosedVars) && + !((options.hasOnlyManualCoding && manual) || (options.hasClosedVars && closed))) return null; + const exportedCodes = options.hasOnlyManualCoding && !options.hasClosedVars ? codes.filter(code => this.isManual(code)) : codes; + if (options.hasOnlyVarsWithCodes && !exportedCodes.length) return null; + return { + id: variable.alias || variable.id, + label: variable.label || '', + sourceType: variable.sourceType, + generalInstruction: options.hasGeneralInstructions ? variable.manualInstruction || '' : '', + codes: exportedCodes.map(code => this.getCode(code, options)) + }; + } + + private static getCode(code: CodeData, options: CodeBookContentSetting): CodeInfo { + const fallback: CodeInfo = { id: `${code.id}`, label: '', description: '

Kodierschema mit Schemer Version ab 1.5 erzeugen!

' }; + if (options.showScore) fallback.score = ''; + if (Object.prototype.hasOwnProperty.call(code, 'rules')) return fallback; + try { + const text = ToTextFactory.codeAsText(code, 'SIMPLE'); + const rules = options.hasOnlyManualCoding && !options.hasClosedVars ? '' : + (text.ruleSetDescriptions || []).filter(rule => rule !== 'Keine Regeln definiert.' || !code.manualInstruction) + .map(rule => `

${rule}

`).join(''); + const result: CodeInfo = { id: `${code.id}`, label: options.codeLabelToUpper ? text.label.toUpperCase() : text.label, description: `${rules}${code.manualInstruction || ''}` }; + if (options.showScore) result.score = `${text.score}`; + return result; + } catch { + return fallback; + } + } +} diff --git a/projects/ngx-coding-components/codebook-generator/manual-instruction.ts b/projects/ngx-coding-components/codebook-generator/manual-instruction.ts new file mode 100644 index 0000000..7f7d38d --- /dev/null +++ b/projects/ngx-coding-components/codebook-generator/manual-instruction.ts @@ -0,0 +1,13 @@ +import { load } from 'cheerio'; + +/** Rich-text instructions may consist entirely of an image or a formula. */ +export function hasCodebookManualInstruction(instruction?: string | null): boolean { + if (!instruction) return false; + const $ = load(instruction); + $('script,style').remove(); + const text = $.root().text().replace(/[\s\p{Default_Ignorable_Code_Point}]/gu, ''); + if (text) return true; + return $('img').toArray().some(element => /^data:image\/(png|jpeg|jpg|gif|bmp);base64,\S+$/i.test($(element).attr('src') || '')) || + $('span.iqb-math-formula').toArray().some(element => !!$(element).attr('data-latex')?.trim()) || + $('aspect-nodeview-math-formula').toArray().some(element => !!$(element).attr('formula')?.trim()); +} diff --git a/projects/ngx-coding-components/codebook-generator/ng-package.json b/projects/ngx-coding-components/codebook-generator/ng-package.json new file mode 100644 index 0000000..ed27894 --- /dev/null +++ b/projects/ngx-coding-components/codebook-generator/ng-package.json @@ -0,0 +1,6 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "lib": { + "entryFile": "public-api.ts" + } +} diff --git a/projects/ngx-coding-components/codebook-generator/public-api.ts b/projects/ngx-coding-components/codebook-generator/public-api.ts new file mode 100644 index 0000000..017d25a --- /dev/null +++ b/projects/ngx-coding-components/codebook-generator/public-api.ts @@ -0,0 +1,5 @@ +export * from './codebook-generator'; +export * from './codebook-docx-generator'; +export * from './manual-instruction'; +export type * from '@iqb/ngx-coding-components/codebook-models'; +export * from './codebook-generation-error'; diff --git a/projects/ngx-coding-components/codebook-generator/web-colors.ts b/projects/ngx-coding-components/codebook-generator/web-colors.ts new file mode 100644 index 0000000..df9d391 --- /dev/null +++ b/projects/ngx-coding-components/codebook-generator/web-colors.ts @@ -0,0 +1,1275 @@ +/** + * The CSS colour names and their hex values, for turning a colour a user picked by name into + * something a generated document can paint with. + */ +export class WebColors { + /** + * The hex value for a CSS colour name, or an empty string when the name is unknown. The name has + * to be given in lower case -- the table is compared against it as it comes in. + */ + static getHexFromWebColor(colorName: string) { + const foundColor = WebColors.colors + .find(color => color.name.toLowerCase() === colorName); + return foundColor ? foundColor.hex : ''; + } + + /** The table itself: name, hex and rgb for every CSS colour. */ + static colors = [ + { + name: 'Lavender', + hex: 'E6E6FA', + rgb: { + r: 230, + g: 230, + b: 250 + } + }, + { + name: 'Thistle', + hex: 'D8BFD8', + rgb: { r: 216, g: 191, b: 216 } + }, + { + name: 'Plum', + hex: 'DDA0DD', + rgb: { + r: 221, + g: 160, + b: 221 + } + }, + { + name: 'Violet', + hex: 'EE82EE', + rgb: { + r: 238, + g: 130, + b: 238 + } + }, + { + name: 'Orchid', + hex: 'DA70D6', + rgb: { + r: 218, + g: 112, + b: 214 + } + }, + { + name: 'Fuchsia', + hex: 'FF00FF', + rgb: { + r: 255, + g: 0, + b: 255 + } + }, + { + name: 'Magenta', + hex: 'FF00FF', + rgb: { + r: 255, + g: 0, + b: 255 + } + }, + { + name: 'MediumOrchid', + hex: 'BA55D3', + rgb: { + r: 186, + g: 85, + b: 211 + } + }, + { + name: 'MediumPurple', + hex: '9370DB', + rgb: { + r: 147, + g: 112, + b: 219 + } + }, + { + name: 'BlueViolet', + hex: '8A2BE2', + rgb: { + r: 138, + g: 43, + b: 226 + } + }, + { + name: 'DarkViolet', + hex: '9400D3', + rgb: { + r: 148, + g: 0, + b: 211 + } + }, + { + name: 'DarkOrchid', + hex: '9932CC', + rgb: { + r: 153, + g: 50, + b: 204 + } + }, + { + name: 'DarkMagenta', + hex: '8B008B', + rgb: { + r: 139, + g: 0, + b: 139 + } + }, + { + name: 'Purple', + hex: '800080', + rgb: { + r: 128, + g: 0, + b: 128 + } + }, + { + name: 'Indigo', + hex: '4B0082', + rgb: { + r: 75, + g: 0, + b: 130 + } + }, + { + name: 'DarkSlateBlue', + hex: '483D8B', + rgb: { + r: 72, + g: 61, + b: 139 + } + }, + { + name: 'SlateBlue', + hex: '6A5ACD', + rgb: { + r: 106, + g: 90, + b: 205 + } + }, + { + name: 'MediumSlateBlue', + hex: '7B68EE', + rgb: { + r: 123, + g: 104, + b: 238 + } + }, + { + name: 'Pink', + hex: 'FFC0CB', + rgb: { + r: 255, + g: 192, + b: 203 + } + }, + { + name: 'LightPink', + hex: 'FFB6C1', + rgb: { + r: 255, + g: 182, + b: 193 + } + }, + { + name: 'HotPink', + hex: 'FF69B4', + rgb: { + r: 255, + g: 105, + b: 180 + } + }, + { + name: 'DeepPink', + hex: 'FF1493', + rgb: { + r: 255, + g: 20, + b: 147 + } + }, + { + name: 'PaleVioletRed', + hex: 'DB7093', + rgb: { + r: 219, + g: 112, + b: 147 + } + }, + { + name: 'MediumVioletRed', + hex: 'C71585', + rgb: { + r: 199, + g: 21, + b: 133 + } + }, + { + name: 'LightSalmon', + hex: 'FFA07A', + rgb: { + r: 255, + g: 160, + b: 122 + } + }, + { + name: 'Salmon', + hex: 'FA8072', + rgb: { + r: 250, + g: 128, + b: 114 + } + }, + { + name: 'DarkSalmon', + hex: 'E9967A', + rgb: { + r: 233, + g: 150, + b: 122 + } + }, + { + name: 'LightCoral', + hex: 'F08080', + rgb: { + r: 240, + g: 128, + b: 128 + } + }, + { + name: 'IndianRed', + hex: 'CD5C5C', + rgb: { + r: 205, + g: 92, + b: 92 + } + }, + { + name: 'Crimson', + hex: 'DC143C', + rgb: { + r: 220, + g: 20, + b: 60 + } + }, + { + name: 'FireBrick', + hex: 'B22222', + rgb: { + r: 178, + g: 34, + b: 34 + } + }, + { + name: 'DarkRed', + hex: '8B0000', + rgb: { + r: 139, + g: 0, + b: 0 + } + }, + { + name: 'Red', + hex: 'FF0000', + rgb: { + r: 255, + g: 0, + b: 0 + } + }, + { + name: 'OrangeRed', + hex: 'FF4500', + rgb: { + r: 255, + g: 69, + b: 0 + } + }, + { + name: 'Tomato', + hex: 'FF6347', + rgb: { + r: 255, + g: 99, + b: 71 + } + }, + { + name: 'Coral', + hex: 'FF7F50', + rgb: { + r: 255, + g: 127, + b: 80 + } + }, + { + name: 'DarkOrange', + hex: 'FF8C00', + rgb: { + r: 255, + g: 140, + b: 0 + } + }, + { + name: 'Orange', + hex: 'FFA500', + rgb: { + r: 255, + g: 165, + b: 0 + } + }, + { + name: 'Yellow', + hex: 'FFFF00', + rgb: { + r: 255, + g: 255, + b: 0 + } + }, + { + name: 'LightYellow', + hex: 'FFFFE0', + rgb: { + r: 255, + g: 255, + b: 224 + } + }, + { + name: 'LemonChiffon', + hex: 'FFFACD', + rgb: { + r: 255, + g: 250, + b: 205 + } + }, + { + name: 'LightGoldenrodYellow', + hex: 'FAFAD2', + rgb: { + r: 250, + g: 250, + b: 210 + } + }, + { + name: 'PapayaWhip', + hex: 'FFEFD5', + rgb: { + r: 255, + g: 239, + b: 213 + } + }, + { + name: 'Moccasin', + hex: 'FFE4B5', + rgb: { + r: 255, + g: 228, + b: 181 + } + }, + { + name: 'PeachPuff', + hex: 'FFDAB9', + rgb: { + r: 255, + g: 218, + b: 185 + } + }, + { + name: 'PaleGoldenrod', + hex: 'EEE8AA', + rgb: { + r: 238, + g: 232, + b: 170 + } + }, + { + name: 'Khaki', + hex: 'F0E68C', + rgb: { + r: 240, + g: 230, + b: 140 + } + }, + { + name: 'DarkKhaki', + hex: 'BDB76B', + rgb: { + r: 189, + g: 183, + b: 107 + } + }, + { + name: 'Gold', + hex: 'FFD700', + rgb: { + r: 255, + g: 215, + b: 0 + } + }, + { + name: 'Cornsilk', + hex: 'FFF8DC', + rgb: { + r: 255, + g: 248, + b: 220 + } + }, + { + name: 'BlanchedAlmond', + hex: 'FFEBCD', + rgb: { + r: 255, + g: 235, + b: 205 + } + }, + { + name: 'Bisque', + hex: 'FFE4C4', + rgb: { + r: 255, + g: 228, + b: 196 + } + }, + { + name: 'NavajoWhite', + hex: 'FFDEAD', + rgb: { + r: 255, + g: 222, + b: 173 + } + }, + { + name: 'Wheat', + hex: 'F5DEB3', + rgb: { + r: 245, + g: 222, + b: 179 + } + }, + { + name: 'BurlyWood', + hex: 'DEB887', + rgb: { + r: 222, + g: 184, + b: 135 + } + }, + { + name: 'Tan', + hex: 'D2B48C', + rgb: { + r: 210, + g: 180, + b: 140 + } + }, + { + name: 'RosyBrown', + hex: 'BC8F8F', + rgb: { + r: 188, + g: 143, + b: 143 + } + }, + { + name: 'SandyBrown', + hex: 'F4A460', + rgb: { + r: 244, + g: 164, + b: 96 + } + }, + { + name: 'Goldenrod', + hex: 'DAA520', + rgb: { + r: 218, + g: 165, + b: 32 + } + }, + { + name: 'DarkGoldenrod', + hex: 'B8860B', + rgb: { + r: 184, + g: 134, + b: 11 + } + }, + { + name: 'Peru', + hex: 'CD853F', + rgb: { + r: 205, + g: 133, + b: 63 + } + }, + { + name: 'Chocolate', + hex: 'D2691E', + rgb: { + r: 210, + g: 105, + b: 30 + } + }, + { + name: 'SaddleBrown', + hex: '8B4513', + rgb: { + r: 139, + g: 69, + b: 19 + } + }, + { + name: 'Sienna', + hex: 'A0522D', + rgb: { + r: 160, + g: 82, + b: 45 + } + }, + { + name: 'Brown', + hex: 'A52A2A', + rgb: { + r: 165, + g: 42, + b: 42 + } + }, + { + name: 'Maroon', + hex: '800000', + rgb: { + r: 128, + g: 0, + b: 0 + } + }, + { + name: 'DarkOliveGreen', + hex: '556B2F', + rgb: { + r: 85, + g: 107, + b: 47 + } + }, + { + name: 'Olive', + hex: '808000', + rgb: { + r: 128, + g: 128, + b: 0 + } + }, + { + name: 'OliveDrab', + hex: '6B8E23', + rgb: { + r: 107, + g: 142, + b: 35 + } + }, + { + name: 'YellowGreen', + hex: '9ACD32', + rgb: { + r: 154, + g: 205, + b: 50 + } + }, + { + name: 'LimeGreen', + hex: '32CD32', + rgb: { + r: 50, + g: 205, + b: 50 + } + }, + { + name: 'Lime', + hex: '00FF00', + rgb: { + r: 0, + g: 255, + b: 0 + } + }, + { + name: 'LawnGreen', + hex: '7CFC00', + rgb: { + r: 124, + g: 252, + b: 0 + } + }, + { + name: 'Chartreuse', + hex: '7FFF00', + rgb: { + r: 127, + g: 255, + b: 0 + } + }, + { + name: 'GreenYellow', + hex: 'ADFF2F', + rgb: { + r: 173, + g: 255, + b: 47 + } + }, + { + name: 'SpringGreen', + hex: '00FF7F', + rgb: { + r: 0, + g: 255, + b: 127 + } + }, + { + name: 'MediumSpringGreen', + hex: '00FA9A', + rgb: { + r: 0, + g: 250, + b: 154 + } + }, + { + name: 'LightGreen', + hex: '90EE90', + rgb: { + r: 144, + g: 238, + b: 144 + } + }, + { + name: 'PaleGreen', + hex: '98FB98', + rgb: { + r: 152, + g: 251, + b: 152 + } + }, + { + name: 'DarkSeaGreen', + hex: '8FBC8F', + rgb: { + r: 143, + g: 188, + b: 143 + } + }, + { + name: 'MediumSeaGreen', + hex: '3CB371', + rgb: { + r: 60, + g: 179, + b: 113 + } + }, + { + name: 'SeaGreen', + hex: '2E8B57', + rgb: { + r: 46, + g: 139, + b: 87 + } + }, + { + name: 'ForestGreen', + hex: '228B22', + rgb: { + r: 34, + g: 139, + b: 34 + } + }, + { + name: 'Green', + hex: '008000', + rgb: { + r: 0, + g: 128, + b: 0 + } + }, + { + name: 'DarkGreen', + hex: '006400', + rgb: { + r: 0, + g: 100, + b: 0 + } + }, + { + name: 'MediumAquamarine', + hex: '66CDAA', + rgb: { + r: 102, + g: 205, + b: 170 + } + }, + { + name: 'Aqua', + hex: '00FFFF', + rgb: { + r: 0, + g: 255, + b: 255 + } + }, + { + name: 'Cyan', + hex: '00FFFF', + rgb: { + r: 0, + g: 255, + b: 255 + } + }, + { + name: 'LightCyan', + hex: 'E0FFFF', + rgb: { + r: 224, + g: 255, + b: 255 + } + }, + { + name: 'PaleTurquoise', + hex: 'AFEEEE', + rgb: { + r: 175, + g: 238, + b: 238 + } + }, + { + name: 'Aquamarine', + hex: '7FFFD4', + rgb: { + r: 127, + g: 255, + b: 212 + } + }, + { + name: 'Turquoise', + hex: '40E0D0', + rgb: { + r: 64, + g: 224, + b: 208 + } + }, + { + name: 'MediumTurquoise', + hex: '48D1CC', + rgb: { + r: 72, + g: 209, + b: 204 + } + }, + { + name: 'DarkTurquoise', + hex: '00CED1', + rgb: { + r: 0, + g: 206, + b: 209 + } + }, + { + name: 'LightSeaGreen', + hex: '20B2AA', + rgb: { + r: 32, + g: 178, + b: 170 + } + }, + { + name: 'CadetBlue', + hex: '5F9EA0', + rgb: { + r: 95, + g: 158, + b: 160 + } + }, + { + name: 'DarkCyan', + hex: '008B8B', + rgb: { + r: 0, + g: 139, + b: 139 + } + }, + { + name: 'Teal', + hex: '008080', + rgb: { + r: 0, + g: 128, + b: 128 + } + }, + { + name: 'LightSteelBlue', + hex: 'B0C4DE', + rgb: { + r: 176, + g: 196, + b: 222 + } + }, + { + name: 'PowderBlue', + hex: 'B0E0E6', + rgb: { + r: 176, + g: 224, + b: 230 + } + }, + { + name: 'LightBlue', + hex: 'ADD8E6', + rgb: { + r: 173, + g: 216, + b: 230 + } + }, + { + name: 'SkyBlue', + hex: '87CEEB', + rgb: { + r: 135, + g: 206, + b: 235 + } + }, + { + name: 'LightSkyBlue', + hex: '87CEFA', + rgb: { + r: 135, + g: 206, + b: 250 + } + }, + { + name: 'DeepSkyBlue', + hex: '00BFFF', + rgb: { + r: 0, + g: 191, + b: 255 + } + }, + { + name: 'DodgerBlue', + hex: '1E90FF', + rgb: { + r: 30, + g: 144, + b: 255 + } + }, + { + name: 'CornflowerBlue', + hex: '6495ED', + rgb: { + r: 100, + g: 149, + b: 237 + } + }, + { + name: 'SteelBlue', + hex: '4682B4', + rgb: { + r: 70, + g: 130, + b: 180 + } + }, + { + name: 'RoyalBlue', + hex: '4169E1', + rgb: { + r: 65, + g: 105, + b: 225 + } + }, + { + name: 'Blue', + hex: '0000FF', + rgb: { + r: 0, + g: 0, + b: 255 + } + }, + { + name: 'MediumBlue', + hex: '0000CD', + rgb: { + r: 0, + g: 0, + b: 205 + } + }, + { + name: 'DarkBlue', + hex: '00008B', + rgb: { + r: 0, + g: 0, + b: 139 + } + }, + { + name: 'Navy', + hex: '000080', + rgb: { + r: 0, + g: 0, + b: 128 + } + }, + { + name: 'MidnightBlue', + hex: '191970', + rgb: { + r: 25, + g: 25, + b: 112 + } + }, + { + name: 'White', + hex: 'FFFFFF', + rgb: { + r: 255, + g: 255, + b: 255 + } + }, + { + name: 'Snow', + hex: 'FFFAFA', + rgb: { + r: 255, + g: 250, + b: 250 + } + }, + { + name: 'Honeydew', + hex: 'F0FFF0', + rgb: { + r: 240, + g: 255, + b: 240 + } + }, + { + name: 'MintCream', + hex: 'F5FFFA', + rgb: { + r: 245, + g: 255, + b: 250 + } + }, + { + name: 'Azure', + hex: 'F0FFFF', + rgb: { + r: 240, + g: 255, + b: 255 + } + }, + { + name: 'AliceBlue', + hex: 'F0F8FF', + rgb: { + r: 240, + g: 248, + b: 255 + } + }, + { + name: 'GhostWhite', + hex: 'F8F8FF', + rgb: { + r: 248, + g: 248, + b: 255 + } + }, + { + name: 'WhiteSmoke', + hex: 'F5F5F5', + rgb: { + r: 245, + g: 245, + b: 245 + } + }, + { + name: 'Seashell', + hex: 'FFF5EE', + rgb: { + r: 255, + g: 245, + b: 238 + } + }, + { + name: 'Beige', + hex: 'F5F5DC', + rgb: { + r: 245, + g: 245, + b: 220 + } + }, + { + name: 'OldLace', + hex: 'FDF5E6', + rgb: { + r: 253, + g: 245, + b: 230 + } + }, + { + name: 'FloralWhite', + hex: 'FFFAF0', + rgb: { + r: 255, + g: 250, + b: 240 + } + }, + { + name: 'Ivory', + hex: 'FFFFF0', + rgb: { + r: 255, + g: 255, + b: 240 + } + }, + { + name: 'AntiqueWhite', + hex: 'FAEBD7', + rgb: { + r: 250, + g: 235, + b: 215 + } + }, + { + name: 'Linen', + hex: 'FAF0E6', + rgb: { + r: 250, + g: 240, + b: 230 + } + }, + { + name: 'LavenderBlush', + hex: 'FFF0F5', + rgb: { + r: 255, + g: 240, + b: 245 + } + }, + { + name: 'MistyRose', + hex: 'FFE4E1', + rgb: { + r: 255, + g: 228, + b: 225 + } + }, + { + name: 'Gainsboro', + hex: 'DCDCDC', + rgb: { + r: 220, + g: 220, + b: 220 + } + }, + { + name: 'LightGray', + hex: 'D3D3D3', + rgb: { + r: 211, + g: 211, + b: 211 + } + }, + { + name: 'Silver', + hex: 'C0C0C0', + rgb: { + r: 192, + g: 192, + b: 192 + } + }, + { + name: 'DarkGray', + hex: 'A9A9A9', + rgb: { + r: 169, + g: 169, + b: 169 + } + }, + { + name: 'Gray', + hex: '808080', + rgb: { + r: 128, + g: 128, + b: 128 + } + }, + { + name: 'DimGray', + hex: '696969', + rgb: { + r: 105, + g: 105, + b: 105 + } + }, + { + name: 'LightSlateGray', + hex: '778899', + rgb: { + r: 119, + g: 136, + b: 153 + } + }, + { + name: 'SlateGray', + hex: '708090', + rgb: { + r: 112, + g: 128, + b: 144 + } + }, + { + name: 'DarkSlateGray', + hex: '2F4F4F', + rgb: { + r: 47, + g: 79, + b: 79 + } + }, + { + name: 'Black', + hex: '000000', + rgb: { + r: 0, + g: 0, + b: 0 + } + } + ]; +} diff --git a/projects/ngx-coding-components/codebook-models/ng-package.json b/projects/ngx-coding-components/codebook-models/ng-package.json new file mode 100644 index 0000000..ed27894 --- /dev/null +++ b/projects/ngx-coding-components/codebook-models/ng-package.json @@ -0,0 +1,6 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "lib": { + "entryFile": "public-api.ts" + } +} diff --git a/projects/ngx-coding-components/codebook-models/public-api.ts b/projects/ngx-coding-components/codebook-models/public-api.ts new file mode 100644 index 0000000..9f1bcc1 --- /dev/null +++ b/projects/ngx-coding-components/codebook-models/public-api.ts @@ -0,0 +1,159 @@ +import { VariableInfo } from '@iqbspecs/variable-info/variable-info.interface'; + +/** + * Item metadata for codebook + */ +export interface ItemMetadata { + id: string; + variableId: string; +} + +/** + * Settings for codebook content generation + */ +export type CodebookTrainingRequirementFilter = 'all' | 'required' | 'not-required'; + +export interface CodeBookContentSetting { + /** Export format (docx or json) */ + exportFormat: 'json' | 'docx'; + /** Missings profile name */ + missingsProfile: string; + /** Include only manual coding */ + hasOnlyManualCoding: boolean; + /** Include general instructions */ + hasGeneralInstructions: boolean; + /** Include derived variables */ + hasDerivedVars: boolean; + /** Include only variables with codes */ + hasOnlyVarsWithCodes: boolean; + /** Include closed variables */ + hasClosedVars: boolean; + /** Convert code labels to uppercase */ + codeLabelToUpper: boolean; + /** Show score */ + showScore: boolean; + /** Hide item-variable relation */ + hideItemVarRelation: boolean; + /** Filter variables by increased coder training requirement; omission means all. */ + trainingRequirement?: CodebookTrainingRequirementFilter; +} + +/** + * Missing code definition + */ +export interface Missing { + /** Missing code */ + code: string | number; + /** Missing label */ + label: string; + /** Missing description */ + description: string; +} + +/** + * Code information for codebook + */ +export interface CodeInfo { + /** Code ID */ + id: string; + /** Code label */ + label: string; + /** Code description */ + description: string; + /** Code score (optional) */ + score?: string; +} + +/** + * Variable information for codebook + */ +export interface BookVariable { + /** Variable ID */ + id: string; + /** Variable label */ + label: string; + /** Variable source type */ + sourceType: string; + /** General instruction */ + generalInstruction: string; + /** Codes */ + codes: CodeInfo[]; +} + +/** + * Unit data for codebook + */ +export interface CodebookUnitDto { + /** Unit key */ + key: string; + /** Unit name */ + name: string; + /** Variables */ + variables: BookVariable[]; + /** Missings */ + missings: Missing[]; + /** Items (optional) */ + items?: ItemMetadata[]; +} + +/** + * Unit properties for codebook generation + */ +export interface UnitPropertiesForCodebook { + /** Unit ID */ + id: number; + /** Unit key */ + key: string; + /** Unit name */ + name: string; + /** Coding scheme */ + scheme?: string; + /** Scheme type */ + schemeType?: string; + /** Metadata */ + metadata?: { + /** Items */ + items?: ItemMetadata[]; + }; + /** Variables */ + variables?: VariableInfo[]; +} + +/** + * Unit selection item for codebook export UI + */ +export interface UnitSelectionItem { + /** Unit ID */ + unitId: number; + /** Unit name */ + unitName: string; + /** Unit alias */ + unitAlias?: string | null; + key: string; + groupName?: string; + disabled?: boolean; +} + +/** + * Missings profile for selection + */ +export interface MissingsProfile { + /** Profile ID */ + id: number; + /** Profile label */ + label: string; + /** Missings data */ + missings?: Missing[] | string; +} + +/** + * Codebook export configuration + */ +export interface CodebookExportConfig { + /** Selected unit IDs */ + selectedUnits: number[]; + /** Content options */ + contentOptions: CodeBookContentSetting; + /** Selected missings profile ID */ + missingsProfileId: number; +} diff --git a/projects/ngx-coding-components/ng-package.json b/projects/ngx-coding-components/ng-package.json index ee0f04d..5b04d68 100644 --- a/projects/ngx-coding-components/ng-package.json +++ b/projects/ngx-coding-components/ng-package.json @@ -3,7 +3,13 @@ "dest": "../../dist/ngx-coding-components", "allowedNonPeerDependencies": [ "@iqb/mathlive", - "katex" + "katex", + "docx", + "cheerio", + "buffer", + "image-size", + "mathml2omml", + "@iqb/responses" ], "lib": { "entryFile": "src/public-api.ts" diff --git a/projects/ngx-coding-components/package.json b/projects/ngx-coding-components/package.json index 6c753d2..9097043 100644 --- a/projects/ngx-coding-components/package.json +++ b/projects/ngx-coding-components/package.json @@ -4,8 +4,8 @@ "exports": { "./lib/ngx-coding-components.module": "./lib/ngx-coding-components.module.d.ts" }, - "version": "4.0.0", - "author": "IQB - Institut zur Qualitätsentwicklung im Bildungswesen", + "version": "4.1.0", + "author": "IQB - Institut zur Qualit\u00e4tsentwicklung im Bildungswesen", "license": "MIT", "description": "Angular components for coding responses in assessments of IQB.", "repository": { @@ -29,9 +29,8 @@ "@angular/platform-browser": "^21.0.0", "@angular/platform-browser-dynamic": "^21.0.0", "@angular/router": "^21.0.0", - "@iqb/responses": "^5.2.2", "@iqbspecs/coding-scheme": "^3.4.1", - "@iqbspecs/response": "^2.0.0", + "@iqbspecs/response": "^1.5.1 || ^2.0.0", "@iqbspecs/variable-info": "^1.3.0", "@ngx-translate/core": "^15.0.0", "@tiptap/core": "^2.23.1", @@ -72,7 +71,26 @@ "dependencies": { "@iqb/mathlive": "^0.4.1", "katex": "^0.16.11", - "tslib": "^2.3.0" + "tslib": "^2.3.0", + "docx": "8.5.0", + "cheerio": "1.1.2", + "buffer": "6.0.3", + "image-size": "1.2.1", + "mathml2omml": "0.5.0", + "@iqb/responses": "5.2.2" }, - "sideEffects": false + "sideEffects": false, + "typesVersions": { + "*": { + "codebook-models": [ + "types/iqb-ngx-coding-components-codebook-models.d.ts" + ], + "codebook-generator": [ + "types/iqb-ngx-coding-components-codebook-generator.d.ts" + ], + "codebook-export": [ + "types/iqb-ngx-coding-components-codebook-export.d.ts" + ] + } + } } diff --git a/projects/ngx-coding-components/src/lib/codebook-export/codebook-export.component.spec.ts b/projects/ngx-coding-components/src/lib/codebook-export/codebook-export.component.spec.ts new file mode 100644 index 0000000..d4a5019 --- /dev/null +++ b/projects/ngx-coding-components/src/lib/codebook-export/codebook-export.component.spec.ts @@ -0,0 +1,118 @@ +import { SimpleChange } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { CodebookExportComponent } from '../../../codebook-export/codebook-export.component'; + +describe('Studio codebook selection contract', () => { + let component: CodebookExportComponent; + beforeEach(() => { + component = new CodebookExportComponent(); + component.availableUnits = [ + { + unitId: 1, key: 'A', unitName: 'Alpha', groupName: 'Gruppe' + }, + { unitId: 2, key: 'B', unitName: 'Beta' }, + { + unitId: 3, key: 'C', unitName: 'Gesperrt', disabled: true + } + ]; + component.ngOnChanges({ availableUnits: new SimpleChange(null, component.availableUnits, true) }); + }); + + it('selects all available units across search results, excluding disabled units', () => { + component.filter = 'Alpha'; component.applyFilter(); component.toggleAll(); + expect(component.selection).toEqual([1, 2]); + expect(component.allSelected).toBeTrue(); + component.toggleAll(); expect(component.selection).toEqual([]); + }); + + it('searches names, keys and groups without changing selection', () => { + component.toggle(component.availableUnits[1]); + component.filter = 'gruppe'; component.applyFilter(); + expect(component.selection).toEqual([2]); + expect(component.dataSource.filteredData.map(unit => unit.unitId)).toEqual([1]); + }); + + it('accepts initial selection and removes unavailable or disabled IDs', () => { + component.selectedUnitIds = [1, 3, 999]; + component.ngOnChanges({ selectedUnitIds: new SimpleChange([], component.selectedUnitIds, true) }); + expect(component.selection).toEqual([1]); + }); + + it('emits a copied export configuration without starting a download or a job', () => { + component.toggle(component.availableUnits[0]); + component.missingsProfiles = [{ id: 8, label: 'Profil' }]; component.selectedMissingsProfileId = 8; + const emit = spyOn(component.exportRequested, 'emit'); + component.exportCodingBook(); + expect(emit).toHaveBeenCalledWith(jasmine.objectContaining({ selectedUnits: [1], missingsProfileId: 8 })); + const config = emit.calls.mostRecent().args[0]!; + component.contentOptions.showScore = false; + expect(config.contentOptions.showScore).toBeTrue(); + expect(config.contentOptions.missingsProfile).toBe('Profil'); + }); + + it('does not copy or emit host-specific options', () => { + const hostOptions = { showScore: false, jobDefinitionId: 10, variableBundleIds: [7] }; + component.defaultContentOptions = hostOptions; + component.ngOnChanges({ defaultContentOptions: new SimpleChange(null, hostOptions, true) }); + component.toggle(component.availableUnits[0]); + const emit = spyOn(component.exportRequested, 'emit'); + component.exportCodingBook(); + const options = emit.calls.mostRecent().args[0]!.contentOptions; + expect(options.showScore).toBeFalse(); + expect(Object.keys(options)).not.toContain('variableBundleIds'); + expect(Object.keys(options)).not.toContain('jobDefinitionId'); + }); + + it('exports the shared training filter and supports Studio-only groups', () => { + component.contentOptions.trainingRequirement = 'required'; + component.showGroupColumn = false; + component.ngOnChanges({ showGroupColumn: new SimpleChange(true, false, false) }); + expect(component.columns).toEqual(['select', 'key', 'name']); + component.toggle(component.availableUnits[0]); + const emit = spyOn(component.exportRequested, 'emit'); + component.exportCodingBook(); + expect(emit.calls.mostRecent().args[0]!.contentOptions.trainingRequirement).toBe('required'); + }); + + it('blocks individual and complete selection while loading', () => { + component.loading = true; + const emit = spyOn(component.selectionChanged, 'emit'); + component.toggle(component.availableUnits[0]); component.toggleAll(); + expect(component.selection).toEqual([]); + expect(emit).not.toHaveBeenCalled(); + }); + + it('notifies the host when refreshed units invalidate its selection', () => { + component.toggleAll(); + const emit = spyOn(component.selectionChanged, 'emit'); + component.availableUnits = [component.availableUnits[0]]; + component.ngOnChanges({ availableUnits: new SimpleChange(null, component.availableUnits, false) }); + expect(component.selection).toEqual([1]); + expect(emit).toHaveBeenCalledOnceWith([1]); + }); + + it('blocks export for empty selection, loading, busy and unsaved changes', () => { + const emit = spyOn(component.exportRequested, 'emit'); + component.exportCodingBook(); component.toggle(component.availableUnits[0]); + component.loading = true; component.exportCodingBook(); component.loading = false; + component.busy = true; component.exportCodingBook(); component.busy = false; + component.workspaceChanges = true; component.exportCodingBook(); + expect(emit).not.toHaveBeenCalled(); + }); +}); + +describe('codebook checkbox accessibility', () => { + it('labels the actual checkbox inputs for assistive technology', async () => { + await TestBed.configureTestingModule({ + imports: [CodebookExportComponent, TranslateModule.forRoot()] + }).compileComponents(); + const fixture = TestBed.createComponent(CodebookExportComponent); + fixture.componentRef.setInput('availableUnits', [{ unitId: 1, key: 'TASK', unitName: 'Aufgabe' }]); + fixture.detectChanges(); + const inputs = fixture.nativeElement.querySelectorAll('input[type="checkbox"]'); + expect(inputs[0].getAttribute('aria-label')).toBe('codebook.coding.select-all-units'); + expect(inputs[1].getAttribute('aria-label')).toBe('TASK'); + fixture.destroy(); + }); +}); diff --git a/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/hanging-indent.ts b/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/hanging-indent.ts index 27de450..f239988 100644 --- a/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/hanging-indent.ts +++ b/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/hanging-indent.ts @@ -1,5 +1,5 @@ import { Command, Extension } from '@tiptap/core'; -import { TextSelection, Transaction } from 'prosemirror-state'; +import { AllSelection, TextSelection, Transaction } from 'prosemirror-state'; declare module '@tiptap/core' { interface Commands { @@ -30,11 +30,11 @@ export const HangingIndent = Extension.create({ { style: `text-indent: -${attributes['indentSize']}px` } : { style: 'text-indent: 0px' } ), - parseHTML: element => element.style.textIndent !== '0px' + parseHTML: element => parseFloat(element.style.textIndent) < 0 }, indentSize: { default: 20, - parseHTML: element => Number(element.getAttribute('indentSize')) + parseHTML: element => Number(element.getAttribute('indentSize')) || 20 } } } @@ -57,11 +57,14 @@ export const HangingIndent = Extension.create({ const updateIndentLevel = (tr: Transaction, hangingIndent: boolean, indentSize: number): Transaction => { const { doc, selection } = tr; - if (doc && selection && (selection instanceof TextSelection)) { + if (doc && selection && (selection instanceof TextSelection || selection instanceof AllSelection)) { const { from, to } = selection; doc.nodesBetween(from, to, (node, pos) => { - setNodeIndentMarkup(tr, pos, hangingIndent, indentSize); - return false; + if (this.options.types.includes(node.type.name)) { + setNodeIndentMarkup(tr, pos, hangingIndent, indentSize); + return false; + } + return true; }); } diff --git a/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/indent-selection.spec.ts b/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/indent-selection.spec.ts new file mode 100644 index 0000000..e7397c5 --- /dev/null +++ b/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/indent-selection.spec.ts @@ -0,0 +1,80 @@ +import { Editor } from '@tiptap/core'; +import { Document } from '@tiptap/extension-document'; +import { Text } from '@tiptap/extension-text'; +import { BulletList } from '@tiptap/extension-bullet-list'; +import { ListItem } from '@tiptap/extension-list-item'; +import { Indent } from './indent'; +import { HangingIndent } from './hanging-indent'; +import { ParagraphExtension } from './paragraph-extension'; + +describe('Indent selection', () => { + let editor: Editor; + beforeEach(() => { + editor = new Editor({ + extensions: [Document, ParagraphExtension, Text, BulletList, ListItem, + Indent.configure({ types: ['paragraph', 'listItem'], maxLevel: 4 }), HangingIndent] + }); + }); + afterEach(() => editor.destroy()); + + it('indents every paragraph selected by Select All and retains the result on reload', () => { + editor.commands.setContent('

A

B

'); + editor.commands.selectAll(); + expect(editor.commands.indent(20)).toBe(true); + expect(editor.getJSON().content?.map(node => node.attrs?.['indent'])).toEqual([1, 1]); + const html = editor.getHTML(); + editor.commands.setContent(html); + expect(editor.getHTML()).toBe(html); + editor.commands.selectAll(); + editor.commands.outdent(20); + expect(editor.getJSON().content?.map(node => node.attrs?.['indent'])).toEqual([0, 0]); + }); + + it('applies and removes hanging indent through list containers without altering their nodes', () => { + editor.commands.setContent('
  • A

  • B

'); + editor.commands.selectAll(); + editor.commands.hangIndent(20); + const paragraphs: boolean[] = []; + editor.state.doc.descendants(node => { + if (node.type.name === 'paragraph') paragraphs.push(node.attrs['hangingIndent']); + if (node.type.name === 'bulletList') expect(node.attrs['hangingIndent']).toBeUndefined(); + }); + expect(paragraphs).toEqual([true, true]); + editor.commands.unhangIndent(20); + expect(editor.getHTML()).not.toContain('text-indent: -'); + }); + + it('keeps the default indent size and clamps repeated commands for a text selection', () => { + editor.commands.setContent('

A

'); + expect(editor.getAttributes('paragraph')['indentSize']).toBe(20); + expect(editor.getAttributes('paragraph')['hangingIndent']).toBe(false); + editor.commands.setTextSelection(1); + for (let i = 0; i < 6; i++) editor.commands.indent(20); + expect(editor.getAttributes('paragraph')['indent']).toBe(4); + for (let i = 0; i < 6; i++) editor.commands.outdent(20); + expect(editor.getAttributes('paragraph')['indent']).toBe(0); + }); + + it('sets spacing on filled paragraphs for both a caret and Select All', () => { + editor.commands.setContent('

A

B

'); + editor.commands.setTextSelection(1); + expect(editor.commands.setMargin(10)).toBe(true); + expect(editor.getJSON().content?.map(node => node.attrs?.['margin'])).toEqual([10, 0]); + editor.commands.selectAll(); + expect(editor.commands.setMargin(20)).toBe(true); + expect(editor.getJSON().content?.map(node => node.attrs?.['margin'])).toEqual([20, 20]); + expect(editor.getText()).toBe('A\n\nB'); + }); + + it('sets spacing inside list items and on an empty paragraph', () => { + editor.commands.setContent('
  • A

'); + editor.commands.selectAll(); + editor.commands.setMargin(10); + const margins: number[] = []; + editor.state.doc.descendants(node => { + if (node.type.name === 'paragraph') margins.push(node.attrs['margin']); + else expect(node.attrs['margin']).toBeUndefined(); + }); + expect(margins).toEqual([10, 10]); + }); +}); diff --git a/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/indent.ts b/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/indent.ts index 6faf3fd..150b380 100644 --- a/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/indent.ts +++ b/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/indent.ts @@ -1,5 +1,5 @@ import { Command, Extension } from '@tiptap/core'; -import { TextSelection, Transaction } from 'prosemirror-state'; +import { AllSelection, TextSelection, Transaction } from 'prosemirror-state'; export interface IndentOptions { types: string[]; @@ -44,7 +44,7 @@ export const Indent = Extension.create({ }, indentSize: { default: 20, - parseHTML: element => Number(element.getAttribute('indentSize')) + parseHTML: element => Number(element.getAttribute('indentSize')) || 20 } } } @@ -77,7 +77,7 @@ export const Indent = Extension.create({ const { doc, selection } = tr; let transaction = tr; - if (doc && selection && (selection instanceof TextSelection)) { + if (doc && selection && (selection instanceof TextSelection || selection instanceof AllSelection)) { const { from, to } = selection; doc.nodesBetween(from, to, (node, pos) => { if (this.options.types.includes(node.type.name)) { diff --git a/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/iqb-math-formula.spec.ts b/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/iqb-math-formula.spec.ts new file mode 100644 index 0000000..8bf7556 --- /dev/null +++ b/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/iqb-math-formula.spec.ts @@ -0,0 +1,31 @@ +import { Editor } from '@tiptap/core'; +import { Document } from '@tiptap/extension-document'; +import { Paragraph } from '@tiptap/extension-paragraph'; +import { Text } from '@tiptap/extension-text'; +import { Bold } from '@tiptap/extension-bold'; +import IqbMathFormula from './iqb-math-formula'; + +describe('IqbMathFormula input interoperability', () => { + let editor: Editor; + beforeEach(() => { + editor = new Editor({ extensions: [Document, Paragraph, Text, Bold, IqbMathFormula] }); + }); + afterEach(() => editor.destroy()); + + [ + 'STALE', + 'STALE' + ].forEach(tag => { + it(`imports native LaTeX from ${tag.startsWith(' { + editor.commands.setContent(`

Vor ${tag} Nach

`); + const formula = editor.getJSON().content?.[0].content?.[1]; + expect(formula?.type).toBe('iqbMathFormula'); + expect(formula?.attrs?.['latex']).toBe('\\frac{1}{2}'); + expect(formula?.marks).toEqual([{ type: 'bold' }]); + expect(editor.getHTML()).not.toContain('STALE'); + const html = editor.getHTML(); + editor.commands.setContent(html); + expect(editor.getHTML()).toBe(html); + }); + }); +}); diff --git a/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/iqb-math-formula.ts b/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/iqb-math-formula.ts index daa1ab9..af87251 100644 --- a/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/iqb-math-formula.ts +++ b/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/iqb-math-formula.ts @@ -34,13 +34,13 @@ const IqbMathFormula = Node.create({ return { latex: { default: '', - parseHTML: element => element.getAttribute('data-latex') || '' + parseHTML: element => element.getAttribute('data-latex') || element.getAttribute('formula') || '' } }; }, parseHTML() { - return [{ tag: 'span.iqb-math-formula[data-latex]' }]; + return [{ tag: 'span.iqb-math-formula[data-latex]' }, { tag: 'aspect-nodeview-math-formula[formula]' }]; }, renderHTML({ HTMLAttributes }) { diff --git a/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/paragraph-extension.ts b/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/paragraph-extension.ts index 079d8ae..d2f50a1 100644 --- a/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/paragraph-extension.ts +++ b/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/paragraph-extension.ts @@ -1,6 +1,5 @@ import { Paragraph } from '@tiptap/extension-paragraph'; import { Command } from '@tiptap/core'; -import { Transaction } from 'prosemirror-state'; declare module '@tiptap/core' { interface Commands { @@ -24,23 +23,17 @@ export const ParagraphExtension = Paragraph.extend({ }, addCommands() { - const setNodeIndentMarkup = (tr: Transaction, pos: number, newMargin: number): Transaction => { - const node = tr?.doc?.nodeAt(pos); - if (node) { - const nodeAttrs = { ...node.attrs, margin: newMargin }; - return tr.setNodeMarkup(pos, node.type, nodeAttrs, node.marks); - } - return tr; - }; + const { name } = this; const applyMargin: (newMargin: number) => () => Command = newMargin => () => ({ tr, state, dispatch }) => { const { selection } = state; - let transaction; tr.doc.nodesBetween(selection.from, selection.to, (node, pos) => { - transaction = setNodeIndentMarkup(tr, pos, newMargin); + // Text nodes cannot receive node markup; only this paragraph owns margin. + if (node.type.name !== name) return; + tr.setNodeMarkup(pos, node.type, { ...node.attrs, margin: newMargin }, node.marks); }); - dispatch?.(transaction); + dispatch?.(tr); return true; }; diff --git a/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/preserve-whitespace.spec.ts b/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/preserve-whitespace.spec.ts new file mode 100644 index 0000000..024f7c8 --- /dev/null +++ b/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/preserve-whitespace.spec.ts @@ -0,0 +1,45 @@ +import { Editor } from '@tiptap/core'; +import { Document } from '@tiptap/extension-document'; +import { Paragraph } from '@tiptap/extension-paragraph'; +import { Text } from '@tiptap/extension-text'; +import { Bold } from '@tiptap/extension-bold'; +import { PreserveWhitespace } from './preserve-whitespace'; + +describe('PreserveWhitespace', () => { + let editor: Editor; + beforeEach(() => { + editor = new Editor({ extensions: [Document, Paragraph, Text, Bold, PreserveWhitespace] }); + }); + afterEach(() => editor.destroy()); + + it('retains repeated and non-breaking spaces through the model write command and reload', () => { + editor.commands.setContent('

A B C D E F

'); + expect(editor.getText()).toBe('A B\u00a0C\u202fD E F'); + const stored = editor.getHTML(); + editor.commands.setContent(stored); + expect(editor.getHTML()).toBe(stored); + }); + + it('honors an explicitly requested parser override and the emitUpdate flag', () => { + let updates = 0; + editor.on('update', () => { updates += 1; }); + editor.commands.setContent('

A B

', false, { preserveWhitespace: false }); + expect(editor.getText()).toBe('A B'); + expect(updates).toBe(0); + editor.commands.setContent('

A B

', true); + expect(editor.getText()).toBe('A B'); + expect(updates).toBe(1); + }); + + it('keeps JSON content and marks unchanged', () => { + const content = { + type: 'doc', + content: [{ + type: 'paragraph', + content: [{ type: 'text', text: 'A B\u00a0C', marks: [{ type: 'bold' }] }] + }] + }; + editor.commands.setContent(content); + expect(editor.getJSON()).toEqual(content); + }); +}); diff --git a/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/preserve-whitespace.ts b/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/preserve-whitespace.ts new file mode 100644 index 0000000..ab004ae --- /dev/null +++ b/projects/ngx-coding-components/src/lib/rich-text-editor/extensions/preserve-whitespace.ts @@ -0,0 +1,15 @@ +import { extensions, RawCommands } from '@tiptap/core'; + +/** Preserve authored spacing when ngx-tiptap writes stored HTML through setContent. */ +export const PreserveWhitespace = extensions.Commands.extend({ + name: 'preserveWhitespace', + priority: 1, + addCommands() { + const originalSetContent = this.parent?.()?.setContent; + if (!originalSetContent) throw new Error('The Tiptap setContent command is required.'); + const setContent: RawCommands['setContent'] = (content, emitUpdate = false, parseOptions = {}, options = {}) => ( + originalSetContent(content, emitUpdate, { preserveWhitespace: 'full', ...parseOptions }, options) + ); + return { setContent }; + } +}); diff --git a/projects/ngx-coding-components/src/lib/rich-text-editor/rich-text-editor.component.html b/projects/ngx-coding-components/src/lib/rich-text-editor/rich-text-editor.component.html index 5191787..ec58096 100644 --- a/projects/ngx-coding-components/src/lib/rich-text-editor/rich-text-editor.component.html +++ b/projects/ngx-coding-components/src/lib/rich-text-editor/rich-text-editor.component.html @@ -79,7 +79,7 @@ Abstand - 0px diff --git a/projects/ngx-coding-components/src/lib/rich-text-editor/rich-text-editor.component.ts b/projects/ngx-coding-components/src/lib/rich-text-editor/rich-text-editor.component.ts index 4b7b8d9..f270a81 100644 --- a/projects/ngx-coding-components/src/lib/rich-text-editor/rich-text-editor.component.ts +++ b/projects/ngx-coding-components/src/lib/rich-text-editor/rich-text-editor.component.ts @@ -30,6 +30,7 @@ import { MatIcon } from '@angular/material/icon'; import { MatTooltip } from '@angular/material/tooltip'; import { MatIconButton, MatButton } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; +import { PreserveWhitespace } from './extensions/preserve-whitespace'; import { AnchorId } from './extensions/anchorId'; import { Indent } from './extensions/indent'; import { HangingIndent } from './extensions/hanging-indent'; @@ -75,7 +76,7 @@ export class RichTextEditorComponent implements AfterViewInit, OnChanges { orderedListStyle: string = 'decimal'; defaultExtensions = [ - Document, Text, ListItem, + Document, Text, ListItem, PreserveWhitespace, Underline, Superscript, Subscript, TextStyle, Color, Bold, Italic, Strike, History, @@ -107,6 +108,7 @@ export class RichTextEditorComponent implements AfterViewInit, OnChanges { ]; editor: Editor = new Editor({ + parseOptions: { preserveWhitespace: 'full' }, extensions: this.defaultExtensions }); diff --git a/projects/ngx-coding-components/tsconfig.spec.json b/projects/ngx-coding-components/tsconfig.spec.json index 2432ead..fc50d18 100644 --- a/projects/ngx-coding-components/tsconfig.spec.json +++ b/projects/ngx-coding-components/tsconfig.spec.json @@ -7,6 +7,9 @@ "jasmine" ], "paths": { + "@iqb/ngx-coding-components/codebook-export": ["projects/ngx-coding-components/codebook-export/public-api.ts"], + "@iqb/ngx-coding-components/codebook-generator": ["projects/ngx-coding-components/codebook-generator/public-api.ts"], + "@iqb/ngx-coding-components/codebook-models": ["projects/ngx-coding-components/codebook-models/public-api.ts"], "@angular/*": ["../../node_modules/@angular/*"], "@ngx-translate/core": ["../../node_modules/@ngx-translate/core"], "@ngx-translate/core/*": ["../../node_modules/@ngx-translate/core/*"], diff --git a/scripts/build-codebook-node.mjs b/scripts/build-codebook-node.mjs new file mode 100644 index 0000000..63ef6d7 --- /dev/null +++ b/scripts/build-codebook-node.mjs @@ -0,0 +1,18 @@ +import { build } from 'esbuild'; +import { readFile, writeFile, copyFile } from 'node:fs/promises'; + +// Nest/webpack and Jest consumers also need a native CommonJS entry point. +const root = 'dist/ngx-coding-components'; +await build({ + entryPoints: ['projects/ngx-coding-components/codebook-generator/public-api.ts'], + outfile: `${root}/cjs/codebook-generator.cjs`, bundle: true, platform: 'node', + format: 'cjs', target: 'node20', external: ['@iqb/responses', '@iqbspecs/coding-scheme', 'docx', 'cheerio', 'buffer', 'image-size', 'katex'], sourcemap: true +}); +const manifest = JSON.parse(await readFile(`${root}/package.json`, 'utf8')); +const entry = manifest.exports['./codebook-generator']; +manifest.exports['./codebook-generator'] = { + types: entry.types, require: './cjs/codebook-generator.cjs', default: entry.default +}; +await writeFile(`${root}/package.json`, `${JSON.stringify(manifest, null, 2)}\n`); + +await copyFile('node_modules/mathml2omml/LICENSE', `${root}/cjs/mathml2omml.LICENSE`); diff --git a/scripts/capture-studio-codebook-reference.mjs b/scripts/capture-studio-codebook-reference.mjs new file mode 100644 index 0000000..5a4c2f8 --- /dev/null +++ b/scripts/capture-studio-codebook-reference.mjs @@ -0,0 +1,70 @@ +// Explicit maintainer operation, never run by CI. Requires a Studio checkout with +// the immutable pre-harmonisation commit; generates only invented fixture data. +import assert from 'node:assert/strict'; +import { execFileSync } from 'node:child_process'; +import { createRequire, Module } from 'node:module'; +import { readFile, writeFile } from 'node:fs/promises'; +import { referenceCorpus, referenceOptions } from './fixtures/codebook-synthetic.mjs'; +import { whitespaceReferenceCases, whitespaceReferenceUnits } from './fixtures/codebook-whitespace.mjs'; +import { docxParts, partHashes } from './codebook-docx-test-utils.mjs'; + +const studio = process.argv[2]; +if (!studio) throw new Error('Usage: node scripts/capture-studio-codebook-reference.mjs /path/to/studio-checkout'); +const ref = '2504265721a59c7c65425072951ce82ab16f0fb8'; +const require = createRequire(import.meta.url); +const ts = require('typescript'); +const source = path => execFileSync('git', ['show', `${ref}:${path}`], { cwd: studio, encoding: 'utf8' }); +const baselineLock = JSON.parse(source('package-lock.json')); +for (const name of ['docx', '@iqb/responses', 'mathml2omml', 'cheerio', '@iqbspecs/coding-scheme']) { + const installed = JSON.parse(await readFile(new URL(`../node_modules/${name}/package.json`, import.meta.url), 'utf8')); + assert.equal(installed.version, baselineLock.packages[`node_modules/${name}`].version, + `Reference capture requires the original Studio dependency version: ${name}`); +} +const math = await import('mathml2omml'); +function compile(code, overrides = {}) { + const output = ts.transpileModule(code, { compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.CommonJS, esModuleInterop: true } }).outputText; + const mod = new Module(import.meta.filename); + mod.filename = import.meta.filename; + mod.paths = Module._nodeModulePaths(new URL('..', import.meta.url).pathname); + mod.require = name => overrides[name] || require(name); + mod._compile(output, mod.filename); + return mod.exports; +} +const colors = compile(source('apps/api/src/app/utils/web-colors.ts')); +const renderer = compile(source('apps/api/src/app/classes/download-docx.class.ts'), { + mathml2omml: math, '../utils/web-colors': colors +}).DownloadDocx; +const dataSource = source('apps/api/src/app/classes/download-workspaces.class.ts'); +const ast = ts.createSourceFile('baseline.ts', dataSource, ts.ScriptTarget.Latest, true); +const cls = ast.statements.find(node => ts.isClassDeclaration(node) && node.name.text === 'DownloadWorkspacesClass'); +const names = new Set(['isClosed', 'isManual', 'getCodeInfo', 'getCodeInfoFromCodeAsText', 'getRulesDescription', + 'getBaseOrDerivedBookVariable', 'getCodes', 'getManualOrClosedCodedBookVariable', 'getBookVariable', + 'getCodeBookDataForUnit', 'getBookVariables', 'getSortedBookVariables']); +const methods = cls.members.filter(node => node.name && names.has(node.name.getText(ast))).map(node => node.getText(ast)); +assert.equal(methods.length, names.size); +const adapter = compile(`import { ToTextFactory } from '@iqb/responses'; import { CodingScheme } from '@iqbspecs/coding-scheme'; + export class DownloadWorkspacesClass { ${methods.join('\n')} }`).DownloadWorkspacesClass; +const cases = []; +for (const options of referenceOptions()) { + const data = referenceCorpus().map(unit => adapter.getCodeBookDataForUnit(unit, options, + [{ code: 99, label: 'Missing', description: 'Keine Antwort' }])); + const json = data.map(({ key, name, variables, missings }) => ({ key, name, variables, missings })); + const parts = await docxParts(await renderer.getDocXCodebook(data, options)); + cases.push({ options, json, docxParts: partHashes(parts) }); +} +const fixture = { source: `iqb-berlin/studio-lite@${ref}`, docx: '8.5.0', responses: '5.2.2', + normalization: ['footer date', 'IQB-Studio Codebook -> IQB Codebook', 'exclude docProps/core.xml timestamps', + 'random media filenames in first-reference order; image bytes and relationships preserved'], cases }; +await writeFile(new URL('./fixtures/studio-codebook/synthetic-reference.json', import.meta.url), `${JSON.stringify(fixture, null, 2)}\n`); +console.log(`Captured ${cases.length} original Studio JSON/DOCX cases from raw invented schemas.`); + +const whitespaceCases = []; +for (const entry of whitespaceReferenceCases()) { + const data = whitespaceReferenceUnits(entry.html).map(unit => adapter.getCodeBookDataForUnit(unit, entry.options, [])); + const json = data.map(({ key, name, variables, missings }) => ({ key, name, variables, missings })); + const parts = await docxParts(await renderer.getDocXCodebook(data, entry.options)); + whitespaceCases.push({ ...entry, json, docxParts: partHashes(parts) }); +} +await writeFile(new URL('./fixtures/studio-codebook/whitespace-reference.json', import.meta.url), + `${JSON.stringify({ ...fixture, cases: whitespaceCases }, null, 2)}\n`); +console.log(`Captured ${whitespaceCases.length} original Studio spacing cases for code and general instructions.`); diff --git a/scripts/codebook-contract.test.mjs b/scripts/codebook-contract.test.mjs new file mode 100644 index 0000000..b437f51 --- /dev/null +++ b/scripts/codebook-contract.test.mjs @@ -0,0 +1,239 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { createRequire } from 'node:module'; +import JSZip from 'jszip'; +import { load } from 'cheerio'; +import { CodebookGenerator, CodebookDocxGenerator, hasCodebookManualInstruction } from '../dist/ngx-coding-components/fesm2022/iqb-ngx-coding-components-codebook-generator.mjs'; + +const options = { exportFormat: 'json', missingsProfile: '', hasOnlyManualCoding: false, + hasClosedVars: false, hasOnlyVarsWithCodes: true, hasDerivedVars: true, + hasGeneralInstructions: true, codeLabelToUpper: false, showScore: true, hideItemVarRelation: false }; +const code = (id, manualInstruction = '', type = 'FULL_CREDIT') => ({ id, label: `Code ${id}`, manualInstruction, type, score: 1, ruleSets: [], ruleSetOperatorAnd: true }); +const variable = (id, codes, extra = {}) => ({ id, alias: id, label: id, sourceType: 'BASE', manualInstruction: '', codes, ...extra }); +const unit = (variables, key = 'U') => ({ id: 1, key, name: 'Aufgabe', scheme: JSON.stringify({ version: '3.0', variableCodings: variables }), metadata: { items: [{ id: 'ITEM', variableId: 'V' }] } }); +const data = (variables, extra = {}) => CodebookGenerator.getCodebookData([unit(variables)], { ...options, ...extra }, [])[0].variables; + +for (const manual of [false, true]) for (const closed of [false, true]) for (const onlyCodes of [false, true]) { + test(`selection manual=${manual} closed=${closed} onlyCodes=${onlyCodes}`, () => { + const vars = [variable('M', [code(1, '

Bewerten

'), code(2)]), variable('C', [code(0, '', 'RESIDUAL_AUTO')]), + variable('X', [code(1, 'Bewerten'), code(0, '', 'RESIDUAL_AUTO')]), variable('A', [code(1)]), variable('E', [])]; + const actual = data(vars, { hasOnlyManualCoding: manual, hasClosedVars: closed, hasOnlyVarsWithCodes: onlyCodes }); + const expected = manual && closed ? ['C', 'M', 'X'] : manual ? ['M', 'X'] : closed ? ['C', 'X'] : onlyCodes ? ['A', 'C', 'M', 'X'] : ['A', 'C', 'E', 'M', 'X']; + assert.deepEqual(actual.map(v => v.id), expected); + if (manual && !closed) assert.deepEqual(actual.map(v => v.codes.map(c => c.id)), [['1'], ['1']]); + }); +} + +test('manual rich text handles empty HTML, invisible text, image and formula-only content', () => { + for (const empty of ['', ' ', '

 ​

', '


', '', '

⁠

', '

­

', '

️

']) assert.equal(hasCodebookManualInstruction(empty), false, empty); + for (const nonempty of ['Text', '

<

', '', '', '[[iqb-math:x^2]]']) assert.equal(hasCodebookManualInstruction(nonempty), true, nonempty); +}); + +test('IDs, code zero, derived filtering, BASE_NO_VALUE and stable sorting', () => { + const vars = [variable('Z', [code(0)], { alias: 'A' }), variable('B', [code(-1)]), variable('NO', [code(1)], { sourceType: 'BASE_NO_VALUE' }), variable('D', [code(1)], { sourceType: 'COPY_VALUE' })]; + const actual = data(vars, { hasDerivedVars: false }); + assert.deepEqual(actual.map(v => v.id), ['A', 'B']); + assert.equal(actual[0].codes[0].id, '0'); +}); + +test('invalid schemas fail with the unit key and absent schemes are allowed', () => { + assert.throws(() => CodebookGenerator.getCodebookData([{ id: 1, key: 'BAD', name: '', scheme: '{' }], options, []), /BAD/); + assert.deepEqual(CodebookGenerator.getCodebookData([{ id: 1, key: 'EMPTY', name: '' }], options, [])[0].variables, []); +}); + +test('JSON contract omits items and score when disabled; units sorted by key', async () => { + const blob = await CodebookGenerator.generateCodebook([unit([variable('V', [code(0)])], 'Z'), unit([], 'A')], { ...options, showScore: false }, [{ code: 0, label: 'Missing', description: 'Leer' }]); + const parsed = JSON.parse(await blob.text()); + assert.deepEqual(parsed.map(u => u.key), ['A', 'Z']); + assert.equal('items' in parsed[1], false); + assert.equal('score' in parsed[1].variables[0].codes[0], false); + assert.equal(parsed[1].missings[0].code, 0); +}); + +async function document(description, extra = {}) { + const units = [{ key: 'U', name: 'Aufgabe', variables: [{ id: 'V', label: 'Variable', sourceType: 'BASE', generalInstruction: '', codes: [{ id: '0', label: 'CODE ZERO', score: '7', description }] }], + items: [{ id: 'ITEM', variableId: 'V' }], missings: [{ code: 0, label: 'MISSING ZERO', description: 'Leer' }] }]; + const blob = await CodebookDocxGenerator.generateDocx(units, { ...options, ...extra }); + const zip = await JSZip.loadAsync(await blob.arrayBuffer()); + return { zip, xml: await zip.file('word/document.xml').async('string') }; +} + +test('DOCX keeps Studio headings, item relations, score column order and neutral footer', async () => { + const { zip, xml } = await document('

INSTRUCTION

'); + assert.match(xml, /U Aufgabe/); assert.match(xml, /V Variable/); assert.match(xml, /Item\(s\): ITEM/); + assert.match(xml, /0 MISSING ZERO/); + assert.ok(xml.indexOf('>7<') < xml.indexOf('INSTRUCTION')); + const footer = await zip.file('word/footer1.xml').async('string'); + assert.match(footer, /IQB Codebook/); assert.doesNotMatch(footer, /IQB-Studio|IQB-Kodierbox/); +}); + +for (const description of ['

', '

[[iqb-math:x^2]]

']) { + test(`DOCX retains mathematical content ${description}`, async () => { + const { xml } = await document(description); + assert.match(xml, / { + const image = 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAUCAIAAABwJOjsAAAAJElEQVR4nGOQSDgwIIhh1OJRi0ctHrV41OJRi0ctHrV45FgMAHBlzy43YLJ+AAAAAElFTkSuQmCC'; + const { zip, xml } = await document(`

unterdurch
neu

`); + assert.match(xml, / name.startsWith('word/media/'))); +}); + +test('empty exports produce JSON array and an actual DOCX archive', async () => { + assert.equal(await (await CodebookGenerator.generateCodebook([], options, [])).text(), '[]'); + const blob = await CodebookGenerator.generateCodebook([], { ...options, exportFormat: 'docx' }, []); + const zip = await JSZip.loadAsync(await blob.arrayBuffer()); + assert.ok(zip.file('word/document.xml')); +}); + +test('CommonJS and ESM backend entry points agree without Angular or browser globals', async () => { + const require = createRequire(import.meta.url); + const commonjs = require('../dist/ngx-coding-components/cjs/codebook-generator.cjs'); + const units = [unit([variable('V', [code(0, 'Manuell')])])]; + const a = await CodebookGenerator.generateCodebook(units, options, []); + const b = await commonjs.CodebookGenerator.generateCodebook(units, options, []); + assert.equal(await a.text(), await b.text()); +}); + +test('plain instructions and whitespace around formatted text are not lost', async () => { + const plain = await document('Bitte bewerten'); assert.match(plain.xml, /Bitte bewerten/); + const { xml } = await document('

vor fett nach

'); + assert.match(xml, />vor nach { + const units = instruction => [{ key: 'A', name: 'Aufgabe', items: [], missings: [], variables: [{ + id: 'V', label: 'Variable', sourceType: 'BASE', generalInstruction: instruction, + codes: [{ id: '1', label: 'Code', score: '1', description: '

Bewerten

' }] + }] }]; + let expected; + for (const instruction of ['', ' ', '\t\r\n', '\u00a0']) { + const zip = await JSZip.loadAsync(await (await CodebookDocxGenerator.generateDocx(units(instruction), options)).arrayBuffer()); + const actual = await zip.file('word/document.xml').async('string'); + if (expected) assert.equal(actual, expected, JSON.stringify(instruction)); + expected = actual; + } +}); + +function descriptionParagraphs(xml) { + const $ = load(xml, { xml: true }); + return $('w\\:tr').first().children('w\\:tc').last().find('w\\:p').toArray() + .map(paragraph => $(paragraph).find('w\\:t').text()); +} + +for (const tag of ['ul', 'ol']) { + test(`DOCX retains direct ${tag} list text and existing paragraph list items exactly once`, async () => { + const { xml } = await document(`<${tag}>
  • Erstes Kriterium
  • Zweites Kriterium

  • `); + assert.deepEqual(descriptionParagraphs(xml), tag === 'ol' ? ['1.Erstes Kriterium', '2.Zweites Kriterium'] : ['Erstes Kriterium', 'Zweites Kriterium']); + const $ = load(xml, { xml: true }); + assert.equal($('w\\:numPr').length, tag === 'ul' ? 2 : 0); + assert.equal($('w\\:r').filter((_, run) => $(run).find('w\\:t').text() === 'Kriterium').find('w\\:b').length, 1); + }); +} + +test('DOCX keeps loose text and formatting before, between and after blocks in source order', async () => { + const { xml } = await document('Vor fett

    Regel

    Zwischen kursiv

    Hinweis

    Danach'); + assert.deepEqual(descriptionParagraphs(xml), ['Vor fett', 'Regel', 'Zwischen kursiv', 'Hinweis', 'Danach']); + const $ = load(xml, { xml: true }); + assert.equal($('w\\:r').filter((_, run) => $(run).find('w\\:t').text() === 'fett').find('w\\:b').length, 1); + assert.equal($('w\\:r').filter((_, run) => $(run).find('w\\:t').text() === 'kursiv').find('w\\:i').length, 1); +}); + +test('DOCX keeps mixed and nested list contents without repeating paragraphs or images', async () => { + const image = 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAUCAIAAABwJOjsAAAAJElEQVR4nGOQSDgwIIhh1OJRi0ctHrV41OJRi0ctHrV45FgMAHBlzy43YLJ+AAAAAElFTkSuQmCC'; + const { xml, zip } = await document(`
    • Vor

      Absatz

      Danach
      • Unterpunkt [[iqb-math:x^2]]
    `); + assert.deepEqual(descriptionParagraphs(xml), ['Vor', 'Absatz', 'Danach', '•Unterpunkt ', '']); + const $ = load(xml, { xml: true }); + assert.equal($('w\\:drawing').length, 1); + assert.equal($('m\\:oMath').length, 1); + assert.equal(Object.keys(zip.files).filter(name => name.startsWith('word/media/') && !zip.files[name].dir).length, 1); +}); + +test('complete DOCX exports keep list instructions and plain instructions after generated rules in ESM and CJS', async () => { + const require = createRequire(import.meta.url); + const commonjs = require('../dist/ngx-coding-components/cjs/codebook-generator.cjs'); + for (const [instruction, ruleSets, expected] of [ + ['
    • Erstes Kriterium
    • Zweites Kriterium
    ', [], ['Erstes Kriterium', 'Zweites Kriterium']], + ['Manuelle Instruktion', [{ rules: [{ method: 'MATCH', parameters: ['ABC'] }], ruleOperatorAnd: true }], ['ABC', 'Manuelle Instruktion']] + ]) { + const units = [unit([variable('V', [{ ...code(1, instruction), ruleSets }])])]; + let reference; + for (const generator of [CodebookGenerator, commonjs.CodebookGenerator]) { + const blob = await generator.generateCodebook(units, { ...options, exportFormat: 'docx', hasOnlyManualCoding: true, hasClosedVars: true }, []); + const zip = await JSZip.loadAsync(await blob.arrayBuffer()); + const xml = await zip.file('word/document.xml').async('string'); + assert.deepEqual(descriptionParagraphs(xml), expected); + if (reference) assert.equal(xml, reference); + reference = xml; + } + } +}); + +test('escaped inequality formulas produce valid XML text in inline and block form', async () => { + const { xml } = await document('

    Prefix [[iqb-math:a%3Cb]] suffix

    '); + assert.match(xml, /a<b/); assert.doesNotMatch(xml, //); + assert.doesNotMatch(xml, //); +}); + +test('incomplete codes are closed and general instructions alone are not manual', () => { + const variables = [variable('I', [code(0, '', 'INTENDED_INCOMPLETE')]), + variable('G', [code(1)], { manualInstruction: '

    Allgemeiner Hinweis

    ' })]; + assert.deepEqual(data(variables, { hasClosedVars: true }).map(v => v.id), ['I']); + assert.deepEqual(data(variables, { hasOnlyManualCoding: true }), []); +}); + +test('malformed variable and code collections produce a named export error', () => { + for (const variables of [[null], [{ id: 'V', codes: {} }], [{ id: 'V', codes: [null] }]]) { + assert.throws(() => CodebookGenerator.getCodebookData([unit(variables, 'INVALID')], options, []), /INVALID/); + } +}); + +test('Studio reference preserves document, styles, numbering, settings and page footer XML', async () => { + const { readFile } = await import('node:fs/promises'); + const units = [{ key: 'A', name: 'Referenz', variables: [{ id: 'V', label: 'Variable', sourceType: 'BASE', + generalInstruction: '

    Hinweis

    ', codes: [{ id: '1', label: 'RICHTIG', score: '1', + description: '

    Text

    [[iqb-math:x^2]]

    ' }] }], + missings: [{ code: 99, label: 'MISSING', description: 'Keine Antwort' }], items: [] }]; + const blob = await CodebookDocxGenerator.generateDocx(units, { ...options, exportFormat: 'docx', codeLabelToUpper: true, hideItemVarRelation: true }); + const zip = await JSZip.loadAsync(await blob.arrayBuffer()); + for (const file of ['document.xml', 'styles.xml', 'numbering.xml', 'settings.xml', 'footer1.xml']) { + const expected = await readFile(new URL(`./fixtures/studio-codebook/${file}`, import.meta.url), 'utf8'); + const actual = (await zip.file(`word/${file}`).async('string')).replace(new Date().toLocaleDateString(), '{{DATE}}'); + assert.equal(actual, expected, file); + } +}); + +test('invisible-only instructions do not include variables in manual exports', () => { + for (const instruction of ['

    ⁠

    ', '

    ­

    ', '

    ️

    ']) { + assert.deepEqual(data([variable('EMPTY', [code(1, instruction)])], { hasOnlyManualCoding: true }), []); + } +}); + +test('DOCX retains nested marks and spaces between differently formatted runs', async () => { + const { xml } = await document('

    BEWERTEN Text unten oben

    '); + const $ = (await import('cheerio')).load(xml, { xml: true }); + const runs = $('w\\:r').toArray(); + const findRun = text => runs.find(run => $(run).find('w\\:t').text() === text); + assert.equal($(findRun('BEWERTEN')).find('w\\:b').length, 1); + assert.equal($(findRun('BEWERTEN')).find('w\\:i').length, 1); + assert.equal($(findRun('Text')).find('w\\:u').length, 1); + assert.equal($(findRun('Text')).find('w\\:strike').length, 1); + assert.equal($(findRun('unten')).find('w\\:vertAlign').attr('w:val'), 'subscript'); + assert.equal($(findRun('oben')).find('w\\:vertAlign').attr('w:val'), 'superscript'); + assert.match($('w\\:t').toArray().map(element => $(element).text()).join(''), /BEWERTEN Text unten oben/); + const spaced = await document('

    vor nach

    '); + const text = (await import('cheerio')).load(spaced.xml, { xml: true }); + assert.match(text('w\\:t').toArray().map(element => text(element).text()).join(''), /vor nach/); +}); + +test('shared training filters combine with manual/closed and derived-variable selection', () => { + const vars = [variable('TRAIN', [code(0, 'Bewerten')], { processing: ['CODER_TRAINING_REQUIRED'] }), + variable('OTHER', [code(1, 'Bewerten')]), variable('CLOSED', [code(0, '', 'RESIDUAL_AUTO')], { processing: ['CODER_TRAINING_REQUIRED'] }), + variable('DERIVED', [code(1, 'Bewerten')], { sourceType: 'COPY_VALUE', processing: ['CODER_TRAINING_REQUIRED'] })]; + assert.deepEqual(data(vars, { trainingRequirement: 'required', hasOnlyManualCoding: true, hasDerivedVars: false }).map(v => v.id), ['TRAIN']); + assert.deepEqual(data(vars, { trainingRequirement: 'not-required', hasOnlyManualCoding: true }).map(v => v.id), ['OTHER']); + assert.deepEqual(data(vars, { trainingRequirement: 'required', hasClosedVars: true }).map(v => v.id), ['CLOSED']); + assert.deepEqual(data(vars, { trainingRequirement: 'all' }).map(v => v.id), data(vars).map(v => v.id)); +}); diff --git a/scripts/codebook-docx-test-utils.mjs b/scripts/codebook-docx-test-utils.mjs new file mode 100644 index 0000000..c283e0f --- /dev/null +++ b/scripts/codebook-docx-test-utils.mjs @@ -0,0 +1,55 @@ +import assert from 'node:assert/strict'; +import { createHash } from 'node:crypto'; +import JSZip from 'jszip'; + +export async function docxParts(output) { + const zip = await JSZip.loadAsync(output instanceof Blob ? await output.arrayBuffer() : output); + // docx assigns random media filenames. Preserve each relationship and each image + // byte, replacing only that random name in first-reference order. + const mediaNames = new Map(); + for (const [name, file] of Object.entries(zip.files).sort(([a], [b]) => a.localeCompare(b))) { + if (!name.endsWith('.rels')) continue; + for (const match of (await file.async('string')).matchAll(/Target="media\/([^"]+)"/g)) { + if (!mediaNames.has(match[1])) { + const bytes = await zip.file(`word/media/${match[1]}`).async('nodebuffer'); + mediaNames.set(match[1], `image-${mediaNames.size}-${createHash('sha256').update(bytes).digest('hex')}.${match[1].split('.').pop()}`); + } + } + } + const result = {}; + for (const [name, file] of Object.entries(zip.files).sort(([a], [b]) => a.localeCompare(b))) { + if (file.dir || name === 'docProps/core.xml') continue; // creation/modification timestamps, not document layout + const stableName = name.startsWith('word/media/') ? `word/media/${mediaNames.get(name.slice(11)) || name.slice(11)}` : name; + result[stableName] = name.endsWith('.xml') || name.endsWith('.rels') ? + await file.async('string') : + await file.async('nodebuffer'); + if (typeof result[stableName] === 'string') { + if (/^word\/footer\d+\.xml$/.test(name)) { + result[stableName] = result[stableName].replaceAll('IQB-Studio Codebook', 'IQB Codebook') + .replace(/(]*>IQB Codebook <\/w:t>[\s\S]*?]*>)[\d./-]+(<\/w:t>)/g, '$1{{DATE}}$2') + .replace(/(IQB Codebook )[\d./-]+(?=<\/w:t>)/g, '$1{{DATE}}'); + } + for (const [random, stable] of mediaNames) result[stableName] = result[stableName].replaceAll(random, stable); + } + } + return result; +} +export function partHashes(parts) { + return Object.fromEntries(Object.entries(parts).map(([name, content]) => [name, createHash('sha256').update(content).digest('hex')])); +} + +// Undo only the independently specified correction in the old synthetic Farb-para +// before hashing against the immutable Studio oracle. All other parts stay exact. +export function originalStudioCompatibleParts(parts) { + const result = { ...parts }; + result['word/document.xml'] = result['word/document.xml'].replace(/]*)?>[\s\S]*?<\/w:p>/g, paragraph => { + if (!paragraph.includes('>Farbe')) return paragraph; + assert.match(paragraph, //, 'authored 18pt must become 36 Word half-points'); + assert.match(paragraph, //); + assert.match(paragraph, /w:fill="FFFF00"/); + return paragraph.replace('', '') + .replace('', '') + .replace('w:fill="FFFF00"', 'w:fill="ffff00"'); + }); + return result; +} diff --git a/scripts/codebook-docx-test-utils.test.mjs b/scripts/codebook-docx-test-utils.test.mjs new file mode 100644 index 0000000..94110db --- /dev/null +++ b/scripts/codebook-docx-test-utils.test.mjs @@ -0,0 +1,20 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import JSZip from 'jszip'; +import { docxParts } from './codebook-docx-test-utils.mjs'; + +test('document comparison normalizes the footer date across days without changing user content', async () => { + const document = 'IQB-Studio Codebook 1/2/2020'; + for (const date of ['1/2/2020', '13.10.2026', '2026-10-14']) { + for (const text of [`IQB-Studio Codebook ${date}`, + `IQB-Studio Codebook ${date}`]) { + const zip = new JSZip(); + zip.file('word/document.xml', document); + zip.file('word/footer1.xml', `${text}`); + const parts = await docxParts(await zip.generateAsync({ type: 'nodebuffer' })); + assert.equal(parts['word/document.xml'], document); + assert.ok(parts['word/footer1.xml'].includes('{{DATE}}')); + assert.ok(!parts['word/footer1.xml'].includes(date)); + } + } +}); diff --git a/scripts/codebook-editor-matrix.test.mjs b/scripts/codebook-editor-matrix.test.mjs new file mode 100644 index 0000000..b1ed39c --- /dev/null +++ b/scripts/codebook-editor-matrix.test.mjs @@ -0,0 +1,29 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { createRequire } from 'node:module'; +import { load } from 'cheerio'; +import { CodebookDocxGenerator } from '../dist/ngx-coding-components/fesm2022/iqb-ngx-coding-components-codebook-generator.mjs'; +import { defaults } from './fixtures/codebook-synthetic.mjs'; +import { editorMarkCases, editorMarkUnits } from './fixtures/codebook-editor-matrix.mjs'; +import { docxParts, partHashes } from './codebook-docx-test-utils.mjs'; +const require = createRequire(import.meta.url); +const cjs = require('../dist/ngx-coding-components/cjs/codebook-generator.cjs'); +for (const entry of editorMarkCases()) test(`editor mark combination ${entry.id}`, async () => { + const units = editorMarkUnits(entry.html); + const parts = await docxParts(await CodebookDocxGenerator.generateDocx(units, defaults)); + const common = await docxParts(await cjs.CodebookDocxGenerator.generateDocx(units, defaults)); + assert.deepEqual(partHashes(parts), partHashes(common), 'ESM and CommonJS document parts'); + const $ = load(parts['word/document.xml'], { xml: true }); + const marked = $('w\\:r').toArray().filter(run => $(run).children('w\\:t').text() .includes(entry.text)); + assert.equal(marked.length, 2, 'general and code instruction each retain exact Unicode/spacing'); + for (const run of marked) { + for (const [bit, tag] of ['b', 'i', 'u', 'strike'].entries()) { + assert.equal($(run).find(`w\\:${tag}`).length > 0, !!(entry.mask & (1 << bit)), tag); + } + assert.equal($(run).find('w\\:vertAlign').attr('w:val'), + entry.vertical ? (entry.vertical === 'sup' ? 'superscript' : 'subscript') : undefined); + const paragraph = $(run).closest('w\\:p'); + assert.equal(paragraph.find('w\\:jc').attr('w:val'), entry.alignment === 'justify' ? 'both' : entry.alignment); + assert.equal(paragraph.find('w\\:t').toArray().map(node => $(node).text()).join(''), `vor ${entry.text} nach`); + } +}); diff --git a/scripts/codebook-matrix.test.mjs b/scripts/codebook-matrix.test.mjs new file mode 100644 index 0000000..bd104dc --- /dev/null +++ b/scripts/codebook-matrix.test.mjs @@ -0,0 +1,90 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { readFile } from 'node:fs/promises'; +import { load } from 'cheerio'; +import { CodebookGenerator } from '../dist/ngx-coding-components/fesm2022/iqb-ngx-coding-components-codebook-generator.mjs'; +import { defaults, selectionCorpus, expectedSelection, optionMatrix, missingCases, referenceCorpus, referenceOptions } from './fixtures/codebook-synthetic.mjs'; +import { docxParts, partHashes, originalStudioCompatibleParts } from './codebook-docx-test-utils.mjs'; + +const corpus = selectionCorpus(); +const compactRows = corpus.rows.filter(({ variable }) => variable.sourceType === 'BASE' || + (variable.id.endsWith('_MIXED') && ['SUM_SCORE', 'BASE_NO_VALUE'].includes(variable.sourceType))); +const compactUnits = [{ ...corpus.units[1], scheme: JSON.stringify({ version: '3.4', variableCodings: compactRows.map(r => r.variable).reverse() }) }]; +const projection = variables => variables.map(({ id, codes }) => ({ id, codes: codes.map(c => c.id) })); + +for (const options of optionMatrix()) { + test(`all options ${JSON.stringify(options)}`, async () => { + const expected = expectedSelection(corpus.rows, options); + const compactExpected = expectedSelection(compactRows, options); + for (const missings of missingCases) { + const json = JSON.parse(await (await CodebookGenerator.generateCodebook(corpus.units, { ...options, exportFormat: 'json' }, missings)).text()); + assert.deepEqual(json.map(unit => unit.key), ['A', 'Z']); + assert.deepEqual(projection(json[0].variables), expected); + assert.deepEqual(json[1].variables, []); + assert.deepEqual(json[0].missings, missings); + assert.equal('items' in json[0], false); + for (const variable of json[0].variables) { + assert.equal(!!variable.generalInstruction, options.hasGeneralInstructions); + for (const code of variable.codes) { + assert.equal('score' in code, options.showScore); + if (options.showScore && code.id === '0') assert.equal(code.score, '0'); + if (options.codeLabelToUpper) assert.equal(code.label, code.label.toUpperCase()); + } + } + const parts = await docxParts(await CodebookGenerator.generateCodebook(compactUnits, { ...options, exportFormat: 'docx' }, missings)); + const $ = load(parts['word/document.xml'], { xml: true }); + const headings = $('w\\:p').filter((_, p) => $(p).find('w\\:pStyle').attr('w:val') === 'Heading2'); + assert.deepEqual(headings.toArray().map(p => $(p).find('w\\:t').text().split(' ')[0]), compactExpected.map(v => v.id)); + const tables = $('w\\:tbl').toArray(); + assert.equal(tables.length, compactExpected.length); + tables.forEach((table, i) => { + const rows = $(table).children('w\\:tr').toArray(); + assert.deepEqual(rows.map(row => $(row).children('w\\:tc').first().text()), compactExpected[i].codes); + rows.forEach(row => assert.equal($(row).children('w\\:tc').length, options.showScore ? 4 : 3)); + assert.equal($(table).find('w\\:cantSplit').length, rows.length); + // Widths needed by Microsoft Word and the grid needed by Writer are both preserved. + assert.equal($(table).children('w\\:tblGrid').children().length, options.showScore ? 4 : 3); + assert.equal($(table).find('w\\:tcW').length, rows.length * (options.showScore ? 4 : 3)); + }); + const paragraphs = $('w\\:body').children('w\\:p').toArray().map(p => $(p).find('w\\:t').text()); + assert.equal(paragraphs.filter(t => t.startsWith('Item(s):')).length, options.hideItemVarRelation ? 0 : compactExpected.length); + assert.equal(paragraphs.includes('Allgemeiner Hinweis'), options.hasGeneralInstructions && compactExpected.some(v => v.id.endsWith('_GENERAL_ONLY'))); + assert.equal(paragraphs.some(t => /^\s+$/.test(t)), false, 'whitespace instructions must not change page layout'); + for (const missing of missings) assert.ok(paragraphs.includes(`${missing.code} ${missing.label}`)); + const formulaCount = compactExpected.filter(v => v.id.endsWith('_FORMULA')).length; + const imageCount = compactExpected.filter(v => v.id.endsWith('_IMAGE')).length; + assert.equal($('m\\:oMath').length, formulaCount); + assert.equal($('w\\:drawing').length, imageCount); + assert.doesNotMatch(parts['word/document.xml'], / { + for (const version of ['3.0', '3.3', '3.4']) { + const unit = { ...compactUnits[0], scheme: JSON.stringify({ ...JSON.parse(compactUnits[0].scheme), version }) }; + assert.deepEqual(projection(CodebookGenerator.getCodebookData([unit], defaults, [])[0].variables), expectedSelection(compactRows, defaults)); + } + for (const scheme of ['{', '{}', '{"variableCodings":null}', '{"variableCodings":[null]}']) { + await assert.rejects(CodebookGenerator.generateCodebook([{ ...compactUnits[0], key: 'BROKEN', scheme }], defaults, []), /BROKEN/); + } +}); + +test('raw synthetic schemas match original Studio JSON and complete DOCX parts for all formatting combinations', async () => { + const expected = JSON.parse(await readFile(new URL('./fixtures/studio-codebook/synthetic-reference.json', import.meta.url), 'utf8')); + let index = 0; + for (const options of referenceOptions()) { + const entry = expected.cases[index++]; + assert.deepEqual(entry.options, options); + const units = referenceCorpus(); + const missings = [{ code: 99, label: 'Missing', description: 'Keine Antwort' }]; + const json = JSON.parse(await (await CodebookGenerator.generateCodebook(units, { ...options, exportFormat: 'json' }, missings)).text()); + assert.deepEqual(json, entry.json, `original Studio raw-schema JSON: case ${index}`); + const parts = await docxParts(await CodebookGenerator.generateCodebook(units, options, missings)); + assert.deepEqual(partHashes(originalStudioCompatibleParts(parts)), entry.docxParts, `original Studio DOCX: case ${index}`); + } + assert.equal(index, 32); +}); diff --git a/scripts/codebook-rich-layout.test.mjs b/scripts/codebook-rich-layout.test.mjs new file mode 100644 index 0000000..1712b8f --- /dev/null +++ b/scripts/codebook-rich-layout.test.mjs @@ -0,0 +1,203 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { load } from 'cheerio'; +import { + CodebookDocxGenerator, + CodebookGenerator, + hasCodebookManualInstruction, +} from '../dist/ngx-coding-components/fesm2022/iqb-ngx-coding-components-codebook-generator.mjs'; +import { defaults, image } from './fixtures/codebook-synthetic.mjs'; +import { docxParts } from './codebook-docx-test-utils.mjs'; +async function render(html) { + const parts = await docxParts( + await CodebookDocxGenerator.generateDocx( + [ + { + key: 'U', + name: 'Formatierung', + items: [], + missings: [], + variables: [ + { + id: 'V', + label: 'Instruktion', + sourceType: 'BASE', + generalInstruction: '', + codes: [{ id: '1', label: 'Code', score: '1', description: html }], + }, + ], + }, + ], + defaults, + ), + ); + return load(parts['word/document.xml'], { xml: true }); +} +const runs = ($, text) => + $('w\\:r') + .toArray() + .filter((r) => $(r).children('w\\:t').text() === text); +for (const start of [0, 7]) { + test(`ordered decimal lists retain their explicit start ${start}`, async () => { + const $ = await render(`
    1. ONE

    2. TWO

    `); + assert.ok($(runs($, 'ONE')[0]).closest('w\\:p').find('w\\:t').text().startsWith(`${start}.`)); + assert.ok($(runs($, 'TWO')[0]).closest('w\\:p').find('w\\:t').text().startsWith(`${start + 1}.`)); + }); +} +for (const [command, position] of [['overline', 'top'], ['underline', 'bot']]) { + test(`wide ${command} covers the full expression at the correct position`, async () => { + const $ = await render(`

    [[iqb-math:\\${command}{S\\cap M}]]

    `); + assert.equal($('m\\:barPr m\\:pos').attr('m:val'), position); + assert.equal($('m\\:bar > m\\:e').text(), 'S∩M'); + assert.equal($('m\\:acc').length, 0); + }); +} +test('nested over/underlines keep both positions and a narrow bar accent remains an accent', async () => { + const $ = await render('

    [[iqb-math:\\overline{\\underline{S\\cap M}}]]

    [[iqb-math:\\bar{x}]]

    '); + assert.deepEqual($('m\\:barPr m\\:pos').toArray().map(node => $(node).attr('m:val')), ['top', 'bot']); + assert.equal($('m\\:accPr m\\:chr').attr('m:val'), 'ˉ'); +}); +for (const [latex, structure] of [['\\frac{1}{2}', 'f'], ['\\sqrt{x}', 'rad'], + ['\\sum_{i=1}^{3}i', 'nary'], ['\\begin{pmatrix}1&2\\\\3&4\\end{pmatrix}', 'm']]) { + test(`compound math ${structure} inherits size and color for its control symbols`, async () => { + const html = `

    ` + + `` + + '

    '; + const $ = await render(html); + const control = $(`m\\:${structure}Pr > m\\:ctrlPr > w\\:rPr`); + assert.equal(control.find('w\\:sz').attr('w:val'), '54'); + assert.equal(control.find('w\\:color').attr('w:val'), 'FF0000'); + assert.equal(control.find('w\\:b').length, 1); + }); +} +test('surrounding font size, color and marks also apply to native Aspect formulas', async () => { + const $ = await render('

    Vor ' + + '' + + ' Nach

    [[iqb-math:x^2]]

    '); + const math = $('m\\:oMath'); + math.first().find('m\\:r').each((_, run) => { + assert.equal($(run).find('w\\:sz').attr('w:val'), '54'); + assert.equal($(run).find('w\\:color').attr('w:val'), 'FF0000'); + assert.equal($(run).find('w\\:b').length, 1); + assert.equal($(run).find('w\\:u').attr('w:val'), 'single'); + }); + assert.equal(math.last().find('w\\:sz,w\\:color,w\\:b,w\\:u').length, 0, 'unstyled math must not inherit cached marks'); +}); + +test('Aspect tooltip text and visible anchor highlight survive static export', async () => { + const $ = await render('

    SICHTBAR' + + 'BEREICH

    '); + assert.equal($(runs($, 'SICHTBAR')[0]).find('w\\:t').text(), 'SICHTBAR'); + assert.equal($(runs($, 'BEREICH')[0]).find('w\\:shd').attr('w:fill'), 'DCCCE6'); + assert.equal($(runs($, 'BEREICH')[0]).find('w\\:i').length, 1); +}); +test('inline color, size and highlight stay inside their authored text boundaries', async () => { + const $ = await render( + '

    Vor ROT Schwarz GRUEN Ende

    ', + ); + const r = (t) => $(runs($, t)[0]); + assert.equal(r('Vor ').find('w\\:sz').attr('w:val'), '20'); + assert.equal(r('Vor ').find('w\\:color').attr('w:val'), '000000'); + assert.equal(r('ROT').find('w\\:sz').attr('w:val'), '36'); + assert.equal(r('ROT').find('w\\:color').attr('w:val'), 'FF0000'); + assert.equal(r('GRUEN').find('w\\:shd').attr('w:fill'), '00FF00'); + assert.equal(r(' Ende').find('w\\:shd').attr('w:fill'), 'FFFFFF'); +}); +for (const [size, halfPoints] of [ + ['8px', 12], + ['18px', 27], + ['24px', 36], + ['36px', 54], + ['18pt', 36], +]) + test(`font size ${size} uses Word half-points`, async () => { + const $ = await render(`

    SIZE

    `); + assert.equal($(runs($, 'SIZE')[0]).find('w\\:sz').attr('w:val'), String(halfPoints)); + }); +test('paragraph spacing, indent and hanging indent survive editor serialization', async () => { + const $ = await render('

    INDENT

    '); + const p = $(runs($, 'INDENT')[0]).closest('w\\:p'); + assert.equal(p.find('w\\:ind').attr('w:start'), '600'); + assert.equal(p.find('w\\:ind').attr('w:hanging'), '300'); + assert.equal(p.find('w\\:spacing').attr('w:before'), '0'); + assert.equal(p.find('w\\:spacing').attr('w:after'), '150'); +}); +for (const [style, marker] of [ + ['decimal', '1.'], + ['lower-latin', 'a.'], + ['upper-latin', 'A.'], + ['lower-roman', 'i.'], + ['upper-roman', 'I.'], + ['lower-greek', 'α.'], + ['disc', '•'], + ['circle', '○'], + ['square', '▪'], +]) + test(`authored list style ${style} is retained`, async () => { + const tag = ['disc', 'circle', 'square'].includes(style) ? 'ul' : 'ol'; + const $ = await render(`<${tag} style="list-style:${style}">
  • ONE

  • TWO

  • `); + const p = $(runs($, 'ONE')[0]).closest('w\\:p'); + assert.ok(p.text().startsWith(marker), p.text()); + }); +test('inline image stays between text runs, without duplicate paragraph image', async () => { + const $ = await render( + `

    vornach

    `, + ); + const p = $(runs($, 'vor')[0]).closest('w\\:p'); + assert.equal(p.find('w\\:drawing').length, 1); + assert.equal($('w\\:drawing').length, 1); + assert.equal(p.find('w\\:t').text(), 'vornach'); +}); +for (const side of ['left', 'right']) + test(`block image float ${side} is retained in Word`, async () => { + const $ = await render( + `

    Daneben

    `, + ); + assert.equal($('wp\\:anchor').length, 1); + assert.equal($('wp\\:positionH wp\\:align').text(), side); + assert.equal($('wp\\:wrapSquare').length, 1); + }); +test('Aspect formula renders from LaTeX once instead of frozen MathLive fragments', async () => { + const $ = await render( + '

    vorSTALEnach

    ', + ); + assert.equal($('m\\:oMath').length, 1); + assert.ok(!$('w\\:t').text().includes('STALE')); +}); +test('math-only paragraph respects authored center alignment', async () => { + const $ = await render('

    [[iqb-math:x^2]]

    '); + assert.equal($('m\\:jc').attr('m:val'), 'center'); +}); + +test('formula-only Aspect instructions survive the real manual-code filter', async () => { + const html = ''; + assert.equal(hasCodebookManualInstruction(html), true); + const raw = [ + { + key: 'A', + name: 'Aspect', + scheme: JSON.stringify({ + version: '3.4', + variableCodings: [ + { + id: 'V', + sourceType: 'BASE', + manualInstruction: '', + codes: [{ id: 1, type: 'FULL_CREDIT', label: 'Code', score: 1, manualInstruction: html, ruleSets: [] }], + }, + ], + }), + }, + ]; + const data = CodebookGenerator.getCodebookData(raw, defaults, []); + assert.equal(data[0].variables.length, 1); + assert.equal(data[0].variables[0].codes.length, 1); +}); +test('newlines in a LaTeX attribute do not become HTML break tags', async () => { + const $ = await render( + '

    ', + ); + assert.equal($('m\\:oMath').length, 1); + assert.equal($('m\\:m').length, 1); + assert.ok(!$.root().text().includes('br')); +}); diff --git a/scripts/codebook-rich-matrix.test.mjs b/scripts/codebook-rich-matrix.test.mjs new file mode 100644 index 0000000..84f71e6 --- /dev/null +++ b/scripts/codebook-rich-matrix.test.mjs @@ -0,0 +1,111 @@ +import assert from 'node:assert/strict'; +import { createRequire } from 'node:module'; +import { test } from 'node:test'; +import { load } from 'cheerio'; +import { CodebookDocxGenerator } from '../dist/ngx-coding-components/fesm2022/iqb-ngx-coding-components-codebook-generator.mjs'; +import { defaults } from './fixtures/codebook-synthetic.mjs'; +import { richFormatCases, aspectHtml } from './fixtures/codebook-rich-format-cases.mjs'; +import { docxParts, partHashes } from './codebook-docx-test-utils.mjs'; + +const require = createRequire(import.meta.url); +const CommonJs = require('../dist/ngx-coding-components/cjs/codebook-generator.cjs').CodebookDocxGenerator; + +for (const source of ['aspect', 'coding-components']) { + for (const showScore of [false, true]) { + test(`complete ${source} rich format matrix, score=${showScore}, ESM/CJS`, async () => { + const cases = richFormatCases().filter( + (entry) => + source === 'aspect' || !['inline-image', 'horizontal-rules', 'aspect-annotations'].includes(entry.group), + ); + const units = [ + { + key: 'FORMAT', + name: 'Synthetic acceptance matrix', + items: [], + missings: [], + variables: [ + { + id: 'V', + label: 'Formatting', + sourceType: 'BASE', + generalInstruction: '', + codes: cases.map((entry, index) => ({ + id: String(index + 1), + label: entry.group, + score: '1', + description: source === 'aspect' ? aspectHtml(entry.html) : entry.html, + })), + }, + ], + }, + ]; + const options = { ...defaults, showScore }; + const parts = await docxParts(await CodebookDocxGenerator.generateDocx(units, options)); + const commonParts = await docxParts(await CommonJs.generateDocx(units, options)); + assert.deepEqual(partHashes(parts), partHashes(commonParts)); + const $ = load(parts['word/document.xml'], { xml: true }); + const paragraphs = new Map(); + $('w\\:p').each((_, paragraph) => { + const id = $(paragraph) + .find('w\\:t') + .text() + .match(/FMT\d{4}/)?.[0]; + if (id) paragraphs.set(id, paragraph); + }); + assert.equal(paragraphs.size, cases.length); + let markCases = 0; + for (const entry of cases) { + const p = $(paragraphs.get(entry.id)); + const expected = entry.expected; + if (expected.align) { + assert.equal( + p.find('w\\:jc').attr('w:val'), + expected.align === 'justify' ? 'both' : expected.align, + entry.id, + ); + } + if (entry.group === 'marks-font-alignment') { + const run = p.find('w\\:r').filter((_, node) => $(node).find('w\\:t').text().includes(entry.id)); + assert.equal(run.find('w\\:sz').attr('w:val'), String(Math.round(expected.size * 1.5)), entry.id); + for (const [bit, tag] of ['b', 'i', 'u', 'strike'].entries()) { + assert.equal(!!run.find(`w\\:${tag}`).length, !!(expected.mask & (1 << bit)), `${entry.id} ${tag}`); + } + const vertical = expected.vertical ? (expected.vertical === 'sup' ? 'superscript' : 'subscript') : undefined; + assert.equal(run.find('w\\:vertAlign').attr('w:val'), vertical, entry.id); + assert.ok(run.text().includes('10\u00a0kg; 20\u202fcm; Abstand bleibt'), entry.id); + markCases += 1; + } else if (entry.group === 'format-boundaries') { + const run = (text) => p.find('w\\:r').filter((_, node) => $(node).children('w\\:t').text() === text); + assert.equal(run('FARBE').find('w\\:color').attr('w:val'), expected.hex, entry.id); + assert.equal(run('FARBE').find('w\\:sz').attr('w:val'), '36', entry.id); + assert.equal(run(' NORMAL ').find('w\\:color').attr('w:val'), '000000', entry.id); + assert.equal(run(' ENDE').find('w\\:shd').attr('w:fill'), 'FFFFFF', entry.id); + } else if (entry.group === 'paragraph-layout') { + assert.equal(p.find('w\\:ind').attr('w:start'), String(expected.indent * 300), entry.id); + assert.equal(p.find('w\\:ind').attr('w:hanging'), expected.hanging ? '300' : undefined, entry.id); + assert.equal(p.find('w\\:spacing').attr('w:after'), String(expected.margin * 15), entry.id); + } else if (entry.group === 'list-styles-depth') { + const marker = { + disc: '•', + circle: '○', + square: '▪', + decimal: '1.', + 'lower-latin': 'a.', + 'upper-latin': 'A.', + 'lower-roman': 'i.', + 'upper-roman': 'I.', + 'lower-greek': 'α.', + }; + assert.ok(p.find('w\\:t').text().startsWith(marker[expected.style]), entry.id); + assert.equal(p.find('w\\:r > w\\:tab').length, 1, entry.id); + assert.equal(p.find('w\\:ind').attr('w:start'), String(expected.depth * 600 + 100), entry.id); + } + } + assert.equal(markCases, 2496); + assert.equal($('m\\:oMath').length, 40); + assert.equal($('w\\:drawing').length, source === 'aspect' ? 4 : 3); + assert.equal($('wp\\:anchor').length, 2); + assert.doesNotMatch(parts['word/document.xml'], /HTML konnte nicht verarbeitet werden| { + const expected = reference.cases[index]; + assert.deepEqual({ id: expected.id, html: expected.html, text: expected.text, options: expected.options }, entry); + for (const generator of [CodebookGenerator, commonjs.CodebookGenerator]) { + const units = whitespaceReferenceUnits(entry.html); + const json = await generator.generateCodebook(units, { ...entry.options, exportFormat: 'json' }, []); + assert.deepEqual(JSON.parse(await json.text()), expected.json); + const parts = await docxParts(await generator.generateCodebook(units, entry.options, [])); + assert.deepEqual(partHashes(parts), expected.docxParts, 'all DOCX parts must match original Studio'); + const $ = load(parts['word/document.xml'], { xml: true }); + const paragraphs = $('w\\:body').children('w\\:p').toArray().map(p => $(p).find('w\\:t').text()); + assert.ok(paragraphs.includes(entry.text), 'general instruction must retain its exact spacing'); + const description = $('w\\:tr').first().children('w\\:tc').last().find('w\\:t').toArray().map(t => $(t).text()).join(''); + assert.equal(description, entry.text, 'code instruction must retain its exact spacing'); + } + }); +} diff --git a/scripts/compare-codebook-installations.mjs b/scripts/compare-codebook-installations.mjs new file mode 100644 index 0000000..1e87b74 --- /dev/null +++ b/scripts/compare-codebook-installations.mjs @@ -0,0 +1,133 @@ +// Runs real host preparation and adapters against in-memory persistence boundaries. +// Does not replace an HTTP/browser/Redis acceptance run. +import assert from 'node:assert/strict'; +import { createRequire, Module } from 'node:module'; +import { readFile, writeFile, mkdir } from 'node:fs/promises'; +import { resolve, join } from 'node:path'; +import { load } from 'cheerio'; +import { optionMatrix, selectionCorpus, missingCases, expectedSelection, defaults, referenceCorpus, referenceOptions } from './fixtures/codebook-synthetic.mjs'; +import { whitespaceReferenceCases, whitespaceReferenceUnits } from './fixtures/codebook-whitespace.mjs'; +import { docxParts, partHashes, originalStudioCompatibleParts } from './codebook-docx-test-utils.mjs'; + +const [studioPath, boxPath, outputPath] = process.argv.slice(2); +if (!studioPath || !boxPath || !outputPath) throw new Error('Usage: node scripts/compare-codebook-installations.mjs STUDIO_ROOT BOX_ROOT OUTPUT_DIR'); +const localRequire = createRequire(import.meta.url); +const ts = localRequire('typescript'); +async function hostModule(root, path, overrides = {}) { + const filename = join(root, path); + const require = createRequire(filename); + const source = await readFile(filename, 'utf8'); + const output = ts.transpileModule(source, { compilerOptions: { target: ts.ScriptTarget.ES2022, + module: ts.ModuleKind.CommonJS, esModuleInterop: true, experimentalDecorators: true } }).outputText; + const mod = new Module(filename); + mod.filename = filename; + mod.paths = Module._nodeModulePaths(root); + mod.require = name => overrides[name] || require(name); + mod._compile(output, filename); + return mod.exports; +} +const studio = resolve(studioPath); +const box = resolve(boxPath); +const output = resolve(outputPath); +await mkdir(output, { recursive: true }); +const Studio = (await hostModule(studio, 'apps/api/src/app/classes/download-workspaces.class.ts', { + '@studio-lite/shared-code': { isCurrentFromOrder: order => order === 0 } // unrelated metadata reports, never called here +})).DownloadWorkspacesClass; +const boxAdapter = await hostModule(box, 'apps/backend/src/app/admin/code-book/codebook-generator.class.ts'); +// Entity constructors are DI tokens only. Repositories/profiles supply source +// records in host format; the production service, scope handling and adapters run. +const Service = (await hostModule(box, 'apps/backend/src/app/database/services/coding/codebook-generation.service.ts', { + '../../entities/file_upload.entity': { default: class FileUpload {} }, + '../../entities/job-definition.entity': { JobDefinition: class JobDefinition {} }, + '../../entities/variable-bundle.entity': { VariableBundle: class VariableBundle {} }, + './missings-profiles.service': { MissingsProfilesService: class MissingsProfilesService {} }, + '../../../admin/code-book/codebook-generator.class': boxAdapter +})).CodebookGenerationService; +const corpus = selectionCorpus(); +const rows = corpus.rows.filter(({ variable }) => variable.sourceType === 'BASE' || + (variable.id.endsWith('_MIXED') && ['SUM_SCORE', 'BASE_NO_VALUE'].includes(variable.sourceType))); +const rawUnits = [{ ...corpus.units[1], scheme: JSON.stringify({ version: '3.4', variableCodings: rows.map(r => r.variable).reverse() }), + metadata: { items: rows.map(({ variable }) => ({ id: `ITEM_${variable.id}`, variableId: variable.id })) } }]; +let currentUnits = rawUnits; +const service = new Service({ find: async query => { + assert.equal(query.where.workspace_id, 42); + return currentUnits.map(unit => ({ id: unit.id, file_id: `${unit.key}.VOCS`, filename: `${unit.name}.vocs`, data: unit.scheme, + structured_data: { metadata: unit.metadata } })); +} }, { findOne: async () => { throw new Error('Unexpected job definition lookup'); } }, +{ find: async () => { throw new Error('Unexpected bundle lookup'); } }, +{ getMissingsProfileDetails: async (workspace, id) => { + assert.equal(workspace, 42); assert.equal(id, 7); + return { missings: currentMissings }; +} }); +// The logger is not part of generation; keep the 2304-case comparison readable. +service.logger = { log() {}, warn() {}, error(...args) { throw new Error(args.join(' ')); } }; +let currentMissings = []; +let comparisons = 0; +const wordCases = []; +async function compare(options, missings, save = false, suffix = '', reference = null) { + currentMissings = missings; + const current = { ...options, missingsProfile: missings.length ? 'Synthetic profile' : '' }; + const ids = currentUnits.map(unit => unit.id); + const a = await Studio.getWorkspaceCodingBook(42, { findAllWithProperties: async () => currentUnits }, + { findMissingsProfiles: async () => [{ id: 7, label: 'Synthetic profile', missings: JSON.stringify(missings) }] }, current, ids); + const b = await service.generateCodebook(42, missings.length ? 7 : 0, current, ids); + assert.ok(b, 'Box service must return a file'); + if (current.exportFormat === 'json') { + assert.deepEqual(JSON.parse(a.toString()), JSON.parse(b.toString()), 'Studio preparation vs Box service + adapter'); + if (reference) assert.deepEqual(JSON.parse(a.toString()), reference.json, 'installed host vs original Studio JSON'); + else assert.deepEqual(JSON.parse(a.toString())[0].variables.map(({ id, codes }) => ({ id, codes: codes.map(c => c.id) })), expectedSelection(rows, options)); + } else { + const aParts = await docxParts(a); + const bParts = await docxParts(b); + assert.deepEqual(partHashes(aParts), partHashes(bParts), 'complete DOCX parts including images/relationships/footer/styles'); + if (reference) assert.deepEqual(partHashes(originalStudioCompatibleParts(aParts)), reference.docxParts, 'installed host vs original Studio DOCX'); + const $ = load(aParts['word/document.xml'], { xml: true }); + assert.equal($('w\\:body').children('w\\:p').toArray().some(p => /^\s+$/.test($(p).find('w\\:t').text())), false); + if (save) { + wordCases.push({ suffix, columns: options.showScore ? 4 : 3, tables: $('w\\:tbl').length, + hasMissingZero: missings.some(m => String(m.code) === '0'), + images: $('w\\:drawing').length, formulas: $('m\\:oMath').length, + variableHeadings: $('w\\:pStyle').filter((_, e) => $(e).attr('w:val') === 'Heading2').length }); + } + } + if (save) { + await writeFile(join(output, `studio${suffix}.${current.exportFormat}`), a); + await writeFile(join(output, `kodierbox${suffix}.${current.exportFormat}`), b); + } + comparisons += 1; +} +for (const options of optionMatrix()) { + for (const missings of missingCases) for (const exportFormat of ['json', 'docx']) await compare({ ...options, exportFormat }, missings); + if (comparisons % 600 === 0) console.log(`${comparisons} host export pairs compared`); +} +for (const exportFormat of ['json', 'docx']) await compare({ ...defaults, hideItemVarRelation: false, exportFormat }, missingCases[1], true); +currentUnits = referenceCorpus(); +const references = JSON.parse(await readFile(new URL('./fixtures/studio-codebook/synthetic-reference.json', import.meta.url), 'utf8')).cases; +for (const showScore of [false, true]) { + const options = { ...[...referenceOptions()].at(-1), showScore }; + const reference = references.find(entry => JSON.stringify(entry.options) === JSON.stringify(options)); + assert.ok(reference, 'original Studio reference must exist'); + for (const exportFormat of ['json', 'docx']) await compare({ ...options, exportFormat }, + [{ code: 99, label: 'Missing', description: 'Keine Antwort' }], true, showScore ? '-rich-scores' : '-rich-no-scores', reference); +} +const whitespaceReferences = JSON.parse(await readFile(new URL('./fixtures/studio-codebook/whitespace-reference.json', import.meta.url), 'utf8')).cases; +let whitespaceCases = 0; +for (const entry of whitespaceReferenceCases()) { + const reference = whitespaceReferences[whitespaceCases++]; + assert.deepEqual({ id: reference.id, html: reference.html, text: reference.text, options: reference.options }, entry); + currentUnits = whitespaceReferenceUnits(entry.html); + for (const exportFormat of ['json', 'docx']) await compare({ ...entry.options, exportFormat }, [], false, '', reference); +} +assert.equal(whitespaceCases, whitespaceReferences.length); +await writeFile(join(output, 'word-expectations.json'), JSON.stringify({ cases: wordCases }, null, 2)); +const roots = [studio, box]; +const artifacts = await Promise.all(roots.map(async root => { + const lock = JSON.parse(await readFile(join(root, 'package-lock.json'), 'utf8')); + return lock.packages['node_modules/@iqb/ngx-coding-components'].integrity; +})); +assert.equal(artifacts[0], artifacts[1], 'both installed artifacts must have the same integrity'); +await writeFile(join(output, 'installation-comparison.json'), JSON.stringify({ hostExportPairs: comparisons, optionCombinations: 768, + missingCases: 3, formats: 2, originalStudioWhitespaceCases: whitespaceCases, + identicalJson: true, identicalAllDocxParts: true, artifactIntegrity: artifacts[0], + realHostAdapters: true, inMemoryRepositories: true, httpBrowserRedis: false, windowsWord: false }, null, 2)); +console.log(`PASS: ${comparisons} export pairs through real Studio and Box preparation/adapters.`); diff --git a/scripts/create-codebook-editor-office-fixture.mjs b/scripts/create-codebook-editor-office-fixture.mjs new file mode 100644 index 0000000..bcc78c2 --- /dev/null +++ b/scripts/create-codebook-editor-office-fixture.mjs @@ -0,0 +1,19 @@ +// Maintainer-only: regenerate the synthetic Office visual-acceptance fixture. +import { writeFile } from 'node:fs/promises'; +import { CodebookDocxGenerator } from '../dist/ngx-coding-components/fesm2022/iqb-ngx-coding-components-codebook-generator.mjs'; +import { editorMarkCases, editorMarkUnits } from './fixtures/codebook-editor-matrix.mjs'; +import { defaults, image } from './fixtures/codebook-synthetic.mjs'; +const entries = [...editorMarkCases()].filter(entry => entry.id.includes('-forward-') && entry.alignment === 'left'); +const units = editorMarkUnits(''); +units[0].variables[0].generalInstruction = '

    Alle 48 gültigen Kombinationen aus vier Textmarkierungen und Grundlinie/Hoch-/Tiefstellung.

    '; +units[0].variables[0].codes = entries.map((entry, index) => ({ + id: String(index + 1), label: entry.id.replace('-forward-left', ''), score: '1', description: entry.html +})); +units[0].variables[0].codes.push( + { id: '49', label: 'Formeln', score: '1', description: '

    Formel: [[iqb-math:x^2]] und

    ' }, + { id: '50', label: 'Listen', score: '1', description: '
    • Liste 10 kg

      • Unterpunkt

    ' }, + { id: '51', label: 'Bild', score: '1', description: `

    vor Bild

    nach Bild

    ` } +); +const blob = await CodebookDocxGenerator.generateDocx(units, defaults); +await writeFile(new URL('./fixtures/word-acceptance/editor-combinations.docx', import.meta.url), Buffer.from(await blob.arrayBuffer())); +console.log('Generated 48 mark combinations plus formula/list/image cases from invented content.'); diff --git a/scripts/fixtures/codebook-editor-matrix.mjs b/scripts/fixtures/codebook-editor-matrix.mjs new file mode 100644 index 0000000..afd1f76 --- /dev/null +++ b/scripts/fixtures/codebook-editor-matrix.mjs @@ -0,0 +1,22 @@ +// Supported inline marks shared by the coding-components instruction editor and +// Aspect's Tiptap editor. Subscript/superscript are mutually exclusive. +export function* editorMarkCases() { + const marks = ['strong', 'em', 'u', 's']; + for (let mask = 0; mask < 16; mask += 1) for (const vertical of ['', 'sub', 'sup']) { + for (const reverse of [false, true]) for (const alignment of ['left', 'center', 'right', 'justify']) { + const tags = marks.filter((_, bit) => mask & (1 << bit)); + if (vertical) tags.push(vertical); + if (reverse) tags.reverse(); + const text = '10\u00a0kg; 20\u202fcm; Abstand bleibt'; + const marked = tags.reduce((html, tag) => `<${tag}>${html}`, text); + yield { id: `${mask}-${vertical || 'base'}-${reverse ? 'reverse' : 'forward'}-${alignment}`, + html: `

    vor ${marked} nach

    `, text, mask, vertical, alignment }; + } + } +} +export function editorMarkUnits(html) { + return [{ key: 'EDITOR', name: 'Synthetische Formatkombinationen', items: [], missings: [], variables: [{ + id: 'V', label: 'Formatkombination', sourceType: 'BASE', generalInstruction: html, + codes: [{ id: '1', label: 'Code', score: '1', description: html }] + }] }]; +} diff --git a/scripts/fixtures/codebook-rich-format-cases.mjs b/scripts/fixtures/codebook-rich-format-cases.mjs new file mode 100644 index 0000000..cc9a193 --- /dev/null +++ b/scripts/fixtures/codebook-rich-format-cases.mjs @@ -0,0 +1,118 @@ +import { image } from './codebook-synthetic.mjs'; +export const fontSizes = [8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 36]; +export const alignments = ['left', 'center', 'right', 'justify']; +export const listStyles = [ + 'disc', + 'circle', + 'square', + 'decimal', + 'lower-latin', + 'upper-latin', + 'lower-roman', + 'upper-roman', + 'lower-greek', +]; +const attribute = (value) => value.replaceAll('&', '&').replaceAll('"', '"').replaceAll('<', '<'); +export function richFormatCases() { + const cases = []; + let index = 0; + const add = (group, html, expected = {}) => { + const id = `FMT${String(++index).padStart(4, '0')}`; + cases.push({ id, group, html: html.replaceAll('CASE', id), expected }); + }; + for (let mask = 0; mask < 16; mask++) + for (const vertical of ['', 'sub', 'sup']) + for (const align of alignments) + for (const size of fontSizes) { + const tags = ['strong', 'em', 'u', 's'].filter((_, bit) => mask & (1 << bit)); + if (vertical) tags.push(vertical); + const text = 'CASE 10\u00a0kg; 20\u202fcm; Abstand bleibt'; + const marked = tags.reduce((s, t) => `<${t}>${s}`, text); + add( + 'marks-font-alignment', + `

    ${marked}

    `, + { mask, vertical, align, size }, + ); + } + for (const [value, hex] of [ + ['#234567', '234567'], + ['rgb(255, 0, 0)', 'FF0000'], + ['blue', '0000FF'], + ['#0a3', '00AA33'], + ]) + for (const align of alignments) { + add( + 'format-boundaries', + `

    CASE VOR FARBE NORMAL MARK ENDE

    `, + { hex, align }, + ); + } + for (const style of listStyles) + for (let depth = 1; depth <= 5; depth++) { + const tag = ['disc', 'circle', 'square'].includes(style) ? 'ul' : 'ol'; + let html = '

    CASE LISTE

    '; + for (let d = 0; d < depth; d++) html = `<${tag} style="list-style:${style}">
  • ${html}
  • `; + add('list-styles-depth', html, { style, depth }); + } + for (const align of alignments) + for (const margin of [0, 10, 20]) + for (let indent = 0; indent <= 4; indent++) + for (const hanging of [false, true]) { + add( + 'paragraph-layout', + `

    CASE ABSATZ

    `, + { align, margin, indent, hanging }, + ); + } + for (const level of [1, 2, 3, 4]) + for (const align of alignments) + add('headings', `CASE UEBERSCHRIFT`, { level, align }); + for (const float of ['none', 'left', 'right']) + add( + 'block-images', + `

    CASE VOR BILD

    NACH BILD

    `, + { float }, + ); + add( + 'inline-image', + `

    CASE VORNACH

    `, + ); + for (const latex of [ + 'x^2', + '\\frac{1}{2}', + '\\sqrt{x}', + 'aCASE DANACH

    `, + { latex, align }, + ); + } + add( + 'linebreaks-specials', + '

    CASE 10 kg;20 cm;FETT KURSIV
    ZEILE – ‑ ‘ ’ ‚ „ “ ” ‹ › « » µ ç Ç

    ABSATZ

    ', + ); + add( + 'blockquote', + '

    CASE ZITAT FETT

    VERSCHACHTELT

    ', + ); + add( + 'horizontal-rules', + '

    CASE VOR


    MITTE


    NACH

    ', + ); + return cases; +} +export function aspectHtml(html) { + return html.replace( + /<\/span>/g, + (_, latex) => ``, + ); +} diff --git a/scripts/fixtures/codebook-synthetic.mjs b/scripts/fixtures/codebook-synthetic.mjs new file mode 100644 index 0000000..578bbc5 --- /dev/null +++ b/scripts/fixtures/codebook-synthetic.mjs @@ -0,0 +1,102 @@ +// Entirely invented data. Source archives only informed the structural categories; +// no task identifiers, text, rules, pictures or formulas were copied from them. +export const image = 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAUCAIAAABwJOjsAAAAJElEQVR4nGOQSDgwIIhh1OJRi0ctHrV41OJRi0ctHrV45FgMAHBlzy43YLJ+AAAAAElFTkSuQmCC'; +export const booleanOptions = ['hasOnlyManualCoding', 'hasClosedVars', 'hasOnlyVarsWithCodes', 'hasDerivedVars', + 'hasGeneralInstructions', 'codeLabelToUpper', 'showScore', 'hideItemVarRelation']; +export const defaults = { exportFormat: 'docx', missingsProfile: '', hasOnlyManualCoding: true, + hasClosedVars: true, hasOnlyVarsWithCodes: true, hasDerivedVars: true, hasGeneralInstructions: true, + codeLabelToUpper: true, showScore: true, hideItemVarRelation: true, trainingRequirement: 'all' }; +export function* optionMatrix() { + for (let bits = 0; bits < 256; bits += 1) { + for (const trainingRequirement of ['all', 'required', 'not-required']) { + yield { ...defaults, ...Object.fromEntries(booleanOptions.map((key, i) => [key, !!(bits & (1 << i))])), trainingRequirement }; + } + } +} +export const missingCases = [[], [{ code: 0, label: 'Missing zero', description: 'Keine Eingabe' }, + { code: 99, label: 'Missing other', description: 'Nicht bearbeitet' }], +[{ code: '0', label: 'Missing string zero', description: 'Keine Eingabe' }]]; +export const ruleCases = [ + ['MATCH', ['alpha']], ['MATCH_REGEX', ['^beta$']], ['NUMERIC_MATCH', ['7']], + ['NUMERIC_RANGE', ['2', '9']], ['NUMERIC_FULL_RANGE', ['3', '8']], + ['NUMERIC_LESS_THAN', ['5']], ['NUMERIC_MORE_THAN', ['6']], ['NUMERIC_MAX', ['10']], + ['NUMERIC_MIN', ['4']], ['IS_EMPTY', []], ['IS_NULL', []], ['IS_TRUE', []], ['IS_FALSE', []] +]; +const code = (id, manualInstruction = '', type = 'FULL_CREDIT', extra = {}) => ({ id, type, + label: `Label ${id}`, score: id === 0 ? 0 : 2, manualInstruction, ruleSets: [], ruleSetOperatorAnd: true, ...extra }); + +// Expected membership is explicit: [no filter, manual only, closed only, both]. +// These declarations are the specification, not a call into the production filter. +const groups = [ + { name: 'MANUAL', codes: [code(2, '

    Bewerten

    '), code(0)], selected: [[2, 0], [2], null, [2, 0]] }, + { name: 'MIXED', codes: [code(2, '

    Gemischt

    '), code(0, '', 'RESIDUAL_AUTO')], selected: [[2, 0], [2], [2, 0], [2, 0]] }, + { name: 'CLOSED', codes: [code(0, '', 'RESIDUAL_AUTO')], selected: [[0], null, [0], [0]] }, + { name: 'INCOMPLETE', codes: [code(0, '', 'INTENDED_INCOMPLETE')], selected: [[0], null, [0], [0]] }, + { name: 'AUTO', codes: [code(0)], selected: [[0], null, null, null] }, + { name: 'EMPTY', codes: [], selected: [[], null, null, null] }, + { name: 'GENERAL_ONLY', codes: [code(0)], general: '

    Allgemeiner Hinweis

    ', selected: [[0], null, null, null] }, + { name: 'INVISIBLE', codes: [code(0, '

     ⁠­

    ')], selected: [[0], null, null, null] }, + { name: 'IMAGE', codes: [code(0, ``)], selected: [[0], [0], null, [0]] }, + { name: 'FORMULA', codes: [code(0, '

    [[iqb-math:x^2]]

    ')], selected: [[0], [0], null, [0]] }, + { name: 'RESIDUAL', codes: [code(0, '

    Rest bewerten

    ', 'RESIDUAL')], selected: [[0], [0], null, [0]] }, + { name: 'TO_CHECK', codes: [code(0, '
    • Prüfen

    ', 'TO_CHECK')], selected: [[0], [0], null, [0]] }, + { name: 'PARTIAL', codes: [code(0, '

    Teilweise

    ', 'PARTIAL_CREDIT')], selected: [[0], [0], null, [0]] }, + { name: 'CLOSED_TEXT', codes: [code(0, '

    Geschlossener Hinweis

    ', 'RESIDUAL_AUTO')], selected: [[0], null, [0], [0]] } +]; +export function selectionCorpus() { + const rows = []; + for (const sourceType of ['BASE', 'MANUAL', 'COPY_VALUE', 'SUM_SCORE', 'SOLVER', 'UNIQUE_VALUES', 'CONCAT_CODE', 'SUM_CODE', 'BASE_NO_VALUE']) { + for (const training of [false, true]) { + for (const group of groups) { + const id = `${sourceType}_${training ? 'TRAIN' : 'REGULAR'}_${group.name}`; + const variable = { id, alias: `PUBLIC.${id}`, label: id, sourceType, deriveSources: ['SOURCE'], + sourceParameters: sourceType === 'SOLVER' ? { solverExpression: 'SOURCE + 1' } : {}, + processing: training ? ['CODER_TRAINING_REQUIRED'] : [], manualInstruction: group.general || ' ', codes: group.codes }; + rows.push({ variable, selected: group.selected, training }); + } + } + } + return { rows, units: [{ id: 2, key: 'Z', name: 'Leer', scheme: JSON.stringify({ version: '3.4', variableCodings: [] }), metadata: { items: [] } }, + { id: 1, key: 'A', name: 'Künstliche Kategorien', scheme: JSON.stringify({ version: '3.4', variableCodings: rows.map(r => r.variable).reverse() }), + metadata: { items: rows.map(({ variable }) => ({ id: `ITEM_${variable.id}`, variableId: variable.alias })) } }] }; +} +export function expectedSelection(rows, options) { + const column = Number(options.hasOnlyManualCoding) + 2 * Number(options.hasClosedVars); + return rows.filter(({ variable, training, selected }) => selected[column] !== null && + variable.sourceType !== 'BASE_NO_VALUE' && (options.hasDerivedVars || variable.sourceType === 'BASE') && + (options.trainingRequirement === 'all' || training === (options.trainingRequirement === 'required')) && + (!options.hasOnlyVarsWithCodes || selected[column].length)) + .map(({ variable, selected }) => ({ id: variable.alias, codes: selected[column].map(String) })) + .sort((a, b) => a.id < b.id ? -1 : Number(a.id > b.id)); +} + +// Stable original-Studio behaviour. Approved corrections have separate tests. +// In particular no nested marks, loose text, whitespace at mark boundaries, +// corrected item relations, or changed manual-only filtering is hidden here. +export function referenceCorpus() { + const variables = ruleCases.map(([method, parameters], i) => ({ id: `RULE_${String(i).padStart(2, '0')}`, + alias: `ALIAS_${String(i).padStart(2, '0')}`, label: `Regel ${i}`, sourceType: 'BASE', manualInstruction: i ? '' : ' ', + codes: [code(1, '

    Bewerten

    ', 'FULL_CREDIT', { ruleSets: [{ rules: [{ method, parameters }], ruleOperatorAnd: true }] })] })); + variables.push({ id: 'RICH', alias: 'Z_RICH', label: 'Formatierung', sourceType: 'BASE', + manualInstruction: '

    Allgemeiner Hinweis

    ', codes: [code(2, + `

    Farbe

    ` + + '

    Unterstrichen

    Kursiv

    Durchgestrichen

    ' + + '
    • Kriterium eins

    • Kriterium zwei

    ' + + '

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    ' + + ``)] }); + variables.push({ id: 'DERIVED', alias: 'Z_SUM', label: 'Abgeleitet', sourceType: 'SUM_SCORE', deriveSources: ['RULE_00'], + manualInstruction: '', codes: [code(3, '

    Summe bewerten

    ')] }); + variables.push({ id: 'CONNECT', alias: 'Z_CONNECT', label: 'Verknüpfungen', sourceType: 'BASE', manualInstruction: '', + codes: [true, false].map((and, i) => code(i + 4, '

    Verknüpft

    ', 'FULL_CREDIT', { ruleSetOperatorAnd: and, + ruleSets: [{ ruleOperatorAnd: and, valueArrayPos: 'ANY', rules: [{ method: 'MATCH', parameters: ['gamma'] }, { method: 'IS_TRUE', fragment: 0 }] }, + { ruleOperatorAnd: and, valueArrayPos: 0, rules: [{ method: 'NUMERIC_MATCH', parameters: ['11'] }] }] })) }); + return [{ id: 1, key: 'A', name: 'Synthetische Studio Referenz', scheme: JSON.stringify({ version: '3.4', variableCodings: variables }), metadata: { items: [] } }, + { id: 2, key: 'B', name: 'Zweite Aufgabe', scheme: JSON.stringify({ version: '3.0', variableCodings: [variables[0]] }), metadata: { items: [] } }]; +} +export function* referenceOptions() { + for (let bits = 0; bits < 32; bits += 1) { + const keys = ['hasGeneralInstructions', 'codeLabelToUpper', 'showScore', 'hideItemVarRelation', 'hasDerivedVars']; + yield { ...defaults, hasOnlyManualCoding: false, hasClosedVars: false, hasOnlyVarsWithCodes: false, + ...Object.fromEntries(keys.map((key, i) => [key, !!(bits & (1 << i))])) }; + } +} diff --git a/scripts/fixtures/codebook-whitespace.mjs b/scripts/fixtures/codebook-whitespace.mjs new file mode 100644 index 0000000..99d5ac5 --- /dev/null +++ b/scripts/fixtures/codebook-whitespace.mjs @@ -0,0 +1,33 @@ +import { defaults } from './codebook-synthetic.mjs'; + +// Invented typography examples. Internal spacing is unchanged by old Studio; +// the separately tested corrections at formatting boundaries still apply. +const variants = [ + ['nbsp-named', '

    10 kg

    ', '10\u00a0kg'], + ['nbsp-decimal', '

    10 kg

    ', '10\u00a0kg'], + ['nbsp-hex', '

    10 kg

    ', '10\u00a0kg'], + ['nbsp-literal', '

    10\u00a0kg

    ', '10\u00a0kg'], + ['narrow-nbsp-decimal', '

    20 cm

    ', '20\u202fcm'], + ['narrow-nbsp-hex', '

    20 cm

    ', '20\u202fcm'], + ['narrow-nbsp-literal', '

    20\u202fcm

    ', '20\u202fcm'], + ['repeated-nbsp', '

    10  kg

    ', '10\u00a0\u00a0kg'], + ['formatted-nbsp', '

    10 kg

    ', '10\u00a0kg'], + ['repeated-spaces', '

    Abstand bleibt

    ', 'Abstand bleibt'] +]; + +export function whitespaceReferenceUnits(html) { + return [{ id: 1, key: 'SPACE', name: 'Synthetische Abstände', metadata: { items: [] }, + scheme: JSON.stringify({ version: '3.4', variableCodings: [{ id: 'V', label: 'Abstände', + sourceType: 'BASE', manualInstruction: html, + codes: [{ id: 1, type: 'FULL_CREDIT', label: 'Code', score: 1, + ruleSets: [], ruleSetOperatorAnd: true, manualInstruction: html }] }] }) }]; +} + +export function* whitespaceReferenceCases() { + for (const [id, html, text] of variants) { + for (const showScore of [false, true]) { + yield { id, html, text, options: { ...defaults, hasOnlyManualCoding: false, + hasClosedVars: false, hasOnlyVarsWithCodes: false, codeLabelToUpper: false, showScore } }; + } + } +} diff --git a/scripts/fixtures/studio-codebook/README.md b/scripts/fixtures/studio-codebook/README.md new file mode 100644 index 0000000..ce22e4c --- /dev/null +++ b/scripts/fixtures/studio-codebook/README.md @@ -0,0 +1,56 @@ +# Studio DOCX reference + +Generated from iqb-berlin/studio-lite develop commit +`2504265721a59c7c65425072951ce82ab16f0fb8`, class `DownloadDocx`, using docx 8.5.0. +The input is in the `Studio reference preserves ...` contract test. + +Only two baseline normalizations: +- `IQB-Studio Codebook` becomes the approved neutral `IQB Codebook`. +- The generation date becomes `{{DATE}}`. + +Document XML, styles, numbering, settings and footer must otherwise match exactly. +The fixture deliberately excludes the separately tested approved corrections +(code/missing 0, item relationships, plain instructions and whitespace boundaries). + +## Synthetic raw-schema reference + +`synthetic-reference.json` contains original Studio JSON and SHA-256 digests of +every DOCX part for 32 formatting combinations, starting from raw invented +schemes in `../codebook-synthetic.mjs`. It covers all 13 supported rule methods, +AND/OR rules and rule sets, array positions, multiple tasks, derived variables, +aliases, formatted text, list paragraphs, images and inline/block formulas. +Whitespace-only general instructions must retain the original Studio layout. + +The synthetic categories were informed by five example ZIPs, but none of their +identifiers, task content, instructions, rules or images are included. The example +ZIPs are neither required by tests nor committed as reference fixtures. + +Besides the two normalizations above, ZIP container timestamps are irrelevant, +`docProps/core.xml` (creation/modification metadata) is excluded, and random image +filenames are replaced in first-reference order. Image bytes, relationship +targets, document XML, styles, widths, margins, numbering and page fields remain +part of the exact comparison. `codebook-docx-test-utils.mjs` implements this. + +The baseline is captured explicitly from the immutable Studio commit above: + +```sh +node scripts/capture-studio-codebook-reference.mjs /path/to/studio-checkout +``` + +CI only reads this fixture. Never regenerate it from the shared generator to make +a failing test pass. Approved semantic corrections are checked independently by +the option matrix rather than being disguised as original Studio behaviour. + +## Protected spacing reference + +`whitespace-reference.json` is captured by the same command from the same old +Studio commit. Its 20 cases cover NBSP and narrow NBSP as HTML entities, numeric +character references and literal Unicode, repeated NBSP, bold text and repeated +ordinary spaces, each with and without a score column. Every input places the +same text in both a general instruction and a code instruction. + +The tests compare original Studio JSON and every normalized DOCX part against +both ESM and CommonJS exports and additionally assert the exact instruction text. +The host installation comparison also runs all 20 cases in JSON and DOCX through +both installed adapters against this independent reference. These cases extend +the references without changing the original 32-case fixture. diff --git a/scripts/fixtures/studio-codebook/document.xml b/scripts/fixtures/studio-codebook/document.xml new file mode 100644 index 0000000..bfa3d3e --- /dev/null +++ b/scripts/fixtures/studio-codebook/document.xml @@ -0,0 +1 @@ +99 MISSINGKeine AntwortA ReferenzV VariableHinweis1RICHTIG1Textx2 \ No newline at end of file diff --git a/scripts/fixtures/studio-codebook/footer1.xml b/scripts/fixtures/studio-codebook/footer1.xml new file mode 100644 index 0000000..c63db64 --- /dev/null +++ b/scripts/fixtures/studio-codebook/footer1.xml @@ -0,0 +1 @@ +IQB Codebook {{DATE}} Seite PAGE von NUMPAGES \ No newline at end of file diff --git a/scripts/fixtures/studio-codebook/numbering.xml b/scripts/fixtures/studio-codebook/numbering.xml new file mode 100644 index 0000000..473063e --- /dev/null +++ b/scripts/fixtures/studio-codebook/numbering.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scripts/fixtures/studio-codebook/settings.xml b/scripts/fixtures/studio-codebook/settings.xml new file mode 100644 index 0000000..58b2c76 --- /dev/null +++ b/scripts/fixtures/studio-codebook/settings.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scripts/fixtures/studio-codebook/styles.xml b/scripts/fixtures/studio-codebook/styles.xml new file mode 100644 index 0000000..74947d2 --- /dev/null +++ b/scripts/fixtures/studio-codebook/styles.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scripts/fixtures/studio-codebook/synthetic-reference.json b/scripts/fixtures/studio-codebook/synthetic-reference.json new file mode 100644 index 0000000..fc45b58 --- /dev/null +++ b/scripts/fixtures/studio-codebook/synthetic-reference.json @@ -0,0 +1,9301 @@ +{ + "source": "iqb-berlin/studio-lite@2504265721a59c7c65425072951ce82ab16f0fb8", + "docx": "8.5.0", + "responses": "5.2.2", + "normalization": [ + "footer date", + "IQB-Studio Codebook -> IQB Codebook", + "exclude docProps/core.xml timestamps", + "random media filenames in first-reference order; image bytes and relationships preserved" + ], + "cases": [ + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": false, + "hasGeneralInstructions": false, + "codeLabelToUpper": false, + "showScore": false, + "hideItemVarRelation": false, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    " + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "5bb32bfe61cd729e3f4209fb2229dc59d411975f31f3e505f0375010865301bd", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": false, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": false, + "hideItemVarRelation": false, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    " + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "

    Allgemeiner Hinweis

    ", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "99d5f96d9ff75cef3deb0dc265411526598bd0ea7a3f755372ae7761f3b36cc1", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": false, + "hasGeneralInstructions": false, + "codeLabelToUpper": true, + "showScore": false, + "hideItemVarRelation": false, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    " + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "5bb32bfe61cd729e3f4209fb2229dc59d411975f31f3e505f0375010865301bd", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": false, + "hasGeneralInstructions": true, + "codeLabelToUpper": true, + "showScore": false, + "hideItemVarRelation": false, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    " + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "

    Allgemeiner Hinweis

    ", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "99d5f96d9ff75cef3deb0dc265411526598bd0ea7a3f755372ae7761f3b36cc1", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": false, + "hasGeneralInstructions": false, + "codeLabelToUpper": false, + "showScore": true, + "hideItemVarRelation": false, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "26bb549d5e562916822058d9a3de6914df179b4bc47428988064589310dbf659", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": false, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": true, + "hideItemVarRelation": false, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "

    Allgemeiner Hinweis

    ", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "c50511de7b40e5f30b30b3181abc78a0c6f89ef4d156eb9d63162a84c08a8744", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": false, + "hasGeneralInstructions": false, + "codeLabelToUpper": true, + "showScore": true, + "hideItemVarRelation": false, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "26bb549d5e562916822058d9a3de6914df179b4bc47428988064589310dbf659", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": false, + "hasGeneralInstructions": true, + "codeLabelToUpper": true, + "showScore": true, + "hideItemVarRelation": false, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "

    Allgemeiner Hinweis

    ", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "c50511de7b40e5f30b30b3181abc78a0c6f89ef4d156eb9d63162a84c08a8744", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": false, + "hasGeneralInstructions": false, + "codeLabelToUpper": false, + "showScore": false, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    " + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "5bb32bfe61cd729e3f4209fb2229dc59d411975f31f3e505f0375010865301bd", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": false, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": false, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    " + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "

    Allgemeiner Hinweis

    ", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "99d5f96d9ff75cef3deb0dc265411526598bd0ea7a3f755372ae7761f3b36cc1", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": false, + "hasGeneralInstructions": false, + "codeLabelToUpper": true, + "showScore": false, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    " + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "5bb32bfe61cd729e3f4209fb2229dc59d411975f31f3e505f0375010865301bd", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": false, + "hasGeneralInstructions": true, + "codeLabelToUpper": true, + "showScore": false, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    " + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "

    Allgemeiner Hinweis

    ", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "99d5f96d9ff75cef3deb0dc265411526598bd0ea7a3f755372ae7761f3b36cc1", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": false, + "hasGeneralInstructions": false, + "codeLabelToUpper": false, + "showScore": true, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "26bb549d5e562916822058d9a3de6914df179b4bc47428988064589310dbf659", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": false, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": true, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "

    Allgemeiner Hinweis

    ", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "c50511de7b40e5f30b30b3181abc78a0c6f89ef4d156eb9d63162a84c08a8744", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": false, + "hasGeneralInstructions": false, + "codeLabelToUpper": true, + "showScore": true, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "26bb549d5e562916822058d9a3de6914df179b4bc47428988064589310dbf659", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": false, + "hasGeneralInstructions": true, + "codeLabelToUpper": true, + "showScore": true, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "

    Allgemeiner Hinweis

    ", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "c50511de7b40e5f30b30b3181abc78a0c6f89ef4d156eb9d63162a84c08a8744", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": false, + "codeLabelToUpper": false, + "showScore": false, + "hideItemVarRelation": false, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    " + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    " + } + ] + }, + { + "id": "Z_SUM", + "label": "Abgeleitet", + "sourceType": "SUM_SCORE", + "generalInstruction": "", + "codes": [ + { + "id": "3", + "label": "RICHTIG", + "description": "

    Summe bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "87cfe0ff9c38fcd2c157e548791bae4d76124e957de2d9bdd589f62b16f84c80", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": false, + "hideItemVarRelation": false, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    " + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "

    Allgemeiner Hinweis

    ", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    " + } + ] + }, + { + "id": "Z_SUM", + "label": "Abgeleitet", + "sourceType": "SUM_SCORE", + "generalInstruction": "", + "codes": [ + { + "id": "3", + "label": "RICHTIG", + "description": "

    Summe bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "633c95ee191bf907f7325608a737de635c7d8ee47559bf086a65dd2d951738c7", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": false, + "codeLabelToUpper": true, + "showScore": false, + "hideItemVarRelation": false, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    " + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    " + } + ] + }, + { + "id": "Z_SUM", + "label": "Abgeleitet", + "sourceType": "SUM_SCORE", + "generalInstruction": "", + "codes": [ + { + "id": "3", + "label": "RICHTIG", + "description": "

    Summe bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "87cfe0ff9c38fcd2c157e548791bae4d76124e957de2d9bdd589f62b16f84c80", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": true, + "showScore": false, + "hideItemVarRelation": false, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    " + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "

    Allgemeiner Hinweis

    ", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    " + } + ] + }, + { + "id": "Z_SUM", + "label": "Abgeleitet", + "sourceType": "SUM_SCORE", + "generalInstruction": "", + "codes": [ + { + "id": "3", + "label": "RICHTIG", + "description": "

    Summe bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "633c95ee191bf907f7325608a737de635c7d8ee47559bf086a65dd2d951738c7", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": false, + "codeLabelToUpper": false, + "showScore": true, + "hideItemVarRelation": false, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    ", + "score": "2" + } + ] + }, + { + "id": "Z_SUM", + "label": "Abgeleitet", + "sourceType": "SUM_SCORE", + "generalInstruction": "", + "codes": [ + { + "id": "3", + "label": "RICHTIG", + "description": "

    Summe bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "57723ed4c2252405e198c6900dfa4ff453a91b4a3032f2f66b0c7a0d59e0137f", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": true, + "hideItemVarRelation": false, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "

    Allgemeiner Hinweis

    ", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    ", + "score": "2" + } + ] + }, + { + "id": "Z_SUM", + "label": "Abgeleitet", + "sourceType": "SUM_SCORE", + "generalInstruction": "", + "codes": [ + { + "id": "3", + "label": "RICHTIG", + "description": "

    Summe bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "70b7903b93334e1302849b9c06cdf99ad810622db701df938e419648acc06f21", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": false, + "codeLabelToUpper": true, + "showScore": true, + "hideItemVarRelation": false, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    ", + "score": "2" + } + ] + }, + { + "id": "Z_SUM", + "label": "Abgeleitet", + "sourceType": "SUM_SCORE", + "generalInstruction": "", + "codes": [ + { + "id": "3", + "label": "RICHTIG", + "description": "

    Summe bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "57723ed4c2252405e198c6900dfa4ff453a91b4a3032f2f66b0c7a0d59e0137f", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": true, + "showScore": true, + "hideItemVarRelation": false, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "

    Allgemeiner Hinweis

    ", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    ", + "score": "2" + } + ] + }, + { + "id": "Z_SUM", + "label": "Abgeleitet", + "sourceType": "SUM_SCORE", + "generalInstruction": "", + "codes": [ + { + "id": "3", + "label": "RICHTIG", + "description": "

    Summe bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "70b7903b93334e1302849b9c06cdf99ad810622db701df938e419648acc06f21", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": false, + "codeLabelToUpper": false, + "showScore": false, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    " + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    " + } + ] + }, + { + "id": "Z_SUM", + "label": "Abgeleitet", + "sourceType": "SUM_SCORE", + "generalInstruction": "", + "codes": [ + { + "id": "3", + "label": "RICHTIG", + "description": "

    Summe bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "87cfe0ff9c38fcd2c157e548791bae4d76124e957de2d9bdd589f62b16f84c80", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": false, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    " + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "

    Allgemeiner Hinweis

    ", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    " + } + ] + }, + { + "id": "Z_SUM", + "label": "Abgeleitet", + "sourceType": "SUM_SCORE", + "generalInstruction": "", + "codes": [ + { + "id": "3", + "label": "RICHTIG", + "description": "

    Summe bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "633c95ee191bf907f7325608a737de635c7d8ee47559bf086a65dd2d951738c7", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": false, + "codeLabelToUpper": true, + "showScore": false, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    " + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    " + } + ] + }, + { + "id": "Z_SUM", + "label": "Abgeleitet", + "sourceType": "SUM_SCORE", + "generalInstruction": "", + "codes": [ + { + "id": "3", + "label": "RICHTIG", + "description": "

    Summe bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "87cfe0ff9c38fcd2c157e548791bae4d76124e957de2d9bdd589f62b16f84c80", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": true, + "showScore": false, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    " + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    " + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    " + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "

    Allgemeiner Hinweis

    ", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    " + } + ] + }, + { + "id": "Z_SUM", + "label": "Abgeleitet", + "sourceType": "SUM_SCORE", + "generalInstruction": "", + "codes": [ + { + "id": "3", + "label": "RICHTIG", + "description": "

    Summe bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    " + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "633c95ee191bf907f7325608a737de635c7d8ee47559bf086a65dd2d951738c7", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": false, + "codeLabelToUpper": false, + "showScore": true, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    ", + "score": "2" + } + ] + }, + { + "id": "Z_SUM", + "label": "Abgeleitet", + "sourceType": "SUM_SCORE", + "generalInstruction": "", + "codes": [ + { + "id": "3", + "label": "RICHTIG", + "description": "

    Summe bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "57723ed4c2252405e198c6900dfa4ff453a91b4a3032f2f66b0c7a0d59e0137f", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": true, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "

    Allgemeiner Hinweis

    ", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    ", + "score": "2" + } + ] + }, + { + "id": "Z_SUM", + "label": "Abgeleitet", + "sourceType": "SUM_SCORE", + "generalInstruction": "", + "codes": [ + { + "id": "3", + "label": "RICHTIG", + "description": "

    Summe bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "70b7903b93334e1302849b9c06cdf99ad810622db701df938e419648acc06f21", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": false, + "codeLabelToUpper": true, + "showScore": true, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    ", + "score": "2" + } + ] + }, + { + "id": "Z_SUM", + "label": "Abgeleitet", + "sourceType": "SUM_SCORE", + "generalInstruction": "", + "codes": [ + { + "id": "3", + "label": "RICHTIG", + "description": "

    Summe bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "57723ed4c2252405e198c6900dfa4ff453a91b4a3032f2f66b0c7a0d59e0137f", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": true, + "showScore": true, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "A", + "name": "Synthetische Studio Referenz", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_01", + "label": "Regel 1", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_02", + "label": "Regel 2", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Übereinstimmung (numerisch) mit '7'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_03", + "label": "Regel 3", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '2' und Wert ist maximal '9'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_04", + "label": "Regel 4", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '3' und Wert ist maximal '8'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_05", + "label": "Regel 5", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert geringer als '5'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_06", + "label": "Regel 6", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert größer als '6'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_07", + "label": "Regel 7", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist maximal '10'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_08", + "label": "Regel 8", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist mindestens '4'

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_09", + "label": "Regel 9", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Leerer Wert

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_10", + "label": "Regel 10", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist NULL

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_11", + "label": "Regel 11", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist WAHR

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "ALIAS_12", + "label": "Regel 12", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Wert ist FALSCH

    Bewerten

    ", + "score": "2" + } + ] + }, + { + "id": "Z_CONNECT", + "label": "Verknüpfungen", + "sourceType": "BASE", + "generalInstruction": "", + "codes": [ + { + "id": "4", + "label": "RICHTIG", + "description": "

    gamma\n\nUND\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + }, + { + "id": "5", + "label": "RICHTIG", + "description": "

    gamma\n\nODER\n\nWert ist WAHR - F1

    Übereinstimmung (numerisch) mit '11' (A1)

    Verknüpft

    ", + "score": "2" + } + ] + }, + { + "id": "Z_RICH", + "label": "Formatierung", + "sourceType": "BASE", + "generalInstruction": "

    Allgemeiner Hinweis

    ", + "codes": [ + { + "id": "2", + "label": "RICHTIG", + "description": "

    Farbe

    Unterstrichen

    Kursiv

    Durchgestrichen

    • Kriterium eins

    • Kriterium zwei

    [[iqb-math:x^2]]

    Formel:[[iqb-math:a%3Cb]]

    ", + "score": "2" + } + ] + }, + { + "id": "Z_SUM", + "label": "Abgeleitet", + "sourceType": "SUM_SCORE", + "generalInstruction": "", + "codes": [ + { + "id": "3", + "label": "RICHTIG", + "description": "

    Summe bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + }, + { + "key": "B", + "name": "Zweite Aufgabe", + "variables": [ + { + "id": "ALIAS_00", + "label": "Regel 0", + "sourceType": "BASE", + "generalInstruction": " ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    alpha

    Bewerten

    ", + "score": "2" + } + ] + } + ], + "missings": [ + { + "code": 99, + "label": "Missing", + "description": "Keine Antwort" + } + ] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "4f37a9ec8bc6e77ac1635576141af357bad88366fb22e54344e54d7053be66e2", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "70b7903b93334e1302849b9c06cdf99ad810622db701df938e419648acc06f21", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/media/image-0-78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684.png": "78d10360f88bf6fea9f54941ae25397f917c4f081d3b14f7a86d2685f6f5e684", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + } + ] +} diff --git a/scripts/fixtures/studio-codebook/whitespace-reference.json b/scripts/fixtures/studio-codebook/whitespace-reference.json new file mode 100644 index 0000000..b612398 --- /dev/null +++ b/scripts/fixtures/studio-codebook/whitespace-reference.json @@ -0,0 +1,1183 @@ +{ + "source": "iqb-berlin/studio-lite@2504265721a59c7c65425072951ce82ab16f0fb8", + "docx": "8.5.0", + "responses": "5.2.2", + "normalization": [ + "footer date", + "IQB-Studio Codebook -> IQB Codebook", + "exclude docProps/core.xml timestamps", + "random media filenames in first-reference order; image bytes and relationships preserved" + ], + "cases": [ + { + "id": "nbsp-named", + "html": "

    10 kg

    ", + "text": "10 kg", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": false, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    10 kg

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    10 kg

    " + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "d5f9f53a5031c8a9ddd1e3a34b7ee7736fc65b91bc282eddaf594b77cbfe1a0c", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "nbsp-named", + "html": "

    10 kg

    ", + "text": "10 kg", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": true, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    10 kg

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    10 kg

    ", + "score": "1" + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "c84f505c7595bf22b934aae1101ee06805d326f0f5c4796d3cf2132c93391312", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "nbsp-decimal", + "html": "

    10 kg

    ", + "text": "10 kg", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": false, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    10 kg

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    10 kg

    " + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "d5f9f53a5031c8a9ddd1e3a34b7ee7736fc65b91bc282eddaf594b77cbfe1a0c", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "nbsp-decimal", + "html": "

    10 kg

    ", + "text": "10 kg", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": true, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    10 kg

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    10 kg

    ", + "score": "1" + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "c84f505c7595bf22b934aae1101ee06805d326f0f5c4796d3cf2132c93391312", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "nbsp-hex", + "html": "

    10 kg

    ", + "text": "10 kg", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": false, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    10 kg

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    10 kg

    " + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "d5f9f53a5031c8a9ddd1e3a34b7ee7736fc65b91bc282eddaf594b77cbfe1a0c", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "nbsp-hex", + "html": "

    10 kg

    ", + "text": "10 kg", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": true, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    10 kg

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    10 kg

    ", + "score": "1" + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "c84f505c7595bf22b934aae1101ee06805d326f0f5c4796d3cf2132c93391312", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "nbsp-literal", + "html": "

    10 kg

    ", + "text": "10 kg", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": false, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    10 kg

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    10 kg

    " + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "d5f9f53a5031c8a9ddd1e3a34b7ee7736fc65b91bc282eddaf594b77cbfe1a0c", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "nbsp-literal", + "html": "

    10 kg

    ", + "text": "10 kg", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": true, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    10 kg

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    10 kg

    ", + "score": "1" + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "c84f505c7595bf22b934aae1101ee06805d326f0f5c4796d3cf2132c93391312", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "narrow-nbsp-decimal", + "html": "

    20 cm

    ", + "text": "20 cm", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": false, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    20 cm

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    20 cm

    " + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "2fbb32bd9464923bcb49d0204245fa54a7f3bab9bf6091260c2ed413b48fa745", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "narrow-nbsp-decimal", + "html": "

    20 cm

    ", + "text": "20 cm", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": true, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    20 cm

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    20 cm

    ", + "score": "1" + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "d22b462f11aa99bb4342156f65b68b5e73092a2abd7fbc728e840519384a9e76", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "narrow-nbsp-hex", + "html": "

    20 cm

    ", + "text": "20 cm", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": false, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    20 cm

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    20 cm

    " + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "2fbb32bd9464923bcb49d0204245fa54a7f3bab9bf6091260c2ed413b48fa745", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "narrow-nbsp-hex", + "html": "

    20 cm

    ", + "text": "20 cm", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": true, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    20 cm

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    20 cm

    ", + "score": "1" + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "d22b462f11aa99bb4342156f65b68b5e73092a2abd7fbc728e840519384a9e76", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "narrow-nbsp-literal", + "html": "

    20 cm

    ", + "text": "20 cm", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": false, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    20 cm

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    20 cm

    " + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "2fbb32bd9464923bcb49d0204245fa54a7f3bab9bf6091260c2ed413b48fa745", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "narrow-nbsp-literal", + "html": "

    20 cm

    ", + "text": "20 cm", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": true, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    20 cm

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    20 cm

    ", + "score": "1" + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "d22b462f11aa99bb4342156f65b68b5e73092a2abd7fbc728e840519384a9e76", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "repeated-nbsp", + "html": "

    10  kg

    ", + "text": "10  kg", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": false, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    10  kg

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    10  kg

    " + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "98e0a6b2f6a6517823bde18586896aa2d7f7e726ca30c3ce400b22de7a34d00d", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "repeated-nbsp", + "html": "

    10  kg

    ", + "text": "10  kg", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": true, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    10  kg

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    10  kg

    ", + "score": "1" + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "3895359b0adbabd51aaa6e49e1b5ed55c532cca9184318cfc32fd9f31e0bc4bd", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "formatted-nbsp", + "html": "

    10 kg

    ", + "text": "10 kg", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": false, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    10 kg

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    10 kg

    " + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "38224679ebf17932f60df3476af6d166cb9ead25bed982aef11d90bca1c1250c", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "formatted-nbsp", + "html": "

    10 kg

    ", + "text": "10 kg", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": true, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    10 kg

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    10 kg

    ", + "score": "1" + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "809580cfa42b32d2b96e75f046ecc9753c28a560e7090f8992754f346f1d61e8", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "repeated-spaces", + "html": "

    Abstand bleibt

    ", + "text": "Abstand bleibt", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": false, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    Abstand bleibt

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Abstand bleibt

    " + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "6648dae92f513a0a1a1e2f6e76cdcdb1bd5265853c9ae43b14f10e5f8ffa4040", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + }, + { + "id": "repeated-spaces", + "html": "

    Abstand bleibt

    ", + "text": "Abstand bleibt", + "options": { + "exportFormat": "docx", + "missingsProfile": "", + "hasOnlyManualCoding": false, + "hasClosedVars": false, + "hasOnlyVarsWithCodes": false, + "hasDerivedVars": true, + "hasGeneralInstructions": true, + "codeLabelToUpper": false, + "showScore": true, + "hideItemVarRelation": true, + "trainingRequirement": "all" + }, + "json": [ + { + "key": "SPACE", + "name": "Synthetische Abstände", + "variables": [ + { + "id": "V", + "label": "Abstände", + "sourceType": "BASE", + "generalInstruction": "

    Abstand bleibt

    ", + "codes": [ + { + "id": "1", + "label": "RICHTIG", + "description": "

    Abstand bleibt

    ", + "score": "1" + } + ] + } + ], + "missings": [] + } + ], + "docxParts": { + "_rels/.rels": "641d22a23aa9eb550103a7a01471eaeda5e84c69413e6e03e7fd290b4811a756", + "[Content_Types].xml": "df5eb4247e8594a57c753483112f94648c5b3c162ca0219282d2fff19332bc95", + "docProps/app.xml": "579eba9a8d602575736c3880cf5b529aabbadee0e8cc0e4df8573db0ceadbd4b", + "docProps/custom.xml": "8ec73cc7ce297e42cc652a1d1884d4ffb18ffa0ba7a6502a36f8da90d1be61a1", + "word/_rels/document.xml.rels": "aa8797f9c50610a37b43a791788cb6fe01a8527573c99c09bc946fff7bc31db3", + "word/_rels/fontTable.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footer1.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/_rels/footnotes.xml.rels": "bf64631f0bc7bd46712fef822bcfb22e069fa5779e133980ca0c9d81f00efc09", + "word/comments.xml": "dd2d62f68c6fd01fc4d7d44081587af54ea3e135b9486cc44fa3c62d087adfec", + "word/document.xml": "dfecbaf8f9be0b973a6651243b6b4f543017cff17adc3655762cc6ed9fef0c51", + "word/fontTable.xml": "a746dbb577ec5d68983d659707c0750ca89d40b9d3316e31af331d5e1c736bad", + "word/footer1.xml": "30efed85f494baa349a2bb3df6b05b5dc032d5e912fc5e3aa5d8b293ceea2127", + "word/footnotes.xml": "5a340540bed5ec7a6a1087be13fb0a226151e22e28c43a57d6e5f02acd53ba02", + "word/numbering.xml": "d7283fe87395f6e1c8f2d7f9ece168d2fb35865df19c2ad0660b1390805433fe", + "word/settings.xml": "9f0ff5915faa60180bf7eb19e0db13633fb19c5cffce8d36c7b9a8387f067ad8", + "word/styles.xml": "c51ec0d97223a6b40d98f915b1886d85a909998d4fecdb66ce8bd9be8ef68582" + } + } + ] +} diff --git a/scripts/fixtures/word-acceptance/README.md b/scripts/fixtures/word-acceptance/README.md new file mode 100644 index 0000000..d2ac2b0 --- /dev/null +++ b/scripts/fixtures/word-acceptance/README.md @@ -0,0 +1,44 @@ +# Synthetic Office acceptance documents + +These documents contain invented test data only. They are retained at a public, +versioned URL so Microsoft Office Web Viewer can render the actual DOCX files +without uploading user data to an Office account. + +- `rich-scores.docx` and `rich-no-scores.docx` are outputs of + `scripts/compare-codebook-installations.mjs` using the installed shared package + at commit `aa404bc2`. Every relevant DOCX part was compared with the immutable + original Studio reference. The corresponding Kodierbox outputs have identical + normalized document parts. +- `studio-browser.docx` is an actual browser download from Studio with its real + production backend and a fresh migrated test database. It includes the rich + corpus plus NBSP, narrow NBSP and repeated spaces in general/code instructions. + +Source fixtures: `../codebook-synthetic.mjs`, `../codebook-whitespace.mjs`. +Package SHA256: `dcee6cc39436baf91cf2558afedfbc202e044ca14dbf72c7bfdf4b381e5c0ccd`. + +These files are inputs to visual acceptance, not proof that Office has rendered +or passed them. Execution results are documented separately. + +`original-studio-scores.docx` and `original-studio-no-scores.docx` are generated +from the immutable pre-harmonisation Studio renderer and adapter at +`2504265721a59c7c65425072951ce82ab16f0fb8`, using the same synthetic rich corpus +and dependency versions. They support direct same-engine visual comparison. + +`editor-combinations.docx` contains all 48 valid shared inline-mark combinations +and mixed formula/list/image cases. Regenerate after `npm run build_cc` with +`node scripts/create-codebook-editor-office-fixture.mjs`. Its executable contract +is `scripts/codebook-editor-matrix.test.mjs` (384 cases, each ESM and CommonJS). + +`kodierbox-browser.docx` is the corresponding actual browser download produced +by a real Redis generation job in Kodierbox. Its JSON companion and every +normalized DOCX part match the Studio browser download exactly. + +## Extended Aspect and rich-text acceptance + +`aspect-full-matrix.docx` and `cc-full-matrix.docx` contain 2,740 / 2,738 browser-authored cases. `aspect-feature-specimen.docx` and `cc-feature-specimen.docx` provide readable subsets covering every distinct feature. `toolbar-acceptance.docx` includes actual toolbar output plus 13 styled native formula sizes; `studio-reference-current.docx` includes the explicitly corrected 18pt legacy sample. + +The current regression corpus is `../codebook-rich-format-cases.mjs`. Execute `npm run build_cc && npm run test:codebook`. The full matrix runs through both ESM and CommonJS and both score layouts. The existing independent original-Studio fixtures are retained; only the strictly asserted font-size correction and equivalent highlight hex spelling are normalized by the legacy comparison. + +These seven current documents (including `formula-compound-fixed.docx`) were rendered in Microsoft Office Web Viewer / Word: 262 captured pages, with each actual page number independently checked in its footer. The final bidirectional editor interchange produces the same complete normalized Word parts as the rendered matrix. See `docs/codebook-aspect-acceptance-2026-10-13.md`. The earlier fixture documents and package hashes above remain historical evidence. + +`formula-compound-before.docx` is a historical failing reference for wide accents and black control symbols. Its one page is retained separately and is not included in the 262 passing pages. The corrected counterpart includes a true wide underline as well. diff --git a/scripts/fixtures/word-acceptance/aspect-feature-specimen.docx b/scripts/fixtures/word-acceptance/aspect-feature-specimen.docx new file mode 100644 index 0000000..8ed548b Binary files /dev/null and b/scripts/fixtures/word-acceptance/aspect-feature-specimen.docx differ diff --git a/scripts/fixtures/word-acceptance/aspect-full-matrix.docx b/scripts/fixtures/word-acceptance/aspect-full-matrix.docx new file mode 100644 index 0000000..87e75d9 Binary files /dev/null and b/scripts/fixtures/word-acceptance/aspect-full-matrix.docx differ diff --git a/scripts/fixtures/word-acceptance/cc-feature-specimen.docx b/scripts/fixtures/word-acceptance/cc-feature-specimen.docx new file mode 100644 index 0000000..7d07d4c Binary files /dev/null and b/scripts/fixtures/word-acceptance/cc-feature-specimen.docx differ diff --git a/scripts/fixtures/word-acceptance/cc-full-matrix.docx b/scripts/fixtures/word-acceptance/cc-full-matrix.docx new file mode 100644 index 0000000..1fe256d Binary files /dev/null and b/scripts/fixtures/word-acceptance/cc-full-matrix.docx differ diff --git a/scripts/fixtures/word-acceptance/editor-combinations.docx b/scripts/fixtures/word-acceptance/editor-combinations.docx new file mode 100644 index 0000000..e82ead5 Binary files /dev/null and b/scripts/fixtures/word-acceptance/editor-combinations.docx differ diff --git a/scripts/fixtures/word-acceptance/format-boundaries-before.docx b/scripts/fixtures/word-acceptance/format-boundaries-before.docx new file mode 100644 index 0000000..9569377 Binary files /dev/null and b/scripts/fixtures/word-acceptance/format-boundaries-before.docx differ diff --git a/scripts/fixtures/word-acceptance/format-boundaries-source.json b/scripts/fixtures/word-acceptance/format-boundaries-source.json new file mode 100644 index 0000000..ce50de2 --- /dev/null +++ b/scripts/fixtures/word-acceptance/format-boundaries-source.json @@ -0,0 +1,54 @@ +{ + "html": "

    Vor ROT Schwarz GRUEN Ende

    H1

    H2

    H3

    H4

    1. LISTE

    EINRUECKUNG

    ", + "styles": [ + { + "tag": "P", + "text": "Vor ROT Schwarz GRUEN Ende", + "font": "20px", + "indent": "0px", + "align": "start" + }, + { + "tag": "H1", + "text": "H1", + "font": "20px", + "indent": "0px", + "align": "start" + }, + { + "tag": "H2", + "text": "H2", + "font": "18px", + "indent": "0px", + "align": "start" + }, + { + "tag": "H3", + "text": "H3", + "font": "16px", + "indent": "0px", + "align": "start" + }, + { + "tag": "H4", + "text": "H4", + "font": "16px", + "indent": "0px", + "align": "start" + }, + { + "tag": "OL", + "text": "LISTE", + "font": "20px", + "indent": "40px", + "align": "start" + }, + { + "tag": "P", + "text": "EINRUECKUNG", + "font": "20px", + "indent": "40px", + "align": "start" + } + ] +} \ No newline at end of file diff --git a/scripts/fixtures/word-acceptance/formula-compound-before.docx b/scripts/fixtures/word-acceptance/formula-compound-before.docx new file mode 100644 index 0000000..d25846e Binary files /dev/null and b/scripts/fixtures/word-acceptance/formula-compound-before.docx differ diff --git a/scripts/fixtures/word-acceptance/formula-compound-fixed.docx b/scripts/fixtures/word-acceptance/formula-compound-fixed.docx new file mode 100644 index 0000000..81f723d Binary files /dev/null and b/scripts/fixtures/word-acceptance/formula-compound-fixed.docx differ diff --git a/scripts/fixtures/word-acceptance/kodierbox-browser.docx b/scripts/fixtures/word-acceptance/kodierbox-browser.docx new file mode 100644 index 0000000..ebefffd Binary files /dev/null and b/scripts/fixtures/word-acceptance/kodierbox-browser.docx differ diff --git a/scripts/fixtures/word-acceptance/original-studio-no-scores.docx b/scripts/fixtures/word-acceptance/original-studio-no-scores.docx new file mode 100644 index 0000000..9701719 Binary files /dev/null and b/scripts/fixtures/word-acceptance/original-studio-no-scores.docx differ diff --git a/scripts/fixtures/word-acceptance/original-studio-scores.docx b/scripts/fixtures/word-acceptance/original-studio-scores.docx new file mode 100644 index 0000000..7f6a5dc Binary files /dev/null and b/scripts/fixtures/word-acceptance/original-studio-scores.docx differ diff --git a/scripts/fixtures/word-acceptance/rich-no-scores.docx b/scripts/fixtures/word-acceptance/rich-no-scores.docx new file mode 100644 index 0000000..2767a4f Binary files /dev/null and b/scripts/fixtures/word-acceptance/rich-no-scores.docx differ diff --git a/scripts/fixtures/word-acceptance/rich-scores.docx b/scripts/fixtures/word-acceptance/rich-scores.docx new file mode 100644 index 0000000..a9738e3 Binary files /dev/null and b/scripts/fixtures/word-acceptance/rich-scores.docx differ diff --git a/scripts/fixtures/word-acceptance/studio-browser.docx b/scripts/fixtures/word-acceptance/studio-browser.docx new file mode 100644 index 0000000..25e385f Binary files /dev/null and b/scripts/fixtures/word-acceptance/studio-browser.docx differ diff --git a/scripts/fixtures/word-acceptance/studio-reference-current.docx b/scripts/fixtures/word-acceptance/studio-reference-current.docx new file mode 100644 index 0000000..32a8f90 Binary files /dev/null and b/scripts/fixtures/word-acceptance/studio-reference-current.docx differ diff --git a/scripts/fixtures/word-acceptance/toolbar-acceptance.docx b/scripts/fixtures/word-acceptance/toolbar-acceptance.docx new file mode 100644 index 0000000..5a30372 Binary files /dev/null and b/scripts/fixtures/word-acceptance/toolbar-acceptance.docx differ diff --git a/scripts/verify-codebook-word-windows.ps1 b/scripts/verify-codebook-word-windows.ps1 new file mode 100644 index 0000000..3566144 --- /dev/null +++ b/scripts/verify-codebook-word-windows.ps1 @@ -0,0 +1,95 @@ +param( + [Parameter(Mandatory = $true)][string]$Directory +) +# Run on Windows with desktop Microsoft Word installed. Opens copies read-only; +# never saves changes to the DOCX sources. PDF and JSON are acceptance evidence, +# not a replacement for inspecting the rendered pages. +$ErrorActionPreference = 'Stop' +if ($env:OS -ne 'Windows_NT') { throw 'This check requires Microsoft Word on Windows.' } +$folder = (Resolve-Path $Directory).Path +$manifest = Get-Content (Join-Path $folder 'word-expectations.json') -Raw | ConvertFrom-Json +if (@($manifest.cases).Count -ne 3) { throw 'Expected three Word acceptance cases.' } +$suffixes = @($manifest.cases | ForEach-Object { $_.suffix } | Sort-Object -Unique) +if (($suffixes -join ',') -ne ',-rich-no-scores,-rich-scores') { throw 'Unexpected Word acceptance cases.' } +$word = $null +$results = @() +try { + $word = New-Object -ComObject Word.Application + $word.Visible = $false + $word.DisplayAlerts = 0 + $word.AutomationSecurity = 3 + foreach ($expected in $manifest.cases) { + foreach ($hostName in @('studio', 'kodierbox')) { + $name = "$hostName$($expected.suffix)" + $document = $null + try { + $path = Join-Path $folder "$name.docx" + # ConfirmConversions=false, ReadOnly=true, AddToRecentFiles=false. + # Open normally; do not request Word's OpenAndRepair mode. + $document = $word.Documents.Open($path, $false, $true, $false) + $document.Repaginate() + if ($document.Fields.Update() -ne 0) { throw "$name could not update all document fields." } + foreach ($section in $document.Sections) { + foreach ($footer in $section.Footers) { + if ($footer.Range.Fields.Update() -ne 0) { throw "$name could not update all footer fields." } + } + } + $document.Repaginate() + $pages = $document.ComputeStatistics(2) + $tables = $document.Tables.Count + $images = $document.InlineShapes.Count + $formulas = $document.OMaths.Count + $headings = @($document.Paragraphs | Where-Object { $_.OutlineLevel -eq 2 }).Count + if ($pages -lt 2) { throw "$name must exercise multiple pages." } + if ($tables -ne $expected.tables) { throw "$name lost tables: $tables / $($expected.tables)" } + if ($images -ne $expected.images) { throw "$name lost images: $images / $($expected.images)" } + if ($formulas -ne $expected.formulas) { throw "$name lost equations: $formulas / $($expected.formulas)" } + if ($headings -ne $expected.variableHeadings) { throw "$name lost variable headings." } + foreach ($table in $document.Tables) { + if ($table.Columns.Count -ne $expected.columns) { throw "$name has an incorrect table column count." } + if ($table.Rows.AllowBreakAcrossPages -ne 0) { throw "$name allows code rows to split across pages." } + } + $footerText = $document.Sections.Item(1).Footers.Item(1).Range.Text + if (-not $footerText.Contains('IQB Codebook')) { throw "$name is missing the footer label." } + $footerFields = $document.Sections.Item(1).Footers.Item(1).Range.Fields + $pageFields = @($footerFields | Where-Object { $_.Type -eq 33 }) # wdFieldPage + $totalFields = @($footerFields | Where-Object { $_.Type -eq 26 }) # wdFieldNumPages + if ($pageFields.Count -ne 1 -or $totalFields.Count -ne 1) { throw "$name lost PAGE or NUMPAGES." } + if ([int]$totalFields[0].Result.Text.Trim() -ne $pages) { throw "$name has an incorrect total page field." } + $pageValue = [int]$pageFields[0].Result.Text.Trim() + if ($pageValue -lt 1 -or $pageValue -gt $pages) { throw "$name has an incorrect current page field." } + $text = $document.Content.Text + if ($expected.hasMissingZero -and -not $text.Contains('0 Missing zero')) { throw "$name is missing Missing 0." } + $document.ExportAsFixedFormat((Join-Path $folder "$name-word-windows.pdf"), 17) + $results += [pscustomobject]@{ + case = $expected.suffix; file = "$name.docx"; pages = $pages; tables = $tables; images = $images; formulas = $formulas + text = $text; footer = $footerText; wordVersion = $word.Version; wordBuild = $word.Build + } + } finally { + if ($null -ne $document) { + $document.Close(0) + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($document) + } + } + } + } + foreach ($group in @($results | Group-Object -Property case)) { + $pair = $group.Group + if ($pair.Count -ne 2) { throw 'Missing host document in Word comparison.' } + if ($pair[0].text -cne $pair[1].text) { throw "$($group.Name): different content in Word." } + if ($pair[0].pages -ne $pair[1].pages) { throw "$($group.Name): different page counts in Word." } + if ($pair[0].footer -cne $pair[1].footer) { throw "$($group.Name): different footers in Word." } + } + $report = [pscustomobject]@{ + checkedAtUtc = [DateTime]::UtcNow.ToString('o'); platform = 'Windows desktop Word' + automatedChecksPassed = $true; visualAcceptanceComplete = $false + documents = @($results | Select-Object case,file,pages,tables,images,formulas,wordVersion,wordBuild) + } + $report | ConvertTo-Json -Depth 5 | Set-Content (Join-Path $folder 'word-windows-result.json') -Encoding UTF8 + Write-Output 'Word checks passed. Inspect all six PDFs for page breaks, fonts, formulas, images and page fields before visual acceptance.' +} finally { + if ($null -ne $word) { + $word.Quit(0) + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($word) + } +} diff --git a/tsconfig.json b/tsconfig.json index a4a04f6..40a826f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,6 +14,9 @@ "esModuleInterop": true, "skipLibCheck": true, "paths": { + "@iqb/ngx-coding-components/codebook-export": ["projects/ngx-coding-components/codebook-export/public-api.ts"], + "@iqb/ngx-coding-components/codebook-generator": ["projects/ngx-coding-components/codebook-generator/public-api.ts"], + "@iqb/ngx-coding-components/codebook-models": ["projects/ngx-coding-components/codebook-models/public-api.ts"], "@ngx-translate/core": [ "./node_modules/@ngx-translate/core" ],