Commit a9d9c87a authored by Konstantin Schulz's avatar Konstantin Schulz
Browse files

client now supports exercise generation and export to moodle xml

parent 1c08388c
import {Component} from '@angular/core';
import {NavController} from 'ionic-angular';
import {CorpusProvider} from "../../providers/corpus/corpus";
import {NavController, NavParams} from 'ionic-angular';
import {AuthorPage} from "../author/author";
import {HttpClient} from "@angular/common/http";
import {TranslateService} from "@ngx-translate/core";
import {Author} from "../../models/author";
import {AuthorDetailPage} from "../author-detail/author-detail";
import {ExerciseProvider} from "../../providers/exercise/exercise";
import {CorpusProvider} from "../../providers/corpus/corpus";
import {AnnisResponse} from "../../models/annisResponse";
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
public isProductionEnv = ['localhost', '127.0.0.1'].indexOf(window.location.hostname) > -1 ? '' : 'hide';
private corpusUrl: string;
public authorsDisplayed: Author[];
constructor(public navCtrl: NavController,
public translate: TranslateService,
public corpusProvider: CorpusProvider,
public http: HttpClient) {
if (!this.corpusProvider.availableCorpora) {
this.corpusProvider.availableCorpora = [];
this.corpusProvider.availableAuthors = [];
this.translate.get("MC_API_CORPORA_URL").subscribe((value) => {
this.corpusUrl = value;
this.corpusProvider.getCorpora(this.corpusUrl);
});
}
this.authorsDisplayed = this.corpusProvider.availableAuthors;
}
getAuthors(event) { // Type: InputEvent
let value: string = event.target.value;
if (value === "") {
this.authorsDisplayed = this.corpusProvider.availableAuthors;
}
else if (!value || !value.trim()) {
this.authorsDisplayed = [];
}
else {
this.authorsDisplayed = this.corpusProvider.availableAuthors.filter((author: Author) => {
return HomePage.filterAuthor(author, value)
});
}
constructor(public navCtrl: NavController, public navParams: NavParams,
public http: HttpClient,
public exerciseProvider: ExerciseProvider,
public corpusProvider: CorpusProvider) {
}
static filterAuthor(author: Author, filterValue: string) {
return author.name.toLowerCase().includes(filterValue.toLowerCase());
goToAuthorPage() {
this.navCtrl.push(AuthorPage).then();
}
showCorpora(author: Author) {
this.corpusProvider.currentAuthor = author;
this.navCtrl.push(AuthorDetailPage).then();
test() {
let formData = new FormData();
// TODO: change the corpus title to something meaningful
this.corpusProvider.currentText = "Gallia est omnis divisa in partes tres et Galli fortes sunt.";
formData.append("title", "TestTitel");
formData.append("text", this.corpusProvider.currentText);
formData.append("type", "Lückentext");
formData.append("instructions", "Fülle die Lücke!");
this.http.post(this.exerciseProvider.MCapiExerciseUrlString, formData).subscribe((data: any) => {
let ar: AnnisResponse = data as AnnisResponse;
// let annisResponse = new AnnisResponse({
// graph: data,
// solution: {}
// });
let a = 0;
});
// let guid = "/8efea04d-aa11-4f75-b540-338dd0d1e435"; //"/45f1323f-0f82-4655-9236-276d8e76d76b";
// window.open("http://localhost:5000/mc/api/v1.0/file" + guid, "_blank");
// this.http.get("http://localhost:5000/mc/api/v1.0/file" + guid);
// .subscribe((retVal: any) => { // this.exerciseProvider.MCapiFileUrlString
// let a = 0;
// });
}
}
<!--
Generated template for the PreviewPage page.
See http://ionicframework.com/docs/components/#navigation for more info on
Ionic pages and navigation.
-->
<ion-header>
<ion-navbar>
<ion-title>{{ 'PREVIEW' | translate }}</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<div *ngIf="exerciseProvider.annisResponse; else loading">
<h1>{{ 'INSTRUCTIONS' | translate }}</h1>
<h4>{{ exerciseFillTheGapString }}: {{ instructionsFillTheGapString }}</h4>
<div *ngFor="let solution of exerciseProvider.annisResponse?.solution | dictToIterable">{{solution}}&#32;</div>
<br>
<br>
<!-- Do not use non-breaking whitespaces (&nbsp;). If you do, some browsers will start breaking lines on a character basis (after a resize occurred), which results in single words being cut into pieces at the end of a line. Use &#32; instead. -->
<div *ngFor="let node of exerciseProvider.annisResponse?.graph.nodes">{{node.annis_tok}}&#32;</div>
<br>
<br>
<!--<button ion-button block (click)="printExercise()">{{ 'PRINT' | translate }}</button>-->
<button ion-button block (click)="downloadExercise()">{{ 'EXPORT' | translate }}</button>
<br>
<br>
<h4>{{ 'EXERCISE_DOWNLOAD_NEXT_STEPS' | translate }}</h4>
<ol>
<li>{{ 'INSTRUCTION_LOGIN_MOODLE' | translate }}</li>
<li>{{ 'INSTRUCTION_COGWHEEL_MORE' | translate }}</li>
<li>{{ 'INSTRUCTION_GO_TO_QUESTION_BANK' | translate }}</li>
<li>{{ 'INSTRUCTION_CHOOSE_FORMAT_AND_IMPORT' | translate }}</li>
</ol>
</div>
<ng-template #loading>
<ion-spinner></ion-spinner>
</ng-template>
</ion-content>
import {NgModule} from '@angular/core';
import {IonicPageModule} from 'ionic-angular';
import {PreviewPage} from './preview';
import {TranslateModule} from "@ngx-translate/core";
import {PipesModule} from "../../pipes/pipes.module";
@NgModule({
declarations: [
PreviewPage,
],
imports: [
IonicPageModule.forChild(PreviewPage),
TranslateModule.forChild(),
PipesModule
],
})
export class PreviewPageModule {
}
page-preview {
div {
display: inline;
}
}
import {Component} from '@angular/core';
import {IonicPage, NavController, NavParams} from 'ionic-angular';
import {HttpClient} from "@angular/common/http";
import {AnnisResponse} from "../../models/annisResponse";
import {Graph} from "../../models/graph";
import {LinkMC} from "../../models/linkMC";
import {NodeMC} from "../../models/nodeMC";
import {ExerciseProvider} from "../../providers/exercise/exercise";
import {TranslateService} from "@ngx-translate/core";
import {CorpusProvider} from "../../providers/corpus/corpus";
/**
* Generated class for the PreviewPage page.
*
* See https://ionicframework.com/docs/components/#navigation for more info on
* Ionic pages and navigation.
*/
@IonicPage()
@Component({
selector: 'page-preview',
templateUrl: 'preview.html',
})
export class PreviewPage {
private exerciseFillTheGapString: string;
private instructionsFillTheGapString: string;
constructor(public navCtrl: NavController, public navParams: NavParams,
public http: HttpClient,
public exerciseProvider: ExerciseProvider,
public translateService: TranslateService,
public corpusProvider: CorpusProvider) {
this.translateService.get("EXERCISE_FILL_THE_GAP").subscribe((value) => {
this.exerciseFillTheGapString = value;
});
this.translateService.get("INSTRUCTIONS_FILL_THE_GAP").subscribe((value) => {
this.instructionsFillTheGapString = value;
});
if (!this.exerciseProvider.annisResponse) {
this.getExerciseData();
}
}
private getExerciseData() {
let formData = new FormData();
// TODO: change the corpus title to something meaningful
formData.append("title", "placeholder");
formData.append("text", this.corpusProvider.currentText);
formData.append("type", this.exerciseFillTheGapString);
formData.append("instructions", this.instructionsFillTheGapString);
this.http.post(this.exerciseProvider.MCapiExerciseUrlString, formData).subscribe((ar: AnnisResponse) => {
let maxGapLength = Math.max.apply(Math, Object.keys(ar.solution).map(key => ar.solution[key].length));
ar.graph.nodes.forEach((node) => {
if (node.annis_tok.startsWith("[[")) {
node.annis_tok = "_".repeat(maxGapLength)
}
});
this.exerciseProvider.annisResponse = ar;
});
}
printExercise() {
}
downloadExercise() {
let uriParts: string[] = this.exerciseProvider.annisResponse.uri.split("/");
window.open(this.exerciseProvider.MCapiFileUrlString + "/" + uriParts[uriParts.length - 1], "_blank");
}
}
......@@ -15,7 +15,40 @@
<ion-content padding>
<h1>{{currentTitleMinor}}</h1>
<p *ngIf="currentText.length > 0; else loading">{{currentText}}</p>
<div *ngIf="corpusProvider.currentText.length > 0; else loading">
<p>{{corpusProvider.currentText}}</p>
<h2>{{ 'EXERCISE_GENERATE' | translate }}</h2>
<ion-list>
<ion-item>
<ion-label>
{{ 'EXERCISE_TYPE' | translate }}
</ion-label>
<ion-select [(ngModel)]="exerciseProvider.exercise.type" name="exerciseType">
<ion-option value="{{ExerciseType.fillTheGap}}">{{ 'EXERCISE_FILL_THE_GAP' | translate }}
</ion-option>
</ion-select>
</ion-item>
<ion-item>
<ion-label>
{{ 'QUERY_PHENOMENON' | translate }}
</ion-label>
<ion-select [(ngModel)]="exerciseProvider.exercise.query.phenomenon" name="queryPhenomenon">
<ion-option value="{{Phenomenon.pos}}">{{ 'PHENOMENON_PART_OF_SPEECH' | translate }}</ion-option>
<!--<ion-option value="{{Phenomenon.dependency}}">{{ 'PHENOMENON_DEPENDENCY' | translate }}</ion-option>-->
</ion-select>
</ion-item>
<ion-item *ngIf="exerciseProvider.exercise.query.phenomenon === Phenomenon.pos">
<ion-label>
{{ 'QUERY_VALUE' | translate }}
</ion-label>
<ion-select [(ngModel)]="exerciseProvider.exercise.query.value" name="queryValue">
<ion-option value="{{PartOfSpeech.conjunction}}">{{ 'PART_OF_SPEECH_CONJUNCTION' | translate }}
</ion-option>
</ion-select>
</ion-item>
</ion-list>
<button ion-button (click)="generateExercise()">{{ 'PREVIEW' | translate }}</button>
</div>
<ng-template #loading>
<ion-spinner></ion-spinner>
</ng-template>
......
import {Component} from '@angular/core';
import {IonicPage, NavController, NavParams} from 'ionic-angular';
import {CorpusProvider} from "../../providers/corpus/corpus";
import {ExerciseType, PartOfSpeech, Phenomenon} from "../../models/enum";
import {ExerciseProvider} from "../../providers/exercise/exercise";
import {PreviewPage} from "../preview/preview";
/**
* Generated class for the ShowTextPage page.
......@@ -15,12 +18,15 @@ import {CorpusProvider} from "../../providers/corpus/corpus";
templateUrl: 'show-text.html',
})
export class ShowTextPage {
public ExerciseType = ExerciseType;
public Phenomenon = Phenomenon;
public PartOfSpeech = PartOfSpeech;
public currentText: string = "";
public currentTitleMinor: string = "";
constructor(public navCtrl: NavController, public navParams: NavParams,
public corpusProvider: CorpusProvider) {
this.currentText = "";
public corpusProvider: CorpusProvider,
public exerciseProvider: ExerciseProvider) {
let urnParts = this.corpusProvider.currentUrn.split(":");
let lastUrnPart: string = urnParts[urnParts.length - 1];
let citationLevels = lastUrnPart.split(".");
......@@ -28,16 +34,20 @@ export class ShowTextPage {
if (index === 0) {
this.currentTitleMinor += this.corpusProvider.currentCorpus.citation_level_1 + " " + value;
}
else if (index === 1){
else if (index === 1) {
this.currentTitleMinor += ", " + this.corpusProvider.currentCorpus.citation_level_2 + " " + value;
}
else{
else {
this.currentTitleMinor += ", " + this.corpusProvider.currentCorpus.citation_level_3 + " " + value;
}
});
this.corpusProvider.getCTStextPassage(this.corpusProvider.currentUrn).subscribe((data: object) => {
this.currentText = data["text"];
this.corpusProvider.currentText = data["text"];
});
}
generateExercise() {
this.exerciseProvider.annisResponse = null;
this.navCtrl.push(PreviewPage).then();
}
}
import {Pipe, PipeTransform} from '@angular/core';
/**
* Generated class for the DictToIterablePipe pipe.
*
* See https://angular.io/api/core/Pipe for more info on Angular Pipes.
*/
@Pipe({
name: 'dictToIterable',
})
export class DictToIterablePipe implements PipeTransform {
/**
* Takes a value and makes it lowercase.
*/
transform(value: object, ...args) {
if (!value) {
return [];
}
return Object.keys(value).map(key => value[key]);
}
}
import { NgModule } from '@angular/core';
import { DictToIterablePipe } from './dict-to-iterable/dict-to-iterable';
@NgModule({
declarations: [DictToIterablePipe],
imports: [],
exports: [DictToIterablePipe]
})
export class PipesModule {}
......@@ -29,6 +29,7 @@ export class CorpusProvider {
public currentUrn: string;
public xml2jsParser: Parser;
private MCapiRawTextUrlString: string;
public currentText: string = "";
constructor(public translate: TranslateService,
public http: HttpClient) {
......@@ -96,8 +97,7 @@ export class CorpusProvider {
}
getCorpora(corpusUrl: string) {
let resultObservable = this.http.get(corpusUrl);
resultObservable.subscribe((data: object) => {
this.http.get(corpusUrl).subscribe((data: object) => {
let corpusList: CorpusMC[] = data["corpora"] as CorpusMC[];
corpusList.forEach((corpus: CorpusMC) => {
corpus.subCorpora = {};
......
import {HttpClient} from '@angular/common/http';
import {Injectable} from '@angular/core';
import {ExerciseType, PartOfSpeech, Phenomenon} from "../../models/enum";
import {Exercise} from "../../models/exercise";
import {QueryMC} from "../../models/queryMC";
import {AnnisResponse} from "../../models/annisResponse";
import {TranslateService} from "@ngx-translate/core";
/*
Generated class for the ExerciseProvider provider.
See https://angular.io/guide/dependency-injection for more info on providers
and Angular DI.
*/
@Injectable()
export class ExerciseProvider {
public exercise: Exercise = new Exercise({
type: ExerciseType.fillTheGap,
query: new QueryMC({
phenomenon: Phenomenon.pos,
value: PartOfSpeech.conjunction
})
});
public annisResponse: AnnisResponse;
public MCapiExerciseUrlString: string;
public MCapiFileUrlString: string
constructor(public http: HttpClient,
public translateService: TranslateService) {
this.translateService.get("MC_API_EXERCISE_URL").subscribe((value) => {
this.MCapiExerciseUrlString = value;
});
this.translateService.get("MC_API_FILE_URL").subscribe((value) => {
this.MCapiFileUrlString = value;
});
}
}
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