Commit 58c742ea authored by Konstantin Schulz's avatar Konstantin Schulz
Browse files

exercise solutions can now be restricted to known vocabulary

parent 5cbce561
{
"name": "mcClient",
"version": "0.3.6",
"version": "0.4.5",
"author": "Ionic Framework",
"homepage": "http://ionicframework.com/",
"private": true,
......
{
"agldtTreebankUrl": "https://perseusdl.github.io/treebank_data/",
"backendApiCorporaPath": "corpora",
"backendApiExercisePath": "exercise",
"backendApiFilePath": "file",
......@@ -7,6 +8,7 @@
"backendApiVocabularyPath": "vocabulary",
"backendBaseUrl": "",
"backendProxyPath": "mc-service/mc/api/v1.0",
"bambergCoreVocabularyUrl": "https://www.ccbuchner.de/reihe-0-0/adeo-53/",
"callidusProjectUrl": "https://www.projekte.hu-berlin.de/de/callidus",
"developerMailTo": "mailto:sulzkons@hu-berlin.de",
"intervalCorporaUpdate": 1209600000,
......
......@@ -56,6 +56,7 @@
"EXERCISE_FEEDBACK_PARTIALLY_CORRECT": "teilweise korrekt",
"EXERCISE_FEEDBACK_PARTIALLY_CORRECT_DEFAULT": "Das ist teilweise korrekt.",
"EXERCISE_GENERATE": "Aufgabe erstellen",
"EXERCISE_NO_OOV": "Unbekannte Vokabeln ausschließen",
"EXERCISE_PARAMETERS": "Übungsparameter",
"EXERCISE_SET_PARAMETERS": "Übungsparameter festlegen",
"EXERCISE_TYPE": "Aufgabentyp",
......
......@@ -56,6 +56,7 @@
"EXERCISE_FEEDBACK_PARTIALLY_CORRECT": "partially correct",
"EXERCISE_FEEDBACK_PARTIALLY_CORRECT_DEFAULT": "That is partially correct.",
"EXERCISE_GENERATE": "Create exercise",
"EXERCISE_NO_OOV": "Exclude unknown words",
"EXERCISE_PARAMETERS": "Exercise parameters",
"EXERCISE_SET_PARAMETERS": "Set exercise parameters",
"EXERCISE_TYPE": "Exercise type",
......
......@@ -49,6 +49,12 @@
</ion-col>
</ion-row>
<ion-row>
<button large ion-button (click)="openUrl(HelperProvider.config['agldtTreebankUrl'])">
{{ 'VOCABULARY_REFERENCE_CORPUS_AGLDT' | translate }}
</button>
<button large ion-button (click)="openUrl(HelperProvider.config['bambergCoreVocabularyUrl'])">
{{ 'VOCABULARY_REFERENCE_CORPUS_BWS' | translate }}
</button>
<button large ion-button (click)="openUrl(HelperProvider.config['proielProjectUrl'])">
{{ 'PROIEL_PROJECT' | translate }}
</button>
......
......@@ -29,12 +29,27 @@
exerciseProvider.exercise.instructionsTranslation }}</h4>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<label>{{ "SOLUTIONS_SHUFFLE" | translate}}
<input type="checkbox" [(ngModel)]="areSolutionsShuffled"/>
</label>
</ion-col>
</ion-row>
<ion-row *ngIf="HelperProvider.isVocabularyCheck">
<ion-col>
<label padding-left>{{ "EXERCISE_NO_OOV" | translate}}
<input padding-right type="checkbox" [(ngModel)]="excludeOOV" (click)="switchOOV()"/>
</label>
<br>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<ion-grid *ngIf="exerciseProvider.exercise.type === ExerciseType.cloze; else matching">
<ion-row>
<ion-col>
<div *ngFor="let solution of (areSolutionsShuffled ? solutionsShuffled : exerciseProvider.annisResponse.solutions)">
<div *ngFor="let solution of (areSolutionsShuffled ? getShuffledSolutions(currentSolutions) : currentSolutions)">
{{solution.target.content}}&#32;
</div>
</ion-col>
......@@ -50,13 +65,6 @@
</ion-grid>
<ng-template #matching>
<ion-grid>
<ion-row>
<ion-col>
<label>{{ "SOLUTIONS_SHUFFLE" | translate}}
<input type="checkbox" [(ngModel)]="areSolutionsShuffled"/>
</label>
</ion-col>
</ion-row>
<ion-row>
<ion-col class="first">
<h5>{{ "GIVEN" | translate }}</h5>
......@@ -66,7 +74,7 @@
</ion-col>
</ion-row>
<ion-row
*ngFor="let solution of (areSolutionsShuffled ? solutionsShuffled : exerciseProvider.annisResponse.solutions)">
*ngFor="let solution of (areSolutionsShuffled ? getShuffledSolutions(currentSolutions) : currentSolutions)">
<ion-col class="first">
{{solution.target.content}}
</ion-col>
......
......@@ -15,4 +15,7 @@ page-preview {
.first {
max-width: 250px;
}
input {
float: left;
}
}
......@@ -29,19 +29,20 @@ export class PreviewPage {
public FileType = FileType;
public ExerciseTypeTranslation = ExerciseTypeTranslation;
public ObjectKeys = Object.keys;
public solutionsShuffled: Solution[];
public currentSolutions: Solution[];
public areSolutionsShuffled: boolean = false;
public currentNodes: NodeMC[] = [];
public maxGapLength: number = 0;
public solutionNodeIdSet: Set<string> = new Set<string>();
showInstructions: boolean = false;
public showInstructions: boolean = false;
public excludeOOV: boolean = false;
constructor(public navCtrl: NavController, public navParams: NavParams,
public http: HttpClient,
public exerciseProvider: ExerciseProvider,
public translateService: TranslateService,
public corpusProvider: CorpusProvider) {
this.solutionsShuffled = [];
this.currentSolutions = [];
this.currentNodes = [];
if (!this.exerciseProvider.annisResponse.solutions) {
this.getExerciseData();
......@@ -74,25 +75,13 @@ export class PreviewPage {
let uriParts: string[] = this.exerciseProvider.annisResponse.uri.split("/");
let fileId: string = uriParts[uriParts.length - 1];
let fileTypeString: string = "?type=" + type;
let solutionIndicesString: string = this.excludeOOV ? "&solution_indices=" + JSON.stringify(this.currentSolutions.map(x => this.exerciseProvider.annisResponse.solutions.indexOf(x))) : "";
let url = HelperProvider.config["backendBaseUrl"] + HelperProvider.config["backendApiFilePath"];
window.open(url + "/" + fileId + fileTypeString, "_blank");
window.open(url + "/" + fileId + fileTypeString + solutionIndicesString, "_blank");
}
private processAnnisResponse(ar: AnnisResponse) {
if (this.exerciseProvider.exercise.type === ExerciseType.cloze) {
this.maxGapLength = Math.max.apply(Math, ar.solutions.map(x => x.target.content.length));
this.solutionNodeIdSet = new Set(ar.solutions.map(x => x.target.salt_id));
}
let targets: SolutionElement[] = ar.solutions.map(x => x.target).sort(() => {
// TODO: for really random shuffling, implement the Fisher-Yates Shuffle algorithm
return 0.5 - Math.random();
});
targets.forEach((value: SolutionElement, index: number, array: SolutionElement[]) => {
this.solutionsShuffled.push(new Solution({target: value, value: ar.solutions[index].value}));
});
ar.solutions.sort((s1, s2) => {
return s1.target.content < s2.target.content ? -1 : (s1.target.content > s2.target.content ? 1 : 0);
});
this.processSolutions(ar.solutions);
this.exerciseProvider.annisResponse.uri = ar.uri;
this.exerciseProvider.annisResponse.solutions = ar.solutions;
this.exerciseProvider.annisResponse.graph = this.corpusProvider.currentUrn.startsWith("urn:") ? this.exerciseProvider.annisResponse.graph : ar.graph;
......@@ -101,4 +90,53 @@ export class PreviewPage {
getNodeById(id: string) {
return this.exerciseProvider.annisResponse.graph.nodes.find(x => x.id === id);
}
getShuffledSolutions(solutions: Solution[]) {
if (this.exerciseProvider.exercise.type === ExerciseType.cloze) {
return solutions.concat().sort((s1, s2) => {
return s1.target.content < s2.target.content ? -1 : (s1.target.content > s2.target.content ? 1 : 0);
});
}
else {
let newSolutions: Solution[] = [];
let targets: SolutionElement[] = solutions.map(x => x.target).sort(() => {
// TODO: for really random shuffling, implement the Fisher-Yates Shuffle algorithm
return 0.5 - Math.random();
});
targets.forEach((value: SolutionElement, index: number, array: SolutionElement[]) => {
newSolutions.push(new Solution({target: value, value: solutions[index].value}));
});
return newSolutions;
}
}
public processSolutions(solutions: Solution[]) {
let newSolutions: Solution[] = [];
if (this.exerciseProvider.exercise.type === ExerciseType.cloze) {
this.maxGapLength = Math.max.apply(Math, solutions.map(x => x.target.content.length));
this.solutionNodeIdSet = new Set(solutions.map(x => x.target.salt_id));
newSolutions = solutions.concat();
}
else {
newSolutions = solutions.concat().sort((s1, s2) => {
return s1.target.content < s2.target.content ? -1 : (s1.target.content > s2.target.content ? 1 : 0);
});
}
this.currentSolutions = newSolutions;
}
switchOOV() {
this.excludeOOV = !this.excludeOOV;
this.currentSolutions = [];
if (this.excludeOOV) {
let nodeIdSet: Set<string> = new Set(this.exerciseProvider.annisResponse.graph.nodes.filter(x => !x.is_oov).map(x => x.id));
let isCloze: boolean = this.exerciseProvider.exercise.type === ExerciseType.cloze;
let solutions: Solution[] = this.exerciseProvider.annisResponse.solutions.filter(x => nodeIdSet.has(x.target.salt_id) && (isCloze || nodeIdSet.has(x.value.salt_id)));
this.processSolutions(solutions);
}
else {
this.processSolutions(this.exerciseProvider.annisResponse.solutions);
}
}
}
......@@ -25,11 +25,10 @@
<ion-row>
<ion-col>
<ion-label>{{ 'VOCABULARY_QUERY_CORPUS' | translate }}</ion-label>
<button large ion-button color="secondary" (click)="chooseCorpus()">{{corpusProvider.currentAuthor ?
<div class="button-like" (click)="chooseCorpus()">{{corpusProvider.currentAuthor ?
(corpusProvider.currentAuthor.name + ": " + (corpusProvider.currentCorpus ?
corpusProvider.currentCorpus.title + " " + corpusProvider.currentTextRange.start + "-" +
corpusProvider.currentTextRange.end : "")) : 'VOCABULARY_CHOOSE_CORPUS' | translate}}
</button>
corpusProvider.currentTextRange.end : "")) : 'VOCABULARY_CHOOSE_CORPUS' | translate}}</div>
</ion-col>
</ion-row>
<ion-row>
......
page-vocabulary-check {
.button-like {
max-width: 99%;
text-decoration: none;
background-color: #f4f4f4;
color: #333333;
padding: 2px 6px 2px 6px;
border: 1px solid #CCCCCC;
border-right-color: #333333;
border-bottom-color: #333333;
cursor: pointer;
}
ion-content {
font-size: 1.5em;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment