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

client now supports exercise generation and export to moodle xml

parent 1c08388c
......@@ -2,7 +2,7 @@
## Installation:
1. Install Node.js (https://nodejs.org/), including NPM.
2. Clone the git repository: `git clone https://scm.cms.hu-berlin.de/callidus/mc-frontend.git`
3. Go to the new directory: `cd callidus`
3. Go to the new directory: `cd mc-frontend`
4. Run `npm install`
5. Run `sudo npm i -g ionic`
6. 1. Run `ionic serve` and enjoy!
......
{
"name": "mcClient",
"type": "ionic-angular",
"integrations": {
"cordova": {}
},
"app_id": ""
}
"type": "ionic-angular"
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -25,14 +25,14 @@
"@ionic-native/splash-screen": "4.7.0",
"@ionic-native/status-bar": "4.7.0",
"@ionic/storage": "2.1.3",
"@ngx-translate/core": "^9.0.2",
"@ngx-translate/core": "^9.1.1",
"@ngx-translate/http-loader": "^2.0.1",
"@types/xml2js": "^0.4.3",
"ionic": "^3.20.0",
"ionic": "^4.0.6",
"ionic-angular": "3.9.2",
"ionicons": "3.0.0",
"rxjs": "5.5.11",
"socks": "^2.2.0",
"socks": "^2.2.1",
"sw-toolbox": "3.6.0",
"zone.js": "0.8.26"
},
......@@ -40,5 +40,8 @@
"@ionic/app-scripts": "3.1.10",
"typescript": "~2.6.2"
},
"description": "An Ionic project"
"description": "An Ionic project",
"cordova": {
"plugins": {}
}
}
......@@ -13,6 +13,9 @@ import {TranslateHttpLoader} from '@ngx-translate/http-loader';
import {AuthorDetailPageModule} from "../pages/author-detail/author-detail.module";
import {CorpusDetailPageModule} from "../pages/corpus-detail/corpus-detail.module";
import {ShowTextPageModule} from "../pages/show-text/show-text.module";
import {ExerciseProvider} from '../providers/exercise/exercise';
import {PreviewPageModule} from "../pages/preview/preview.module";
import {AuthorPageModule} from "../pages/author/author.module";
// The translate loader needs to know where to load i18n files
// in Ionic's static asset pipeline.
......@@ -43,7 +46,9 @@ class CustomErrorHandler implements ErrorHandler {
}
}),
AuthorDetailPageModule,
AuthorPageModule,
CorpusDetailPageModule,
PreviewPageModule,
ShowTextPageModule
],
bootstrap: [IonicApp],
......@@ -54,7 +59,8 @@ class CustomErrorHandler implements ErrorHandler {
StatusBar,
SplashScreen,
{provide: ErrorHandler, useClass: IonicErrorHandler}, // CustomErrorHandler
CorpusProvider
CorpusProvider,
ExerciseProvider
]
})
export class AppModule {
......
......@@ -4,11 +4,33 @@
"AUTHOR_SELECT": "Autor auswählen",
"CORPUS_DETAIL_TITLE": "Korpus-Details",
"CORPUS_SELECT": "Korpus auswählen",
"EXERCISE_DOWNLOAD_NEXT_STEPS": "Nächste Schritte:",
"EXERCISE_FILL_THE_GAP": "Lückentext",
"EXERCISE_GENERATE": "Aufgabe erstellen",
"EXERCISE_TYPE": "Aufgabentyp",
"EXPORT": "Exportieren",
"INSTRUCTION_CHOOSE_FORMAT_AND_IMPORT": "Wählen Sie als Dateiformat das 'Moodle-XML-Format'. Laden Sie anschließend die soeben erhaltene XML-Datei hoch und klicken Sie auf 'Import'.",
"INSTRUCTION_COGWHEEL_MORE": "Klicken Sie auf das Zahnrad-Symbol oben rechts, dann auf 'Mehr'.",
"INSTRUCTION_GO_TO_QUESTION_BANK": "Klicken Sie auf 'Fragensammlung', dann auf 'Import'.",
"INSTRUCTION_LOGIN_MOODLE": "Loggen Sie sich in Moodle ein und betreten Sie den Kurs, der die Übung enthalten soll.",
"INSTRUCTIONS": "Anweisungen",
"INSTRUCTIONS_FILL_THE_GAP": "Ordne die Wörter aus dem Pool den richtigen Lücken zu!",
"MC_API_CORPORA_URL": "http://141.20.186.246:5000/mc/api/v1.0/corpora",
"MC_API_EXERCISE_URL": "http://141.20.186.246:5000/mc/api/v1.0/exercise",
"MC_API_FILE_URL": "http://141.20.186.246:5000/mc/api/v1.0/file",
"MC_API_RAW_TEXT_URL": "http://141.20.186.246:5000/mc/api/v1.0/rawtext",
"PART_OF_SPEECH_CONJUNCTION": "Konjunktion",
"PERSEIDS_CTS_API_BASE_URL": "https://cts.perseids.org/api/cts?request=",
"PERSEIDS_CTS_API_GET_CAPABILITIES": "GetCapabilities",
"PERSEIDS_CTS_API_GET_TEXT_PASSAGE": "GetPassage",
"PERSEIDS_CTS_API_GET_VALID_REFF": "GetValidReff",
"PERSEIDS_CTS_API_URN_SNIPPET": "&urn="
"PERSEIDS_CTS_API_URN_SNIPPET": "&urn=",
"PHENOMENON_DEPENDENCY": "Dependenz",
"PHENOMENON_PART_OF_SPEECH": "Wortart",
"PREVIEW": "Vorschau",
"PRINT": "Drucken",
"QUERY_PHENOMENON": "Phänomen",
"QUERY_VALUE": "Suche",
"TEST": "Test",
"WELCOME": "Willkommen!"
}
\ No newline at end of file
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<title>Ionic App</title>
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<link rel="icon" type="image/x-icon" href="assets/icon/favicon.ico">
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#4e8ef7">
<!-- add to homescreen for ios -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<!-- cordova.js required for cordova apps (remove if not needed) -->
<script src="cordova.js"></script>
<!-- un-comment this code to enable service worker
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js')
.then(() => console.log('service worker installed'))
.catch(err => console.error('Error', err));
}
</script>-->
<link href="build/main.css" rel="stylesheet">
<meta charset="UTF-8">
<title>Ionic App</title>
<meta name="viewport"
content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<link rel="icon" type="image/x-icon" href="assets/icon/favicon.ico">
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#4e8ef7">
<!-- add to homescreen for ios -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<!-- cordova.js required for cordova apps (remove if not needed) -->
<script src="cordova.js"></script>
<!-- un-comment this code to enable service worker
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js')
.then(() => console.log('service worker installed'))
.catch(err => console.error('Error', err));
}
</script>-->
<link href="build/main.css" rel="stylesheet">
</head>
<body>
<!-- Ionic's root component and where the app will load -->
<ion-app></ion-app>
<!-- Ionic's root component and where the app will load -->
<ion-app></ion-app>
<!-- The polyfills js is generated during the build process -->
<script src="build/polyfills.js"></script>
<!-- The polyfills js is generated during the build process -->
<script src="build/polyfills.js"></script>
<!-- The vendor js is generated during the build process
It contains all of the dependencies in node_modules -->
<script src="build/vendor.js"></script>
<!-- The vendor js is generated during the build process
It contains all of the dependencies in node_modules -->
<script src="build/vendor.js"></script>
<!-- The main bundle js is generated during the build process -->
<script src="build/main.js"></script>
<!-- The main bundle js is generated during the build process -->
<script src="build/main.js"></script>
</body>
</html>
import {Graph} from "./graph";
export class AnnisResponse {
public graph: Graph;
public solution: { [targetNodeId: string]: string };
public uri: string;
constructor(init?: Partial<AnnisResponse>) {
Object.assign(this, init);
}
}
\ No newline at end of file
export enum CitationLevel {
default = <any>"default"
}
export enum ExerciseType {
fillTheGap = <any>"fillTheGap"
}
export enum Phenomenon {
pos = <any>"pos",
dependency = <any>"dependency"
}
export enum PartOfSpeech {
conjunction = <any>"conjunction"
}
import {ExerciseType} from "./enum";
import {QueryMC} from "./queryMC";
export class Exercise {
public type: ExerciseType;
public query: QueryMC;
constructor(init?:Partial<Exercise>) {
Object.assign(this, init);
}
}
import {LinkMC} from "./linkMC";
import {NodeMC} from "./nodeMC";
export class Graph {
public directed: boolean;
public graph: object;
public links: LinkMC[];
public multigraph: boolean;
public nodes: NodeMC[];
constructor(init?:Partial<Graph>) {
Object.assign(this, init);
}
}
\ No newline at end of file
export class LinkMC {
public annis_component_name: string;
public annis_component_type: string;
public source: string;
public target: string;
public udep_deprel: string;
constructor(init?:Partial<LinkMC>) {
Object.assign(this, init);
}
}
\ No newline at end of file
export class NodeMC {
public annis_node_name: string;
public annis_node_type: string;
public annis_tok: string;
public annis_type: string;
public id: string;
public udep_lemma: string;
public udep_upostag: string;
public udep_xpostag: string;
public solution: string;
constructor(init?:Partial<NodeMC>) {
Object.assign(this, init);
}
}
\ No newline at end of file
import {PartOfSpeech, Phenomenon} from "./enum";
export class QueryMC {
public phenomenon: Phenomenon;
public value: PartOfSpeech;
constructor(init?:Partial<QueryMC>) {
Object.assign(this, init);
}
}
<ion-header>
<ion-navbar>
<ion-title>
{{ 'AUTHOR_SELECT' | translate }}
</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-searchbar (ionInput)="getAuthors($event)" placeholder="{{ 'AUTHOR_SEARCH' | translate }}"></ion-searchbar>
<ion-list *ngIf="corpusProvider.availableAuthors.length > 0; else loading">
<ion-title>
{{ 'AUTHORS' | translate }}
</ion-title>
<ion-item-sliding *ngFor="let author of authorsDisplayed">
<button ion-item (click)="showCorpora(author)">
<p>{{author.name}}</p>
</button>
</ion-item-sliding>
</ion-list>
<ng-template #loading>
<ion-spinner></ion-spinner>
</ng-template>
</ion-content>
import {NgModule} from '@angular/core';
import {IonicPageModule} from 'ionic-angular';
import {AuthorPage} from './author';
import {TranslateModule} from "@ngx-translate/core";
@NgModule({
declarations: [
AuthorPage,
],
imports: [
IonicPageModule.forChild(AuthorPage),
TranslateModule.forChild()
],
})
export class AuthorPageModule {
}
import {Component} from '@angular/core';
import {IonicPage, NavController, NavParams} from 'ionic-angular';
import {HttpClient} from "@angular/common/http";
import {TranslateService} from "@ngx-translate/core";
import {HomePage} from "../home/home";
import {Author} from "../../models/author";
import {CorpusProvider} from "../../providers/corpus/corpus";
import {AuthorDetailPage} from "../author-detail/author-detail";
/**
* Generated class for the AuthorPage page.
*
* See https://ionicframework.com/docs/components/#navigation for more info on
* Ionic pages and navigation.
*/
@IonicPage()
@Component({
selector: 'page-author',
templateUrl: 'author.html',
})
export class AuthorPage {
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 AuthorPage.filterAuthor(author, value)
});
}
}
static filterAuthor(author: Author, filterValue: string) {
return author.name.toLowerCase().includes(filterValue.toLowerCase());
}
showCorpora(author: Author) {
this.corpusProvider.currentAuthor = author;
this.navCtrl.push(AuthorDetailPage).then();
}
}
......@@ -68,6 +68,7 @@ export class CorpusDetailPage {
}, (error: HttpErrorResponse) => {
// no further references available, go to text display
this.corpusProvider.currentUrn = urn;
this.corpusProvider.currentText = "";
this.navCtrl.push(ShowTextPage).then();
});
}
......
<ion-header>
<ion-navbar>
<ion-title>
{{ 'AUTHOR_SELECT' | translate }}
{{ 'WELCOME' | translate }}
</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-searchbar (ionInput)="getAuthors($event)" placeholder="{{ 'AUTHOR_SEARCH' | translate }}"></ion-searchbar>
<ion-list *ngIf="corpusProvider.availableAuthors.length > 0; else loading">
<ion-title>
{{ 'AUTHORS' | translate }}
</ion-title>
<ion-item-sliding *ngFor="let author of authorsDisplayed">
<button ion-item (click)="showCorpora(author)">
<p>{{author.name}}</p>
</button>
</ion-item-sliding>
</ion-list>
<ng-template #loading>
<ion-spinner></ion-spinner>
</ng-template>
<button ion-button block (click)="goToAuthorPage()">{{ 'AUTHOR_SELECT' | translate }}</button>
<button ion-button block (click)="test()" [ngClass]="isProductionEnv">{{ 'TEST' | translate }}</button>
</ion-content>
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