Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CodeData, CodeType } from '@iqbspecs/coding-scheme/coding-scheme.interface';
import {
DEFAULT_RESIDUAL_MANUAL_INSTRUCTION,
addCode,
canEdit,
canPasteSingleCodeInto,
Expand Down Expand Up @@ -123,9 +124,18 @@ describe('schemer-code-ops', () => {
const list: CodeData[] = [];
const created = addCode(list, 'RESIDUAL', 'RW_MAXIMAL', orderOfCodeTypes) as CodeData;
expect(created.id).toBe(0);
expect(created.manualInstruction).toBe(DEFAULT_RESIDUAL_MANUAL_INSTRUCTION);
expect(list.length).toBe(1);
});

it('should create RESIDUAL_AUTO with default manualInstruction', () => {
const list: CodeData[] = [];
const created = addCode(list, 'RESIDUAL_AUTO', 'RW_MAXIMAL', orderOfCodeTypes) as CodeData;

expect(created.type).toBe('RESIDUAL_AUTO');
expect(created.manualInstruction).toBe(DEFAULT_RESIDUAL_MANUAL_INSTRUCTION);
});

it('should create INTENDED_INCOMPLETE with id 0 and empty manualInstruction', () => {
const list: CodeData[] = [];
const created = addCode(list, 'INTENDED_INCOMPLETE', 'RW_MAXIMAL', orderOfCodeTypes) as CodeData;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ const residualTypes: CodeType[] = [
'INTENDED_INCOMPLETE'
];

export const DEFAULT_RESIDUAL_MANUAL_INSTRUCTION =
'<p style="padding-left: 0; text-indent: 0; margin-bottom: 0; margin-top: 0">Alle anderen Antworten</p>';

export const ensureResidualAutoManualInstruction = (code: CodeData): boolean => {
if (code.type !== 'RESIDUAL_AUTO' || code.manualInstruction) return false;
code.manualInstruction = DEFAULT_RESIDUAL_MANUAL_INSTRUCTION;
return true;
};

export const canEdit = (userRole: UserRoleType): boolean => ['RW_MINIMAL', 'RW_MAXIMAL'].includes(userRole);

export const copySingleCode = (code: CodeData | null | undefined): CodeData | null => {
Expand Down Expand Up @@ -72,10 +81,7 @@ export const addCode = (
score: 0,
ruleSetOperatorAnd: true,
ruleSets: [],
manualInstruction:
codeType === 'RESIDUAL_AUTO' ?
'' :
'<p style="padding-left: 0; text-indent: 0; margin-bottom: 0; margin-top: 0">Alle anderen Antworten</p>'
manualInstruction: DEFAULT_RESIDUAL_MANUAL_INSTRUCTION
};

codeList.push(newCode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@
"title": "Instruktionen für manuelles Kodieren",
"prompt-edit": "Instruktionen für manuelles Kodieren ändern",
"wipe": "Instruktionen komplett löschen",
"error-residual-auto": "Wenn ein automatischer Code für 'Alle anderen Antworten' definiert ist, werden manuelle Instruktionen nie genutzt.",
"error-residual-auto": "Im normalen automatischen Fall für 'Alle anderen Antworten' werden manuelle Instruktionen nicht genutzt.",
"error-intended-incomplete": "Wenn ein automatischer Code für 'Absichtlich unvollständig' definiert ist, werden manuelle Instruktionen nie genutzt."
},
"coding": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ import { renderManualInstructionMath } from '../rich-text-editor/manual-instruct
>
<mat-icon> edit </mat-icon>
</button>
@if ((hasResidualAutoCode) && code.manualInstruction) {
@if (
(hasResidualAutoCode) &&
!suppressResidualAutoWarning &&
code.manualInstruction
) {
<mat-icon
[style.color]="'red'"
[matTooltip]="
Expand Down Expand Up @@ -107,6 +111,7 @@ export class CodeInstructionComponent {
@Input() userRole: UserRoleType = 'RO';
@Input() hasIntendedIncompleteCode = false;
@Input() hasResidualAutoCode = false;
@Input() suppressResidualAutoWarning = false;

constructor(
private sanitizer: DomSanitizer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
codeType === 'RESIDUAL') &&
(hasResidualAutoCode || hasIntendedIncompleteAutoCode)
"
(click)="code.type = codeType; setCodeChanged()"
(click)="setCodeType(codeType)"
>
@if (codeType === 'FULL_CREDIT') {
<mat-icon style="color: limegreen">done_all</mat-icon>
Expand Down Expand Up @@ -182,32 +182,32 @@
(codeRulesChanged)="setCodeChanged()"
>
</code-rules>
} @else if (codeModel === 'MANUAL_ONLY' && (code.type !== 'RESIDUAL_AUTO' &&
code.type !== 'INTENDED_INCOMPLETE')) {
} @else if (codeModel === 'MANUAL_ONLY' && showManualOnlyInstruction()) {
<code-instruction
[code]="code"
(codeDataChanged)="setCodeChanged()"
[userRole]="schemerService.userRole"
[hasResidualAutoCode]="hasResidualAutoCode"
[hasIntendedIncompleteCode]="hasIntendedIncompleteAutoCode"
[suppressResidualAutoWarning]="suppressResidualAutoWarning()"
>
</code-instruction>
}
</div>
@if ((codeModel ?? 'NONE') === 'NONE' || codeModel === 'MANUAL_AND_RULES') { @if (code.type && (code.type ===
'RESIDUAL_AUTO' || code.type === 'INTENDED_INCOMPLETE') &&
!code.manualInstruction) {
<div class="fx-flex-fill"></div>
} @else {
@if ((codeModel ?? 'NONE') === 'NONE' || codeModel === 'MANUAL_AND_RULES') {
@if (showSideInstruction()) {
<code-instruction
[code]="code"
(codeDataChanged)="setCodeChanged()"
[userRole]="schemerService.userRole"
[hasResidualAutoCode]="hasResidualAutoCode"
[hasIntendedIncompleteCode]="hasIntendedIncompleteAutoCode"
[suppressResidualAutoWarning]="suppressResidualAutoWarning()"
class="fx-flex-fill"
>
</code-instruction>
} @else {
<div class="fx-flex-fill"></div>
} }
<button
mat-icon-button
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { CodeData } from '@iqbspecs/coding-scheme/coding-scheme.interface';
import { DEFAULT_RESIDUAL_MANUAL_INSTRUCTION } from '../services/schemer-code-ops';
import { SchemerService } from '../services/schemer.service';
import { SingleCodeComponent } from './single-code.component';

describe('SingleCodeComponent', () => {
let component: SingleCodeComponent;

beforeEach(() => {
component = new SingleCodeComponent({
userRole: 'RW_MAXIMAL',
copySingleCode: jasmine.createSpy('copySingleCode'),
deleteCode: jasmine.createSpy('deleteCode')
} as unknown as SchemerService);
});

it('setCodeType should add default manualInstruction for RESIDUAL_AUTO', () => {
component.code = {
id: 1,
type: 'FULL_CREDIT',
label: '',
score: 1,
manualInstruction: ''
} as unknown as CodeData;
const emitSpy = spyOn(component.codeDataChanged, 'emit');

component.setCodeType('RESIDUAL_AUTO');

expect(component.code.type).toBe('RESIDUAL_AUTO');
expect(component.code.manualInstruction).toBe(DEFAULT_RESIDUAL_MANUAL_INSTRUCTION);
expect(emitSpy).toHaveBeenCalledWith(component.code);
});

it('setCodeType should not overwrite existing manualInstruction', () => {
component.code = {
id: 1,
type: 'FULL_CREDIT',
label: '',
score: 1,
manualInstruction: '<p>custom</p>'
} as unknown as CodeData;

component.setCodeType('RESIDUAL_AUTO');

expect(component.code.manualInstruction).toBe('<p>custom</p>');
});

it('should show RESIDUAL_AUTO manual instructions only for SOLVER codings', () => {
component.code = {
id: 0,
type: 'RESIDUAL_AUTO',
label: '',
score: 0,
manualInstruction: DEFAULT_RESIDUAL_MANUAL_INSTRUCTION
} as unknown as CodeData;

component.sourceType = 'BASE';
expect(component.showManualOnlyInstruction()).toBeFalse();
expect(component.showSideInstruction()).toBeFalse();
expect(component.suppressResidualAutoWarning()).toBeFalse();

component.sourceType = 'SOLVER';
expect(component.showManualOnlyInstruction()).toBeTrue();
expect(component.showSideInstruction()).toBeTrue();
expect(component.suppressResidualAutoWarning()).toBeTrue();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ import { MatDivider } from '@angular/material/divider';
import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
import {
CodeData,
CodeModelType
CodeModelType,
CodeType,
VariableCodingData
} from '@iqbspecs/coding-scheme/coding-scheme.interface';
import { VariableInfo } from '@iqbspecs/variable-info/variable-info.interface';
import { SchemerService } from '../services/schemer.service';
import { ensureResidualAutoManualInstruction } from '../services/schemer-code-ops';
import { CodeRulesComponent } from './code-rules/code-rules.component';
import { CodeInstructionComponent } from './code-instruction.component';

Expand Down Expand Up @@ -74,6 +77,7 @@ export class SingleCodeComponent {
@Input() fragmentMode: boolean | undefined;
@Input() codeIndex: number | undefined;
@Input() codeModel: CodeModelType | undefined;
@Input() sourceType: VariableCodingData['sourceType'] | undefined;
@Input() hasResidualAutoCode = false;
@Input() hasIntendedIncompleteAutoCode = false;

Expand Down Expand Up @@ -121,6 +125,35 @@ export class SingleCodeComponent {
this.codeDataChanged.emit(this.code);
}

setCodeType(codeType: CodeType) {
if (this.code) {
this.code.type = codeType;
ensureResidualAutoManualInstruction(this.code);
this.setCodeChanged();
}
}

showResidualAutoManualInstruction(): boolean {
return this.sourceType === 'SOLVER';
}

showManualOnlyInstruction(): boolean {
if (!this.code) return false;
if (this.code.type === 'RESIDUAL_AUTO') return this.showResidualAutoManualInstruction();
return this.code.type !== 'INTENDED_INCOMPLETE';
}

showSideInstruction(): boolean {
if (!this.code) return false;
if (this.code.type === 'RESIDUAL_AUTO') return this.showResidualAutoManualInstruction();
if (this.code.type === 'INTENDED_INCOMPLETE') return !!this.code.manualInstruction;
return true;
}

suppressResidualAutoWarning(): boolean {
return this.code?.type === 'RESIDUAL_AUTO' && this.showResidualAutoManualInstruction();
}

setCodeInvalid() {
if (this.code) {
this.code.id = 'INVALID';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@
[varInfo]="varInfo"
[fragmentMode]="!!varCoding.fragmenting"
[codeModel]="varCoding.codeModel"
[sourceType]="varCoding.sourceType"
[hasResidualAutoCode]="hasResidualAutoCode"
[hasIntendedIncompleteAutoCode]="hasIntendedIncompleteAutoCode"
[code]="code"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { CodingFactory } from '@iqb/responses/coding-factory';

import { VarCodingComponent } from './var-coding.component';
import { SchemerService } from '../services/schemer.service';
import { DEFAULT_RESIDUAL_MANUAL_INSTRUCTION } from '../services/schemer-code-ops';

describe('VarCodingComponent', () => {
let component: VarCodingComponent;
Expand Down Expand Up @@ -93,6 +94,27 @@ describe('VarCodingComponent', () => {
expect(emitSpy).toHaveBeenCalledWith(component.varCoding);
});

it('varCoding setter should add default manualInstruction to SOLVER RESIDUAL_AUTO codes', () => {
component.varCoding = {
id: 'v1',
alias: 'A',
sourceType: 'SOLVER',
codes: [
{
id: 0,
type: 'RESIDUAL_AUTO',
label: '',
score: 0,
manualInstruction: ''
}
]
} as unknown as VariableCodingData;

expect(component.varCoding?.codes?.[0].manualInstruction).toBe(
DEFAULT_RESIDUAL_MANUAL_INSTRUCTION
);
});

it('smartSchemer without ctrlKey should ignore false/null/undefined dialog results', () => {
const emitSpy = spyOn(component.varCodingChanged, 'emit');
component.varCoding = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
ConfirmDialogData
} from '../dialogs/confirm-dialog.component';
import { SchemerService } from '../services/schemer.service';
import { ensureResidualAutoManualInstruction } from '../services/schemer-code-ops';
import {
GenerateCodingDialogComponent,
GeneratedCodingData
Expand Down Expand Up @@ -102,6 +103,7 @@ export class VarCodingComponent implements OnInit, OnDestroy, OnChanges {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
set varCoding(value: VariableCodingData | null) {
this._varCoding = value;
this.applySolverResidualAutoInstructionDefault();
this.updateHasResidualAutoCode();
this.updateHasIntendedIncompleteAutoCode();
}
Expand Down Expand Up @@ -165,6 +167,12 @@ export class VarCodingComponent implements OnInit, OnDestroy, OnChanges {
false;
}

applySolverResidualAutoInstructionDefault() {
if (this.varCoding?.sourceType === 'SOLVER') {
(this.varCoding.codes || []).forEach(ensureResidualAutoManualInstruction);
}
}

getSanitizedText(text: string): SafeHtml {
return this.sanitizer.bypassSecurityTrustHtml(
renderManualInstructionMath(text)
Expand Down Expand Up @@ -312,6 +320,7 @@ export class VarCodingComponent implements OnInit, OnDestroy, OnChanges {
codes: newCoding.codes
});

this.applySolverResidualAutoInstructionDefault();
this.updateHasResidualAutoCode();
this.updateHasIntendedIncompleteAutoCode();
this.varCodingChanged.emit(this.varCoding);
Expand All @@ -337,6 +346,7 @@ export class VarCodingComponent implements OnInit, OnDestroy, OnChanges {
}
});
} else {
this.applySolverResidualAutoInstructionDefault();
this.updateHasResidualAutoCode();
this.updateHasIntendedIncompleteAutoCode();
this.varCodingChanged.emit(this.varCoding);
Expand Down Expand Up @@ -368,6 +378,7 @@ export class VarCodingComponent implements OnInit, OnDestroy, OnChanges {
}
});
} else {
this.applySolverResidualAutoInstructionDefault();
this.updateHasResidualAutoCode();
this.updateHasIntendedIncompleteAutoCode();
this.varCodingChanged.emit(this.varCoding);
Expand Down Expand Up @@ -408,6 +419,7 @@ export class VarCodingComponent implements OnInit, OnDestroy, OnChanges {
deriveSources
});

this.applySolverResidualAutoInstructionDefault();
this.varCodingChanged.emit(this.varCoding);
}
});
Expand Down
4 changes: 2 additions & 2 deletions src/assets/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@
"has-rule-warning": {
"autocode": "Dieser Code enthält Regeln für das automatische Kodieren.",
"manual": "Dieser Code enthält Instruktionen für das manuelle Kodieren.",
"manual-else": "Dieser Code enthält Instruktionen für das manuelle Kodieren. Durch die Regel 'Alle anderen Antworten' werden aber alle Fälle automatisch abschließend kodiert."
"manual-else": "Dieser Code enthält Instruktionen für das manuelle Kodieren. Im normalen automatischen Fall für 'Alle anderen Antworten' werden sie nicht genutzt."
}
},
"rule-set": {
Expand Down Expand Up @@ -229,4 +229,4 @@
"VALUE_COPY_NOT_FROM_BASE": "Es wurde ein Wert kopiert, aber nicht von einer Basisvariablen.",
"MORE_THAN_ONE_SOURCE": "Es wurde ein Wert kopiert, aber es gibt mehr als eine Quelle."
}
}
}
Loading