Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
42cba8d
feat: use shared codebook generator with Kodierbox scope adapters
jurei733 Sep 28, 2026
d65a2ba
feat: integrate Studio codebook UI while retaining host job handling
jurei733 Sep 28, 2026
08abae3
fix: preserve codebook training filters and update shared generator
jurei733 Sep 28, 2026
1259cea
feat: use shared training filter and simplify codebook options
jurei733 Oct 5, 2026
2c646ad
fix: render asynchronous codebook updates with zoneless detection
jurei733 Oct 5, 2026
fe6c01b
fix(codebook): integrate lossless instruction rendering
jurei733 Oct 5, 2026
7f69a58
fix: preserve missing code types in codebook profiles
jurei733 Oct 12, 2026
5563cac
test: install shared renderer fix and guard DOCX spacing
jurei733 Oct 12, 2026
92b88f3
fix: integrate protected-space codebook rendering
jurei733 Oct 13, 2026
c0a8952
docs: record codebook browser and Office acceptance
jurei733 Oct 13, 2026
405f9df
fix: update codebook formatting and verified acceptance results
jurei733 Oct 13, 2026
cdebfbb
fix: use Node 22 build toolchain in CI
jurei733 Oct 15, 2026
600acdd
fix: execute application validation in branch pipelines
jurei733 Oct 15, 2026
f64b07c
fix: integrate codebook inline image correction
jurei733 Oct 15, 2026
a3c03af
Upgrade Responses engine while preserving legacy coding contracts
jurei733 Oct 15, 2026
8a92674
Preserve legacy solver results with Responses 5.3.1
jurei733 Oct 15, 2026
eaafc1a
Merge develop and verify shared codebook compatibility
jurei733 Oct 15, 2026
926129f
Document Windows Word codebook acceptance
jurei733 Oct 15, 2026
2384940
Integrate codebook alpha with current develop
jurei733 Oct 15, 2026
e747c31
Adapt codebook browser checks to shared form
jurei733 Oct 15, 2026
4a66b82
Use codebook alpha in backend container
jurei733 Oct 15, 2026
392b524
Update shared Codebook build with layout and translation fixes
jurei733 Oct 17, 2026
186181f
Use shared Codebook translation defaults in Kodierbox
jurei733 Oct 17, 2026
69200ad
Clarify production verification with generated dependency overrides
jurei733 Oct 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ variables:
REGISTRY_PASSWORD: "${CI_REGISTRY_PASSWORD}"
REGISTRY_PROJECT: "/${CI_PROJECT_PATH}/"
DOCKER_HUB_PROXY: "${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/"
BASE_IMAGE: node:22-bookworm-slim
# Match package.json/.nvmrc and retain Python/compiler tools for native modules.
BASE_IMAGE: node:22-bookworm
CICD_BASE_IMAGE: "${BASE_IMAGE}-git"
CYPRESS_IMAGE: cypress/browsers:node-22.14.0-chrome-134.0.6998.165-1-ff-136.0.3-edge-134.0.3124.85-1
CYPRESS_CACHE_FOLDER: "${CI_PROJECT_DIR}/cache/Cypress"
Expand Down
27 changes: 3 additions & 24 deletions api-dto/coding/codebook-content-setting.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,11 @@
import type { CodeBookContentSetting as SharedCodebookOptions } from '@iqb/ngx-coding-components/codebook-models';
/**
* Settings for codebook content generation
*/
export type CodebookExportFormat = 'docx' | 'json';
export type CodebookTrainingRequirementFilter = 'all' | 'required' | 'not-required';
export type { CodebookTrainingRequirementFilter } from '@iqb/ngx-coding-components/codebook-models';

export interface CodeBookContentSetting {
/** Export format (docx or json) */
exportFormat: CodebookExportFormat;
/** 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 */
trainingRequirement?: CodebookTrainingRequirementFilter;
export interface CodeBookContentSetting extends SharedCodebookOptions {
/** Restrict variables to a job definition */
jobDefinitionId?: number | null;
/** Restrict variables to one or more variable bundles */
Expand Down
4 changes: 3 additions & 1 deletion apps/backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ USER node
WORKDIR /usr/src/coding-box-${PROJECT}
RUN mkdir packages

# Install dependencies
# Install dependencies from the same commit-pinned Codebook build as the frontend.
COPY --chown=node:node --from=base /usr/src/coding-box-base/vendor/iqb-ngx-coding-components-4.1.0-dev.ff15d6d.tgz ./vendor/iqb-ngx-coding-components-4.1.0-dev.ff15d6d.tgz
COPY --chown=node:node --from=base /usr/src/coding-box-base/dist/apps/${PROJECT}/package.json .
RUN --mount=type=cache,target=~/.npm \
npm pkg set dependencies.@iqb/ngx-coding-components=file:vendor/iqb-ngx-coding-components-4.1.0-dev.ff15d6d.tgz overrides.image-size=2.0.3 && \
npm install --omit=dev --no-fund

# Install PostgreSQL client (missing in dist/apps/backend/package.json - nx bug?)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,66 +1,22 @@
import type { CodeBookContentSetting } from '@iqb/ngx-coding-components/codebook-models';
import { CodebookDocxGenerator } from './codebook-docx-generator.class';

jest.mock('docx', () => ({
ImportedXmlComponent: {
fromXmlString: jest.fn().mockReturnValue({})
}
}));

jest.mock('katex', () => ({
renderToString: jest.fn().mockReturnValue('<math>mocked-mathml</math>')
}));

jest.mock('mathml2omml', () => ({
mml2omml: jest.fn().mockReturnValue('<m:oMath><m:r><m:t xml:space="preserve">a<b & c</m:t></m:r></m:oMath>')
}));

describe('CodebookDocxGenerator', () => {
describe('OMML sanitizing', () => {
it('should sanitize invalid XML chars inside OMML text nodes', () => {
const generator = CodebookDocxGenerator as unknown as {
sanitizeOmmlXml: (omml: string) => string;
};
const rawOmml = '<m:oMath><m:r><m:t xml:space="preserve">a<b & c</m:t></m:r></m:oMath>';
const sanitizedOmml = generator.sanitizeOmmlXml(rawOmml);

expect(sanitizedOmml).toContain('a&lt;b &amp; c');
expect(sanitizedOmml).not.toContain('a<b & c');
});

it('should sanitize OMML before creating ImportedXmlComponent', () => {
const fromXmlString = (
jest.requireMock('docx') as {
ImportedXmlComponent: { fromXmlString: jest.Mock };
}
).ImportedXmlComponent.fromXmlString;
fromXmlString.mockClear();

const generator = CodebookDocxGenerator as unknown as {
latexToOmml: (latex: string) => unknown;
};
const result = generator.latexToOmml('a<b');

expect(result).toEqual({});
expect(fromXmlString).toHaveBeenCalledWith(
'<m:oMath><m:r><m:t xml:space="preserve">a&lt;b &amp; c</m:t></m:r></m:oMath>'
);
});

it('should unwrap the ImportedXmlComponent wrapper before returning OMML', () => {
const fromXmlString = (
jest.requireMock('docx') as {
ImportedXmlComponent: { fromXmlString: jest.Mock };
}
).ImportedXmlComponent.fromXmlString;
const ommlRoot = { rootKey: 'm:oMath' };
fromXmlString.mockReturnValueOnce({ root: [ommlRoot] });

const generator = CodebookDocxGenerator as unknown as {
latexToOmml: (latex: string) => unknown;
};
const result = generator.latexToOmml('a<b');

expect(result).toBe(ommlRoot);
});
describe('CodebookDocxGenerator shared renderer adapter', () => {
it('returns a valid DOCX Buffer even for an empty export', async () => {
const options: CodeBookContentSetting = {
exportFormat: 'docx',
missingsProfile: '',
hasOnlyManualCoding: false,
hasClosedVars: false,
hasOnlyVarsWithCodes: false,
hasDerivedVars: true,
hasGeneralInstructions: true,
codeLabelToUpper: false,
showScore: true,
hideItemVarRelation: true
};
const buffer = await CodebookDocxGenerator.generateDocx([], options);
expect(Buffer.isBuffer(buffer)).toBe(true);
expect(buffer.subarray(0, 2).toString()).toBe('PK');
});
});
Loading
Loading