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/)
2. Clone the repo:
`git clone https://scm.cms.hu-berlin.de/callidus/mc_frontend.git`
......@@ -7,25 +8,35 @@
4. Run `docker-compose build`.
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!
## 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.
----------------------------------------------------------------
##### 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`.
# Development
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.
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`.
----------------------------------------------------------------
## Configuration
### Backend URL
# Configuration
## 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.
### 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.
### Other
## Other
For all other kinds of configuration, use `src/assets/config.json`.
{
"name": "mc_frontend",
"version": "1.3.0",
"version": "1.3.2",
"author": "Ionic Framework",
"homepage": "https://ionicframework.com/",
"scripts": {
......
......@@ -86,22 +86,35 @@
disabled="{{!hasChanges}}">{{ 'APPLY' | translate }}</ion-button>
</ion-col>
</ion-row>
<ion-row><ion-col></ion-col></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 *ngIf="exercises; else loading">
<ion-col>
<ion-list>
<ion-item *ngFor="let exercise of exercises">
<ion-list (click)="showExercise(exercise)">
<ion-item lines="none" class="item-nested-top">
{{exercise.work_author}}: {{exercise.work_title}}
</ion-item>
<ion-item lines="none" class="item-nested-bottom">
{{exercise.exercise_type_translation}} ({{ getDateString(exercise.last_access_time) }})
{{getMatchingDegree(exercise)}}
</ion-item>
</ion-list>
<ion-grid (click)="showExercise(exercise)" class="exercises">
<ion-row class="item-nested-top">
<ion-col>
{{exercise.work_author}}: {{exercise.work_title}}
</ion-col>
</ion-row>
<ion-row class="item-nested-bottom">
<ion-col size="8">
<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-list>
</ion-col>
......
......@@ -2,6 +2,10 @@ ion-list {
cursor: pointer;
}
.exercises {
padding: 0;
}
.item-nested-top {
max-height: 2.6em;
}
......@@ -9,3 +13,11 @@ ion-list {
.item-nested-bottom {
max-height: 2.1em;
}
.span-md {
color: #0e407a;
}
.span-tc {
color: #1a73d9;
}
......@@ -3,7 +3,7 @@ import {Component, OnInit} from '@angular/core';
import {HelperService} from 'src/app/helper.service';
import {NavController, ToastController} from '@ionic/angular';
import {ExerciseMC} from 'src/app/models/exerciseMC';
import {HttpClient, HttpErrorResponse, HttpParams} from '@angular/common/http';
import {HttpClient, HttpParams} from '@angular/common/http';
import {
ExerciseType,
ExerciseTypeTranslation,
......@@ -31,10 +31,25 @@ export class ExerciseListPage implements OnInit {
public ExerciseTypeTranslation = ExerciseTypeTranslation;
public hasChanges = false;
public HelperService = HelperService;
public Math = Math;
public metadata: { [eid: string]: string } = {};
public ObjectKeys = Object.keys;
public showVocabularyCorpus = false;
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 VocabularyCorpus = VocabularyCorpus;
public VocabularyCorpusTranslation = VocabularyCorpusTranslation;
......@@ -88,10 +103,7 @@ export class ExerciseListPage implements OnInit {
}
getMatchingDegree(exercise: ExerciseMC): string {
if (exercise.matching_degree) {
return `[${Math.round(exercise.matching_degree)}%]`;
}
return '';
return exercise.matching_degree ? Math.round(exercise.matching_degree).toString() : '';
}
ngOnInit(): void {
......@@ -118,53 +130,15 @@ export class ExerciseListPage implements OnInit {
this.sortingCategoriesVocCheck.has(this.currentSortingCategory) && !this.exercises.some(x => !!x.matching_degree)) {
return;
}
switch (this.currentSortingCategory) {
case SortingCategory.authorAsc:
this.exercises.sort((a: ExerciseMC, b: ExerciseMC) => {
return a.work_author === b.work_author ? 0 : (a.work_author < b.work_author ? -1 : 1);
});
break;
case SortingCategory.authorDesc:
this.exercises.sort((a: ExerciseMC, b: ExerciseMC) => {
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;
const property: string = this.sortingCategoriesMap[this.currentSortingCategory.toString()];
if (this.sortingCategoriesAsc.has(this.currentSortingCategory)) {
this.exercises.sort((a: ExerciseMC, b: ExerciseMC) => {
return a[property] === b[property] ? 0 : (a[property] < b[property] ? -1 : 1);
});
} else {
this.exercises.sort((a: ExerciseMC, b: ExerciseMC) => {
return a[property] === b[property] ? 0 : (a[property] < b[property] ? 1 : -1);
});
}
}
}
......@@ -177,6 +177,8 @@ export enum SortingCategory {
authorDesc = 'SORTING_CATEGORY_AUTHOR_DESCENDING' as any,
dateAsc = 'SORTING_CATEGORY_DATE_ASCENDING' 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,
typeDesc = 'SORTING_CATEGORY_TYPE_DESCENDING' as any,
vocAsc = 'SORTING_CATEGORY_VOCABULARY_ASCENDING' as any,
......
......@@ -15,6 +15,7 @@ export class ExerciseMC {
public partially_correct_feedback: string;
public search_values: string[];
public solutions: Solution[];
public text_complexity: number;
public uri: string;
public work_author: string;
public work_title: string;
......
......@@ -24,6 +24,7 @@ export class ShowTextPage {
public isDownloading = false;
public showTextComplexity = false;
public textComplexityMap = {
all: 'TEXT_COMPLEXITY_ALL',
n_w: 'TEXT_COMPLEXITY_WORD_COUNT',
n_sent: 'TEXT_COMPLEXITY_SENTENCE_COUNT',
avg_w_per_sent: 'TEXT_COMPLEXITY_AVERAGE_SENTENCE_LENGTH',
......
......@@ -71,6 +71,7 @@
"EXERCISE_FEEDBACK_PARTIALLY_CORRECT_DEFAULT": "Das ist teilweise korrekt.",
"EXERCISE_GENERATE": "Übung erstellen",
"EXERCISE_LIST": "Übungsdatenbank",
"EXERCISE_LIST_LEGEND": "Legende",
"EXERCISE_NO_OOV": "Unbekannte Vokabeln ausschließen",
"EXERCISE_PARAMETERS": "Übungsparameter",
"EXERCISE_SET_PARAMETERS": "Parameter festlegen",
......@@ -143,6 +144,8 @@
"SORTING_CATEGORY_AUTHOR_DESCENDING": "Autor (absteigend)",
"SORTING_CATEGORY_DATE_ASCENDING": "Datum (aufsteigend)",
"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_DESCENDING": "Typ (absteigend)",
"SORTING_CATEGORY_VOCABULARY_ASCENDING": "Vokabular (aufsteigend)",
......@@ -160,6 +163,7 @@
"TEST_REPEAT": "Einheit wiederholen",
"TEXT_COMPLEXITY": "Textkomplexität",
"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_WORD_LENGTH": "Wortlänge (Ø)",
"TEXT_COMPLEXITY_CLAUSE_COUNT": "Anzahl der Hauptsätze",
......
......@@ -71,6 +71,7 @@
"EXERCISE_FEEDBACK_PARTIALLY_CORRECT_DEFAULT": "That is partially correct.",
"EXERCISE_GENERATE": "Create exercise",
"EXERCISE_LIST": "Exercise Repository",
"EXERCISE_LIST_LEGEND": "Legend",
"EXERCISE_NO_OOV": "Exclude unknown words",
"EXERCISE_PARAMETERS": "Exercise parameters",
"EXERCISE_SET_PARAMETERS": "Set parameters",
......@@ -143,6 +144,8 @@
"SORTING_CATEGORY_AUTHOR_DESCENDING": "Author (descending)",
"SORTING_CATEGORY_DATE_ASCENDING": "Date (ascending)",
"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_DESCENDING": "Type (descending)",
"SORTING_CATEGORY_VOCABULARY_ASCENDING": "Vocabulary (ascending)",
......@@ -160,6 +163,7 @@
"TEST_REPEAT": "Repeat test",
"TEXT_COMPLEXITY": "Text complexity",
"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_WORD_LENGTH": "Word length (Ø)",
"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