Commit b1263f47 authored by Konstantin Schulz's avatar Konstantin Schulz

added tutorial page; made wrong lookups for frequency analyses more robust

parent ed93d736
Pipeline #15787 passed with stages
in 2 minutes and 56 seconds
python ./mc_frontend/update_version.py
python3 ./mc_frontend/update_version.py
docker-compose build
docker-compose down
docker-compose up -d
......@@ -56,8 +56,8 @@ class CorpusService:
@staticmethod
def check_corpus_list_age(app: Flask) -> None:
""" Checks whether the corpus list needs to be updated. If yes, it performs the update. """
app.logger.info("Corpus update started.")
""" Checks whether the corpus list needs to be updated. If yes, the update is being performed. """
app.logger.info("Checking corpus age...")
ui_cts: UpdateInfo = DatabaseService.query(
UpdateInfo, filter_by=dict(resource_type=ResourceType.cts_data.name), first=True)
DatabaseService.commit()
......@@ -67,6 +67,7 @@ class CorpusService:
else:
ui_datetime: datetime = datetime.fromtimestamp(ui_cts.last_modified_time)
if (datetime.utcnow() - ui_datetime).total_seconds() > Config.INTERVAL_CORPUS_UPDATE:
app.logger.info("Corpus update started.")
CorpusService.update_corpora()
ui_cts.last_modified_time = datetime.utcnow().timestamp()
DatabaseService.commit()
......
......@@ -54,9 +54,9 @@ class CustomCorpusService:
viva_stop_words: Set[str] = {"Während", "Bekanntschaft"}
@staticmethod
def extract_custom_corpus_text(relevant_text_parts: List[TextPart], start_parts: List[str], end_parts: List[str],
base_urn: str, current_idx: int = 0, consider_start: List[bool] = None) \
-> List[ReferenceableText]:
def extract_custom_corpus_text(
relevant_text_parts: List[TextPart], start_parts: List[str], end_parts: List[str], base_urn: str,
current_idx: int = 0, consider_start: List[bool] = None) -> List[ReferenceableText]:
""" Extracts text from the relevant parts of a (custom) corpus. """
text_list: List[ReferenceableText] = []
nxt: callable = CustomCorpusService.extract_custom_corpus_text_next_level
......@@ -238,10 +238,15 @@ class CustomCorpusService:
cc = CustomCorpusService.init_custom_corpus(cc)
# just regular CTS citation, need to get the correct labels
target_citation_range = target_citation_range[0].split("-")
start_citation_label: str = AnnotationService.get_citation_label(
cc.text_parts, [int(x) for x in target_citation_range[0].split(".")])
end_citation_label: str = AnnotationService.get_citation_label(
cc.text_parts, [int(x) for x in target_citation_range[1].split(".")])
start_citation_label: str
end_citation_label: str
try:
start_citation_label = AnnotationService.get_citation_label(
cc.text_parts, [int(x) for x in target_citation_range[0].split(".")])
end_citation_label = AnnotationService.get_citation_label(
cc.text_parts, [int(x) for x in target_citation_range[1].split(".")])
except ValueError:
return []
start_index: int = -1
end_index: int = -1
for i in range(len(annotations)):
......
......@@ -105,8 +105,8 @@ class Config(object):
HOST_IP_MCSERVER = DOCKER_SERVICE_NAME_MCSERVER if IS_DOCKER else HOST_IP_FALLBACK
HOST_PORT = 5000
INTERNET_PROTOCOL = "http://"
INTERVAL_CORPUS_AGE_CHECK = 60 * 60
INTERVAL_CORPUS_UPDATE = 60 * 60 * 24
INTERVAL_CORPUS_AGE_CHECK = 60 * 60 # once per hour
INTERVAL_CORPUS_UPDATE = 60 * 60 * 24 # once per day
INTERVAL_EXERCISE_DELETE = 60 * 60 * 24 * 30 * 12
INTERVAL_FILE_DELETE = 60 * 60 * 24
INTERVAL_STATIC_EXERCISES = 60 * 60 * 24
......
......@@ -1043,21 +1043,24 @@ class CommonTestCase(unittest.TestCase):
conll: List[TokenList] = CustomCorpusService.get_treebank_annotations(Mocks.urn_custom)
self.assertIs(conll, Mocks.annotations)
with patch.object(mcserver.app.services.customCorpusService.json, "loads", side_effect=ValueError):
conll = CustomCorpusService.get_treebank_annotations(Mocks.urn_custom)
CustomCorpusService.get_treebank_annotations(Mocks.urn_custom)
os.remove(cache_path)
self.assertEqual(parse_mock.call_count, 2)
def test_get_treebank_sub_annotations(self):
""" Retrieves annotations for nested parts of a treebank. """
annotations: List[TokenList] = Mocks.annotations + [TokenList([], metadata=OrderedDict([("sent_id", "2")])),
TokenList([], metadata=OrderedDict([("sent_id", "3")]))]
annotations: List[TokenList] = Mocks.annotations + \
[TokenList([], metadata=OrderedDict([("sent_id", "2")])),
TokenList([], metadata=OrderedDict([("sent_id", "3")]))]
conll: List[TokenList] = CustomCorpusService.get_treebank_sub_annotations(
Mocks.urn + "@1-3", annotations, CustomCorpusService.custom_corpora[4])
self.assertEqual(len(conll), 3)
cc: CustomCorpus = CustomCorpusService.custom_corpora[-1]
urn: str = cc.corpus.source_urn + ":1.1-1.2"
conll: List[TokenList] = CustomCorpusService.get_treebank_sub_annotations(urn, [], cc)
CustomCorpusService.get_treebank_sub_annotations(urn, [], cc)
self.assertEqual(len(cc.text_parts), 2)
conll = CustomCorpusService.get_treebank_sub_annotations(cc.corpus.source_urn + "I.1-I.2", [], cc)
self.assertEqual(conll, [])
def test_get_udpipe(self):
"""Annotates a single text with UdPipe. The beginning of the CONLL has to be left out because it contains the
......
......@@ -34,6 +34,10 @@ export const routes: Routes = [
path: 'sequences',
loadChildren: () => import('./sequences/sequences.module').then( m => m.SequencesPageModule)
},
{
path: 'tutorial',
loadChildren: () => import('./tutorial/tutorial.module').then( m => m.TutorialPageModule)
},
......
......@@ -54,6 +54,13 @@
</a>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<a (click)="helperService.goToPage(navCtrl, configMC.pageUrlTutorial).then(closeMenu.bind(this))">
{{ 'TUTORIAL' | translate }}
</a>
</ion-col>
</ion-row>
<ion-row>
<ion-grid style="text-align: left; padding: 0">
<ion-title>
......@@ -93,7 +100,7 @@
{{ 'SOURCES' | translate }}
</a>
</ion-col>
</ion-row>-->
</ion-row>-->
</ion-grid>
</ion-row>
</ion-grid>
......
......@@ -26,6 +26,7 @@ import configMC from '../configMC';
import {SemanticsPageModule} from './semantics/semantics.module';
import {EmbedPageModule} from './embed/embed.module';
import {SequencesPageModule} from './sequences/sequences.module';
import {TutorialPageModule} from './tutorial/tutorial.module';
describe('AppComponent', () => {
let statusBarSpy, splashScreenSpy, platformReadySpy, fixture: ComponentFixture<AppComponent>,
......@@ -107,8 +108,8 @@ describe('AppComponent', () => {
it('should test routing', (done) => {
const urls: string[] = [configMC.pageUrlEmbed.slice(1), configMC.pageUrlSemantics.slice(1),
configMC.pageUrlSequences.slice(1)];
const modules: any[] = [EmbedPageModule, SemanticsPageModule, SequencesPageModule];
configMC.pageUrlSequences.slice(1), configMC.pageUrlTutorial.slice(1)];
const modules: any[] = [EmbedPageModule, SemanticsPageModule, SequencesPageModule, TutorialPageModule];
let doneCount = 0;
new Promise(resolve => {
urls.forEach((url, index) => {
......
......@@ -196,6 +196,9 @@ describe('CorpusService', () => {
corpusService.getSortedQueryValues(query, 0);
expect(query.availableValues.length).toBe(3);
corpusService.exercise.type = ExerciseType.matching;
corpusService.annisResponse = {};
corpusService.getSortedQueryValues(query, 1);
expect(query.availableValues.length).toBe(0);
corpusService.annisResponse = {
frequency_analysis: [{
values: [PartOfSpeechValue.adjective.toString(), 'a'],
......
......@@ -240,6 +240,10 @@ export class CorpusService {
getSortedQueryValues(query: QueryMC, queryIndex: number): void {
const pmc: PhenomenonMapContent = this.phenomenonMap[query.phenomenon];
if (this.exercise.type === ExerciseType.matching) {
if (!this.annisResponse.frequency_analysis) {
query.availableValues = [];
return;
}
if (queryIndex) {
const relevantFIs: FrequencyItem[] = this.annisResponse.frequency_analysis.filter(
x => x.values[0] === this.exercise.queryItems[0].selectedValues[0] &&
......@@ -550,6 +554,7 @@ export class CorpusService {
newValue = document.querySelector<HTMLInputElement>(`#${targetInputId}`).value;
}
const trIdx: number = inputId - (isStart ? 1 : 4);
// set the new value for the current text range indirectly, depending on the relevant index
const relevantTextRangePart: string[] = isStart ? this.currentTextRangeCache.start : this.currentTextRangeCache.end;
relevantTextRangePart[trIdx] = newValue;
}
......
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {TutorialPage} from './tutorial.page';
const routes: Routes = [
{
path: '',
component: TutorialPage
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class TutorialPageRoutingModule {
}
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormsModule} from '@angular/forms';
import {IonicModule} from '@ionic/angular';
import {TutorialPageRoutingModule} from './tutorial-routing.module';
import {TutorialPage} from './tutorial.page';
import {TranslateModule} from '@ngx-translate/core';
@NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
TranslateModule.forChild(),
TutorialPageRoutingModule
],
declarations: [TutorialPage]
})
export class TutorialPageModule {
}
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<div class="home-logo">
<a (click)="helperService.goToHomePage(navCtrl)">
<img src="assets/imgs/logo.png" width="32px" height="32px" alt="CALLIDUS Logo">
</a>
</div>
</ion-buttons>
<ion-spinner *ngIf="helperService.openRequests.length"></ion-spinner>
<ion-title>{{ 'TUTORIAL' | translate }}</ion-title>
<ion-buttons slot="end">
<ion-menu-button autoHide="false">
<ion-icon name="menu"></ion-icon>
</ion-menu-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-grid>
<ion-row>
<ion-col></ion-col>
</ion-row>
</ion-grid>
</ion-content>
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {IonicModule} from '@ionic/angular';
import {TutorialPage} from './tutorial.page';
import {HttpClientModule} from '@angular/common/http';
import {IonicStorageModule} from '@ionic/storage';
import {RouterModule} from '@angular/router';
import {TranslateTestingModule} from '../translate-testing/translate-testing.module';
describe('TutorialPage', () => {
let component: TutorialPage;
let fixture: ComponentFixture<TutorialPage>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [TutorialPage],
imports: [
HttpClientModule,
IonicModule.forRoot(),
IonicStorageModule.forRoot(),
RouterModule.forRoot([]),
TranslateTestingModule,
]
}).compileComponents();
fixture = TestBed.createComponent(TutorialPage);
component = fixture.componentInstance;
fixture.detectChanges();
}));
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, OnInit} from '@angular/core';
import {HelperService} from '../helper.service';
import {NavController} from '@ionic/angular';
import {HttpClient} from '@angular/common/http';
@Component({
selector: 'app-tutorial',
templateUrl: './tutorial.page.html',
styleUrls: ['./tutorial.page.scss'],
})
export class TutorialPage implements OnInit {
constructor(public helperService: HelperService,
public navCtrl: NavController,
public http: HttpClient) {
}
ngOnInit() {
}
}
......@@ -358,6 +358,7 @@
"TEXT_TOO_LONG": "Text zu lang, max. Wortzahl: ",
"TEXT_WORK": "Textarbeit",
"TOO_MANY_SEARCH_RESULTS": "Zu viele Treffer. Bitte Auswahl einschränken...",
"TUTORIAL": "Tutorial: Machina Callida",
"TYPE": "Typ",
"UNIT_APPLICATION_TITLE": "Wortschatzarbeit am Text",
"UNIT_DATA_SECURITY": "Datenschutz: Es werden keine persönlichen Daten erhoben. Die Ergebnisse können auch nicht bis zu einzelnen Teilnehmern zurückverfolgt werden.",
......
......@@ -358,6 +358,7 @@
"TEXT_TOO_LONG": "Text too long, max. word count: ",
"TEXT_WORK": "Text work",
"TOO_MANY_SEARCH_RESULTS": "Too many hits. Please refine your query...",
"TUTORIAL": "Tutorial: Machina Callida",
"TYPE": "Type",
"UNIT_APPLICATION_TITLE": "Vocabulary work on text",
"UNIT_DATA_SECURITY": "Privacy protection: No personal data will be collected. The results can also not be traced up to individual participants.",
......
......@@ -47,6 +47,7 @@ export default {
pageUrlSources: '/sources',
pageUrlTest: '/test',
pageUrlTextRange: '/text-range',
pageUrlTutorial: '/tutorial',
pageUrlVocabularyCheck: '/vocabulary-check',
perseidsCTSapiBaseUrl: 'https://cts.perseids.org/api/cts?request=',
perseidsCTSapiGetCapabilities: 'GetCapabilities',
......
export const version = '2.3.9';
export const version = '2.4.1';
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