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

new feature: exercise list can now sort exercises by the text complexity of...

new feature: exercise list can now sort exercises by the text complexity of their respective base text
parent 5d8daaa4
# Installation instructions (Docker): # Installation
## via Docker:
1. Install Docker (https://docs.docker.com/v17.12/install/) and Docker-Compose (https://docs.docker.com/compose/install/) 1. Install Docker (https://docs.docker.com/v17.12/install/) and Docker-Compose (https://docs.docker.com/compose/install/)
2. Clone the repo: 2. Clone the repo:
`git clone https://scm.cms.hu-berlin.de/callidus/mc_frontend.git` `git clone https://scm.cms.hu-berlin.de/callidus/mc_frontend.git`
...@@ -7,25 +8,35 @@ ...@@ -7,25 +8,35 @@
4. Run `docker-compose build`. 4. Run `docker-compose build`.
Make sure to assign at least 4GB RAM (Memory) to the Docker container, otherwise the build will fail. Make sure to assign at least 4GB RAM (Memory) to the Docker container, otherwise the build will fail.
5. Run `docker-compose up -d` and enjoy! 5. Run `docker-compose up -d` and enjoy!
## via Command Line:
1. Clone the repo: `git clone https://scm.cms.hu-berlin.de/callidus/mc_frontend.git`
2. Move to the newly created folder: `cd mc_frontend`
3. Run `npm install`
4. Run `npm install -g @angular/cli` (you may need `sudo`).
5. Check that the Angular command line interface is installed by running `which ng`. It should print the path to the Angular CLI executable.
6. Run `npm start`.
If you already ran `npm install` and the CLI still complains about missing dependencies, install them one by one using `npm install DEPENDENCY_NAME`.
7. Open http://localhost:8100 in your browser.
---------------------------------------------------------------- ----------------------------------------------------------------
##### Production Build ## Production Build
To build the application for production environments, use: `ionic cordova build browser --prod --release --max-old-space-size=4096` and serve the content of the `www/` folder, e.g. with Nginx. To build the application for production environments, use: `ionic cordova build browser --prod --release --max-old-space-size=4096` and serve the content of the `www/` folder, e.g. with Nginx.
---------------------------------------------------------------- ----------------------------------------------------------------
##### Development # Development
If you don't want to use Docker for development, you need to run `npm install` first, followed by `npm install @angular/cli`.
To spin up a local web server, run: `ng serve`. If you already ran `npm install` and the CLI still complains about missing dependencies, install them one by one using `npm install DEPENDENCY_NAME`.
To add new pages to the application, use: `ionic generate page PAGE_NAME`. To add new pages to the application, use: `ionic generate page PAGE_NAME`.
---------------------------------------------------------------- ----------------------------------------------------------------
##### Access to the Docker container # Access to the Docker container
Use `docker-compose down` to stop and remove the currently running containers. Use `docker-compose down` to stop and remove the currently running containers.
To access a running container directly, get the container ID via `docker ps` and connect via `docker exec -it CONTAINER_ID bash`. Or, for root access, use: `docker exec -u 0 -it CONTAINER_ID bash` To access a running container directly, get the container ID via `docker ps` and connect via `docker exec -it CONTAINER_ID bash`. Or, for root access, use: `docker exec -u 0 -it CONTAINER_ID bash`
To snapshot a running container, use `docker commit CONTAINER_ID`. It returns a snapshot ID, which you can access via `docker run -it SNAPSHOT_ID`. To snapshot a running container, use `docker commit CONTAINER_ID`. It returns a snapshot ID, which you can access via `docker run -it SNAPSHOT_ID`.
---------------------------------------------------------------- ----------------------------------------------------------------
## Configuration # Configuration
### Backend URL ## Backend URL
To change the URL for the backend, use the `ionic.config.json` file (proxies > proxyUrl). By default, the system assumes that backend and frontend are installed on the same machine. To change the URL for the backend, use the `ionic.config.json` file (proxies > proxyUrl). By default, the system assumes that backend and frontend are installed on the same machine.
### Frontend URL ## Frontend URL
Use the `--host 0.0.0.0 --disable-host-check` flag for `ng serve` if you want to use it in a production environment with an Nginx server using proxy_pass. Use the `--host 0.0.0.0 --disable-host-check` flag for `ng serve` if you want to use it in a production environment with an Nginx server using proxy_pass.
### Other ## Other
For all other kinds of configuration, use `src/assets/config.json`. For all other kinds of configuration, use `src/assets/config.json`.
{ {
"name": "mc_frontend", "name": "mc_frontend",
"version": "1.3.0", "version": "1.3.2",
"author": "Ionic Framework", "author": "Ionic Framework",
"homepage": "https://ionicframework.com/", "homepage": "https://ionicframework.com/",
"scripts": { "scripts": {
......
...@@ -86,22 +86,35 @@ ...@@ -86,22 +86,35 @@
disabled="{{!hasChanges}}">{{ 'APPLY' | translate }}</ion-button> disabled="{{!hasChanges}}">{{ 'APPLY' | translate }}</ion-button>
</ion-col> </ion-col>
</ion-row> </ion-row>
<ion-row><ion-col></ion-col></ion-row>
<ion-row> <ion-row>
<ion-col></ion-col> <ion-col>{{ 'EXERCISE_LIST_LEGEND' | translate}}:</ion-col>
<ion-col><span class="span-tc">{{ 'TEXT_COMPLEXITY' | translate}}</span></ion-col>
<ion-col><span class="span-md">{{ 'VOCABULARY_MATCHING_DEGREE' | translate}}</span></ion-col>
</ion-row> </ion-row>
<ion-row *ngIf="exercises; else loading"> <ion-row *ngIf="exercises; else loading">
<ion-col> <ion-col>
<ion-list> <ion-list>
<ion-item *ngFor="let exercise of exercises"> <ion-item *ngFor="let exercise of exercises">
<ion-list (click)="showExercise(exercise)"> <ion-grid (click)="showExercise(exercise)" class="exercises">
<ion-item lines="none" class="item-nested-top"> <ion-row class="item-nested-top">
{{exercise.work_author}}: {{exercise.work_title}} <ion-col>
</ion-item> {{exercise.work_author}}: {{exercise.work_title}}
<ion-item lines="none" class="item-nested-bottom"> </ion-col>
{{exercise.exercise_type_translation}} ({{ getDateString(exercise.last_access_time) }}) </ion-row>
{{getMatchingDegree(exercise)}} <ion-row class="item-nested-bottom">
</ion-item> <ion-col size="8">
</ion-list> <span>{{exercise.exercise_type_translation}}
({{ getDateString(exercise.last_access_time) }})</span>
</ion-col>
<ion-col size="2">
<span class="span-tc">{{Math.round(exercise.text_complexity)}}</span>
</ion-col>
<ion-col size="2">
<span class="span-md">{{getMatchingDegree(exercise)}}</span>
</ion-col>
</ion-row>
</ion-grid>
</ion-item> </ion-item>
</ion-list> </ion-list>
</ion-col> </ion-col>
......
...@@ -2,6 +2,10 @@ ion-list { ...@@ -2,6 +2,10 @@ ion-list {
cursor: pointer; cursor: pointer;
} }
.exercises {
padding: 0;
}
.item-nested-top { .item-nested-top {
max-height: 2.6em; max-height: 2.6em;
} }
...@@ -9,3 +13,11 @@ ion-list { ...@@ -9,3 +13,11 @@ ion-list {
.item-nested-bottom { .item-nested-bottom {
max-height: 2.1em; max-height: 2.1em;
} }
.span-md {
color: #0e407a;
}
.span-tc {
color: #1a73d9;
}
...@@ -3,7 +3,7 @@ import {Component, OnInit} from '@angular/core'; ...@@ -3,7 +3,7 @@ import {Component, OnInit} from '@angular/core';
import {HelperService} from 'src/app/helper.service'; import {HelperService} from 'src/app/helper.service';
import {NavController, ToastController} from '@ionic/angular'; import {NavController, ToastController} from '@ionic/angular';
import {ExerciseMC} from 'src/app/models/exerciseMC'; import {ExerciseMC} from 'src/app/models/exerciseMC';
import {HttpClient, HttpErrorResponse, HttpParams} from '@angular/common/http'; import {HttpClient, HttpParams} from '@angular/common/http';
import { import {
ExerciseType, ExerciseType,
ExerciseTypeTranslation, ExerciseTypeTranslation,
...@@ -31,10 +31,25 @@ export class ExerciseListPage implements OnInit { ...@@ -31,10 +31,25 @@ export class ExerciseListPage implements OnInit {
public ExerciseTypeTranslation = ExerciseTypeTranslation; public ExerciseTypeTranslation = ExerciseTypeTranslation;
public hasChanges = false; public hasChanges = false;
public HelperService = HelperService; public HelperService = HelperService;
public Math = Math;
public metadata: { [eid: string]: string } = {}; public metadata: { [eid: string]: string } = {};
public ObjectKeys = Object.keys; public ObjectKeys = Object.keys;
public showVocabularyCorpus = false; public showVocabularyCorpus = false;
public SortingCategory = SortingCategory; public SortingCategory = SortingCategory;
public sortingCategoriesAsc: Set<SortingCategory> = new Set<SortingCategory>([
SortingCategory.vocAsc, SortingCategory.authorAsc, SortingCategory.dateAsc, SortingCategory.typeAsc]);
public sortingCategoriesMap: { [sc: string]: string } = {
[SortingCategory.authorAsc]: 'work_author',
[SortingCategory.authorDesc]: 'work_author',
[SortingCategory.complexityAsc]: 'text_complexity',
[SortingCategory.complexityDesc]: 'text_complexity',
[SortingCategory.dateAsc]: 'last_access_time',
[SortingCategory.dateDesc]: 'last_access_time',
[SortingCategory.typeAsc]: 'exercise_type_translation',
[SortingCategory.typeDesc]: 'exercise_type_translation',
[SortingCategory.vocAsc]: 'matching_degree',
[SortingCategory.vocDesc]: 'matching_degree',
};
public sortingCategoriesVocCheck: Set<SortingCategory> = new Set([SortingCategory.vocAsc, SortingCategory.vocDesc]); public sortingCategoriesVocCheck: Set<SortingCategory> = new Set([SortingCategory.vocAsc, SortingCategory.vocDesc]);
public VocabularyCorpus = VocabularyCorpus; public VocabularyCorpus = VocabularyCorpus;
public VocabularyCorpusTranslation = VocabularyCorpusTranslation; public VocabularyCorpusTranslation = VocabularyCorpusTranslation;
...@@ -88,10 +103,7 @@ export class ExerciseListPage implements OnInit { ...@@ -88,10 +103,7 @@ export class ExerciseListPage implements OnInit {
} }
getMatchingDegree(exercise: ExerciseMC): string { getMatchingDegree(exercise: ExerciseMC): string {
if (exercise.matching_degree) { return exercise.matching_degree ? Math.round(exercise.matching_degree).toString() : '';
return `[${Math.round(exercise.matching_degree)}%]`;
}
return '';
} }
ngOnInit(): void { ngOnInit(): void {
...@@ -118,53 +130,15 @@ export class ExerciseListPage implements OnInit { ...@@ -118,53 +130,15 @@ export class ExerciseListPage implements OnInit {
this.sortingCategoriesVocCheck.has(this.currentSortingCategory) && !this.exercises.some(x => !!x.matching_degree)) { this.sortingCategoriesVocCheck.has(this.currentSortingCategory) && !this.exercises.some(x => !!x.matching_degree)) {
return; return;
} }
switch (this.currentSortingCategory) { const property: string = this.sortingCategoriesMap[this.currentSortingCategory.toString()];
case SortingCategory.authorAsc: if (this.sortingCategoriesAsc.has(this.currentSortingCategory)) {
this.exercises.sort((a: ExerciseMC, b: ExerciseMC) => { this.exercises.sort((a: ExerciseMC, b: ExerciseMC) => {
return a.work_author === b.work_author ? 0 : (a.work_author < b.work_author ? -1 : 1); return a[property] === b[property] ? 0 : (a[property] < b[property] ? -1 : 1);
}); });
break; } else {
case SortingCategory.authorDesc: this.exercises.sort((a: ExerciseMC, b: ExerciseMC) => {
this.exercises.sort((a: ExerciseMC, b: ExerciseMC) => { return a[property] === b[property] ? 0 : (a[property] < b[property] ? 1 : -1);
return a.work_author === b.work_author ? 0 : (a.work_author < b.work_author ? 1 : -1); });
});
break;
case SortingCategory.dateAsc:
this.exercises.sort((a: ExerciseMC, b: ExerciseMC) => {
return a.last_access_time === b.last_access_time ? 0 : (a.last_access_time < b.last_access_time ? -1 : 1);
});
break;
case SortingCategory.dateDesc:
this.exercises.sort((a: ExerciseMC, b: ExerciseMC) => {
return a.last_access_time === b.last_access_time ? 0 : (a.last_access_time < b.last_access_time ? 1 : -1);
});
break;
case SortingCategory.typeAsc:
this.exercises.sort((a: ExerciseMC, b: ExerciseMC) => {
return a.exercise_type_translation === b.exercise_type_translation ? 0 :
(a.exercise_type_translation < b.exercise_type_translation ? -1 : 1);
});
break;
case SortingCategory.typeDesc:
this.exercises.sort((a: ExerciseMC, b: ExerciseMC) => {
return a.exercise_type_translation === b.exercise_type_translation ? 0 :
(a.exercise_type_translation < b.exercise_type_translation ? 1 : -1);
});
break;
case SortingCategory.vocAsc:
this.exercises.sort((a: ExerciseMC, b: ExerciseMC) => {
return a.matching_degree === b.matching_degree ? 0 :
(a.matching_degree < b.matching_degree ? -1 : 1);
});
break;
case SortingCategory.vocDesc:
this.exercises.sort((a: ExerciseMC, b: ExerciseMC) => {
return a.matching_degree === b.matching_degree ? 0 :
(a.matching_degree < b.matching_degree ? 1 : -1);
});
break;
default:
break;
} }
} }
} }
...@@ -177,6 +177,8 @@ export enum SortingCategory { ...@@ -177,6 +177,8 @@ export enum SortingCategory {
authorDesc = 'SORTING_CATEGORY_AUTHOR_DESCENDING' as any, authorDesc = 'SORTING_CATEGORY_AUTHOR_DESCENDING' as any,
dateAsc = 'SORTING_CATEGORY_DATE_ASCENDING' as any, dateAsc = 'SORTING_CATEGORY_DATE_ASCENDING' as any,
dateDesc = 'SORTING_CATEGORY_DATE_DESCENDING' as any, dateDesc = 'SORTING_CATEGORY_DATE_DESCENDING' as any,
complexityAsc = 'SORTING_CATEGORY_TEXT_COMPLEXITY_ASCENDING' as any,
complexityDesc = 'SORTING_CATEGORY_TEXT_COMPLEXITY_DESCENDING' as any,
typeAsc = 'SORTING_CATEGORY_TYPE_ASCENDING' as any, typeAsc = 'SORTING_CATEGORY_TYPE_ASCENDING' as any,
typeDesc = 'SORTING_CATEGORY_TYPE_DESCENDING' as any, typeDesc = 'SORTING_CATEGORY_TYPE_DESCENDING' as any,
vocAsc = 'SORTING_CATEGORY_VOCABULARY_ASCENDING' as any, vocAsc = 'SORTING_CATEGORY_VOCABULARY_ASCENDING' as any,
......
...@@ -15,6 +15,7 @@ export class ExerciseMC { ...@@ -15,6 +15,7 @@ export class ExerciseMC {
public partially_correct_feedback: string; public partially_correct_feedback: string;
public search_values: string[]; public search_values: string[];
public solutions: Solution[]; public solutions: Solution[];
public text_complexity: number;
public uri: string; public uri: string;
public work_author: string; public work_author: string;
public work_title: string; public work_title: string;
......
...@@ -24,6 +24,7 @@ export class ShowTextPage { ...@@ -24,6 +24,7 @@ export class ShowTextPage {
public isDownloading = false; public isDownloading = false;
public showTextComplexity = false; public showTextComplexity = false;
public textComplexityMap = { public textComplexityMap = {
all: 'TEXT_COMPLEXITY_ALL',
n_w: 'TEXT_COMPLEXITY_WORD_COUNT', n_w: 'TEXT_COMPLEXITY_WORD_COUNT',
n_sent: 'TEXT_COMPLEXITY_SENTENCE_COUNT', n_sent: 'TEXT_COMPLEXITY_SENTENCE_COUNT',
avg_w_per_sent: 'TEXT_COMPLEXITY_AVERAGE_SENTENCE_LENGTH', avg_w_per_sent: 'TEXT_COMPLEXITY_AVERAGE_SENTENCE_LENGTH',
......
...@@ -71,6 +71,7 @@ ...@@ -71,6 +71,7 @@
"EXERCISE_FEEDBACK_PARTIALLY_CORRECT_DEFAULT": "Das ist teilweise korrekt.", "EXERCISE_FEEDBACK_PARTIALLY_CORRECT_DEFAULT": "Das ist teilweise korrekt.",
"EXERCISE_GENERATE": "Übung erstellen", "EXERCISE_GENERATE": "Übung erstellen",
"EXERCISE_LIST": "Übungsdatenbank", "EXERCISE_LIST": "Übungsdatenbank",
"EXERCISE_LIST_LEGEND": "Legende",
"EXERCISE_NO_OOV": "Unbekannte Vokabeln ausschließen", "EXERCISE_NO_OOV": "Unbekannte Vokabeln ausschließen",
"EXERCISE_PARAMETERS": "Übungsparameter", "EXERCISE_PARAMETERS": "Übungsparameter",
"EXERCISE_SET_PARAMETERS": "Parameter festlegen", "EXERCISE_SET_PARAMETERS": "Parameter festlegen",
...@@ -143,6 +144,8 @@ ...@@ -143,6 +144,8 @@
"SORTING_CATEGORY_AUTHOR_DESCENDING": "Autor (absteigend)", "SORTING_CATEGORY_AUTHOR_DESCENDING": "Autor (absteigend)",
"SORTING_CATEGORY_DATE_ASCENDING": "Datum (aufsteigend)", "SORTING_CATEGORY_DATE_ASCENDING": "Datum (aufsteigend)",
"SORTING_CATEGORY_DATE_DESCENDING": "Datum (absteigend)", "SORTING_CATEGORY_DATE_DESCENDING": "Datum (absteigend)",
"SORTING_CATEGORY_TEXT_COMPLEXITY_ASCENDING": "Komplexität (aufsteigend)",
"SORTING_CATEGORY_TEXT_COMPLEXITY_DESCENDING": "Komplexität (absteigend)",
"SORTING_CATEGORY_TYPE_ASCENDING": "Typ (aufsteigend)", "SORTING_CATEGORY_TYPE_ASCENDING": "Typ (aufsteigend)",
"SORTING_CATEGORY_TYPE_DESCENDING": "Typ (absteigend)", "SORTING_CATEGORY_TYPE_DESCENDING": "Typ (absteigend)",
"SORTING_CATEGORY_VOCABULARY_ASCENDING": "Vokabular (aufsteigend)", "SORTING_CATEGORY_VOCABULARY_ASCENDING": "Vokabular (aufsteigend)",
...@@ -160,6 +163,7 @@ ...@@ -160,6 +163,7 @@
"TEST_REPEAT": "Einheit wiederholen", "TEST_REPEAT": "Einheit wiederholen",
"TEXT_COMPLEXITY": "Textkomplexität", "TEXT_COMPLEXITY": "Textkomplexität",
"TEXT_COMPLEXITY_ABLATIVI_ABSOLUTI_COUNT": "Anzahl der Ablativi Absoluti", "TEXT_COMPLEXITY_ABLATIVI_ABSOLUTI_COUNT": "Anzahl der Ablativi Absoluti",
"TEXT_COMPLEXITY_ALL": "Gesamtschwierigkeit",
"TEXT_COMPLEXITY_AVERAGE_SENTENCE_LENGTH": "Wörter pro Satz (Ø)", "TEXT_COMPLEXITY_AVERAGE_SENTENCE_LENGTH": "Wörter pro Satz (Ø)",
"TEXT_COMPLEXITY_AVERAGE_WORD_LENGTH": "Wortlänge (Ø)", "TEXT_COMPLEXITY_AVERAGE_WORD_LENGTH": "Wortlänge (Ø)",
"TEXT_COMPLEXITY_CLAUSE_COUNT": "Anzahl der Hauptsätze", "TEXT_COMPLEXITY_CLAUSE_COUNT": "Anzahl der Hauptsätze",
......
...@@ -71,6 +71,7 @@ ...@@ -71,6 +71,7 @@
"EXERCISE_FEEDBACK_PARTIALLY_CORRECT_DEFAULT": "That is partially correct.", "EXERCISE_FEEDBACK_PARTIALLY_CORRECT_DEFAULT": "That is partially correct.",
"EXERCISE_GENERATE": "Create exercise", "EXERCISE_GENERATE": "Create exercise",
"EXERCISE_LIST": "Exercise Repository", "EXERCISE_LIST": "Exercise Repository",
"EXERCISE_LIST_LEGEND": "Legend",
"EXERCISE_NO_OOV": "Exclude unknown words", "EXERCISE_NO_OOV": "Exclude unknown words",
"EXERCISE_PARAMETERS": "Exercise parameters", "EXERCISE_PARAMETERS": "Exercise parameters",
"EXERCISE_SET_PARAMETERS": "Set parameters", "EXERCISE_SET_PARAMETERS": "Set parameters",
...@@ -143,6 +144,8 @@ ...@@ -143,6 +144,8 @@
"SORTING_CATEGORY_AUTHOR_DESCENDING": "Author (descending)", "SORTING_CATEGORY_AUTHOR_DESCENDING": "Author (descending)",
"SORTING_CATEGORY_DATE_ASCENDING": "Date (ascending)", "SORTING_CATEGORY_DATE_ASCENDING": "Date (ascending)",
"SORTING_CATEGORY_DATE_DESCENDING": "Date (descending)", "SORTING_CATEGORY_DATE_DESCENDING": "Date (descending)",
"SORTING_CATEGORY_TEXT_COMPLEXITY_ASCENDING": "Complexity (ascending)",
"SORTING_CATEGORY_TEXT_COMPLEXITY_DESCENDING": "Complexity (descending)",
"SORTING_CATEGORY_TYPE_ASCENDING": "Type (ascending)", "SORTING_CATEGORY_TYPE_ASCENDING": "Type (ascending)",
"SORTING_CATEGORY_TYPE_DESCENDING": "Type (descending)", "SORTING_CATEGORY_TYPE_DESCENDING": "Type (descending)",
"SORTING_CATEGORY_VOCABULARY_ASCENDING": "Vocabulary (ascending)", "SORTING_CATEGORY_VOCABULARY_ASCENDING": "Vocabulary (ascending)",
...@@ -160,6 +163,7 @@ ...@@ -160,6 +163,7 @@
"TEST_REPEAT": "Repeat test", "TEST_REPEAT": "Repeat test",
"TEXT_COMPLEXITY": "Text complexity", "TEXT_COMPLEXITY": "Text complexity",
"TEXT_COMPLEXITY_ABLATIVI_ABSOLUTI_COUNT": "Number of Ablativi Absoluti", "TEXT_COMPLEXITY_ABLATIVI_ABSOLUTI_COUNT": "Number of Ablativi Absoluti",
"TEXT_COMPLEXITY_ALL": "Overall complexity",
"TEXT_COMPLEXITY_AVERAGE_SENTENCE_LENGTH": "Words per sentence (Ø)", "TEXT_COMPLEXITY_AVERAGE_SENTENCE_LENGTH": "Words per sentence (Ø)",
"TEXT_COMPLEXITY_AVERAGE_WORD_LENGTH": "Word length (Ø)", "TEXT_COMPLEXITY_AVERAGE_WORD_LENGTH": "Word length (Ø)",
"TEXT_COMPLEXITY_CLAUSE_COUNT": "Main clause count", "TEXT_COMPLEXITY_CLAUSE_COUNT": "Main clause count",
......
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