From 0863b577b1a3d1427b9b2a4cc5677a051787fc6a Mon Sep 17 00:00:00 2001 From: Konstantin Schulz <schulzkx@hu-berlin.de> Date: Tue, 26 May 2020 13:22:16 +0200 Subject: [PATCH] frontend now uses typescript interfaces that are generated automatically from the OpenAPI specification --- README.md | 7 + mc_backend/mcserver/app/__init__.py | 5 +- mc_backend/mcserver/app/api/__init__.py | 4 +- .../mcserver/app/api/staticExercisesAPI.py | 36 +- mc_backend/mcserver/app/models.py | 4 +- .../mcserver/app/services/corpusService.py | 2 +- mc_backend/mcserver/config.py | 8 +- mc_backend/mcserver/mcserver_api.yaml | 129 +++--- mc_backend/mcserver/models_auto.py | 12 +- mc_backend/mocks.py | 2 +- .../controllers/default_controller.py | 19 +- .../openapi/openapi_server/models/__init__.py | 6 +- .../openapi_server/models/annis_response.py | 12 +- .../openapi/openapi_server/models/exercise.py | 4 + .../openapi_server/models/exercise_base.py | 4 + .../openapi_server/models/exercise_form.py | 356 +++++++++++++++ .../models/exercise_form_all_of.py | 124 +++++ .../models/exercise_form_comp.py | 356 +++++++++++++++ .../openapi_server/models/frequency_item.py | 122 +++++ .../openapi_server/models/graph_data.py | 12 +- .../openapi/openapi_server/models/link.py | 8 - .../openapi/openapi_server/models/node_mc.py | 346 ++++++++++++++ .../openapi_server/models/solution_element.py | 2 - .../openapi_server/openapi/openapi.yaml | 136 +++--- mc_backend/openapi_generator.py | 5 +- mc_backend/tests.py | 29 +- mc_frontend/openapi/.gitignore | 4 + mc_frontend/openapi/.openapi-generator-ignore | 23 + .../openapi/.openapi-generator/VERSION | 1 + mc_frontend/openapi/README.md | 205 +++++++++ mc_frontend/openapi/api.module.ts | 32 ++ mc_frontend/openapi/api/api.ts | 3 + mc_frontend/openapi/api/default.service.ts | 430 ++++++++++++++++++ mc_frontend/openapi/configuration.ts | 84 ++++ mc_frontend/openapi/encoder.ts | 21 + mc_frontend/openapi/git_push.sh | 58 +++ mc_frontend/openapi/index.ts | 5 + mc_frontend/openapi/model/annisResponse.ts | 45 ++ .../model/annisResponseFrequencyAnalysis.ts | 28 ++ mc_frontend/openapi/model/corpus.ts | 47 ++ mc_frontend/openapi/model/exercise.ts | 86 ++++ mc_frontend/openapi/model/exerciseAllOf.ts | 55 +++ mc_frontend/openapi/model/exerciseBase.ts | 51 +++ mc_frontend/openapi/model/exerciseForm.ts | 62 +++ .../openapi/model/exerciseFormAllOf.ts | 31 ++ mc_frontend/openapi/model/exerciseFormComp.ts | 62 +++ mc_frontend/openapi/model/frequencyItem.ts | 28 ++ mc_frontend/openapi/model/graphData.ts | 41 ++ mc_frontend/openapi/model/learningResult.ts | 107 +++++ mc_frontend/openapi/model/link.ts | 36 ++ mc_frontend/openapi/model/models.ts | 16 + mc_frontend/openapi/model/node.ts | 60 +++ mc_frontend/openapi/model/nodeMC.ts | 60 +++ mc_frontend/openapi/model/solution.ts | 22 + mc_frontend/openapi/model/solutionElement.ts | 35 ++ mc_frontend/openapi/model/textComplexity.ts | 79 ++++ mc_frontend/openapi/model/updateInfo.ts | 40 ++ mc_frontend/openapi/variables.ts | 9 + mc_frontend/src/app/corpus.service.spec.ts | 49 +- mc_frontend/src/app/corpus.service.ts | 20 +- .../exercise-list/exercise-list.page.spec.ts | 3 +- .../app/exercise-list/exercise-list.page.ts | 2 +- .../exercise-parameters.page.spec.ts | 19 +- .../exercise-parameters.page.ts | 3 +- .../src/app/exercise/exercise.page.spec.ts | 10 +- mc_frontend/src/app/exercise/exercise.page.ts | 2 +- mc_frontend/src/app/helper.service.spec.ts | 2 +- mc_frontend/src/app/helper.service.ts | 21 +- mc_frontend/src/app/models/annisResponse.ts | 23 - mc_frontend/src/app/models/exerciseMC.ts | 3 +- mc_frontend/src/app/models/frequencyItem.ts | 10 - mc_frontend/src/app/models/linkMC.ts | 11 - mc_frontend/src/app/models/mockMC.ts | 26 +- mc_frontend/src/app/models/nodeMC.ts | 17 - mc_frontend/src/app/models/solution.ts | 10 - mc_frontend/src/app/models/solutionElement.ts | 10 - mc_frontend/src/app/models/textData.ts | 2 +- mc_frontend/src/app/preview/preview.page.html | 4 +- .../src/app/preview/preview.page.spec.ts | 73 ++- mc_frontend/src/app/preview/preview.page.ts | 11 +- .../src/app/ranking/ranking.page.spec.ts | 10 +- mc_frontend/src/app/ranking/ranking.page.ts | 6 +- .../src/app/semantics/semantics.page.spec.ts | 5 +- .../src/app/show-text/show-text.page.html | 5 +- .../src/app/show-text/show-text.page.spec.ts | 8 +- .../src/app/show-text/show-text.page.ts | 7 +- .../src/app/vocabulary.service.spec.ts | 5 +- mc_frontend/src/app/vocabulary.service.ts | 2 +- 88 files changed, 3557 insertions(+), 438 deletions(-) create mode 100644 mc_backend/openapi/openapi_server/models/exercise_form.py create mode 100644 mc_backend/openapi/openapi_server/models/exercise_form_all_of.py create mode 100644 mc_backend/openapi/openapi_server/models/exercise_form_comp.py create mode 100644 mc_backend/openapi/openapi_server/models/frequency_item.py create mode 100644 mc_backend/openapi/openapi_server/models/node_mc.py create mode 100644 mc_frontend/openapi/.gitignore create mode 100644 mc_frontend/openapi/.openapi-generator-ignore create mode 100644 mc_frontend/openapi/.openapi-generator/VERSION create mode 100644 mc_frontend/openapi/README.md create mode 100644 mc_frontend/openapi/api.module.ts create mode 100644 mc_frontend/openapi/api/api.ts create mode 100644 mc_frontend/openapi/api/default.service.ts create mode 100644 mc_frontend/openapi/configuration.ts create mode 100644 mc_frontend/openapi/encoder.ts create mode 100644 mc_frontend/openapi/git_push.sh create mode 100644 mc_frontend/openapi/index.ts create mode 100644 mc_frontend/openapi/model/annisResponse.ts create mode 100644 mc_frontend/openapi/model/annisResponseFrequencyAnalysis.ts create mode 100644 mc_frontend/openapi/model/corpus.ts create mode 100644 mc_frontend/openapi/model/exercise.ts create mode 100644 mc_frontend/openapi/model/exerciseAllOf.ts create mode 100644 mc_frontend/openapi/model/exerciseBase.ts create mode 100644 mc_frontend/openapi/model/exerciseForm.ts create mode 100644 mc_frontend/openapi/model/exerciseFormAllOf.ts create mode 100644 mc_frontend/openapi/model/exerciseFormComp.ts create mode 100644 mc_frontend/openapi/model/frequencyItem.ts create mode 100644 mc_frontend/openapi/model/graphData.ts create mode 100644 mc_frontend/openapi/model/learningResult.ts create mode 100644 mc_frontend/openapi/model/link.ts create mode 100644 mc_frontend/openapi/model/models.ts create mode 100644 mc_frontend/openapi/model/node.ts create mode 100644 mc_frontend/openapi/model/nodeMC.ts create mode 100644 mc_frontend/openapi/model/solution.ts create mode 100644 mc_frontend/openapi/model/solutionElement.ts create mode 100644 mc_frontend/openapi/model/textComplexity.ts create mode 100644 mc_frontend/openapi/model/updateInfo.ts create mode 100644 mc_frontend/openapi/variables.ts delete mode 100644 mc_frontend/src/app/models/annisResponse.ts delete mode 100644 mc_frontend/src/app/models/frequencyItem.ts delete mode 100644 mc_frontend/src/app/models/linkMC.ts delete mode 100644 mc_frontend/src/app/models/nodeMC.ts delete mode 100644 mc_frontend/src/app/models/solution.ts delete mode 100644 mc_frontend/src/app/models/solutionElement.ts diff --git a/README.md b/README.md index 0f3fcad..3f4a8d0 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,14 @@ Alternatively, you can use `ssh root@localhost -p 8022 -o "UserKnownHostsFile /d 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`. +## Models +To generate class structures for this project automatically: +1. Install OpenAPI Generator (using, e.g., `brew install openapi-generator`). +2. Run: `openapi-generator generate -i mc_backend/mcserver/mcserver_api.yaml -g typescript-angular -o mc_frontend/openapi/ && openapi-generator generate -i mc_backend/mcserver/mcserver_api.yaml -g python-flask -o mc_backend/openapi/ && python mc_backend/openapi_generator.py`. + ## Documentation +### API +To view the API documentation, visit https://korpling.org/mc-service/mc/api/v1.0/ui/ . ### Changelog To update the changelog, use: `git log --oneline --decorate > CHANGELOG` diff --git a/mc_backend/mcserver/app/__init__.py b/mc_backend/mcserver/app/__init__.py index 925acfb..667c836 100644 --- a/mc_backend/mcserver/app/__init__.py +++ b/mc_backend/mcserver/app/__init__.py @@ -110,6 +110,9 @@ def init_logging(app: Flask, log_file_path: str): app.logger.addHandler(file_handler) app.logger.setLevel(log_level) got_request_exception.connect(log_exception, app) + database_uri: str = app.config.get("SQLALCHEMY_DATABASE_URI", "") + database_uri = database_uri.split('@')[1] if '@' in database_uri else database_uri + app.logger.warning(f"Accessing database at: {database_uri}") def log_exception(sender_app: Flask, exception, **extra): @@ -120,7 +123,7 @@ def log_exception(sender_app: Flask, exception, **extra): exception -- the exception to be logged **extra -- any additional arguments """ - sender_app.logger.exception(f"ERROR for {flask.request.url}") + sender_app.logger.info(f"ERROR for {flask.request.url}") def start_updater(app: Flask) -> Thread: diff --git a/mc_backend/mcserver/app/api/__init__.py b/mc_backend/mcserver/app/api/__init__.py index d8b7a16..84c2c04 100644 --- a/mc_backend/mcserver/app/api/__init__.py +++ b/mc_backend/mcserver/app/api/__init__.py @@ -6,14 +6,13 @@ from mcserver import Config bp = Blueprint("api", __name__) api = Api(bp) -from . import corpusAPI, corpusListAPI, exerciseAPI +from . import corpusAPI, corpusListAPI, exerciseAPI, staticExercisesAPI from mcserver.app.api.exerciseListAPI import ExerciseListAPI from mcserver.app.api.fileAPI import FileAPI from mcserver.app.api.frequencyAPI import FrequencyAPI from mcserver.app.api.h5pAPI import H5pAPI from mcserver.app.api.kwicAPI import KwicAPI from mcserver.app.api.rawTextAPI import RawTextAPI -from mcserver.app.api.staticExercisesAPI import StaticExercisesAPI from mcserver.app.api.textcomplexityAPI import TextComplexityAPI from mcserver.app.api.validReffAPI import ValidReffAPI from mcserver.app.api.vectorNetworkAPI import VectorNetworkAPI @@ -25,7 +24,6 @@ api.add_resource(FrequencyAPI, Config.SERVER_URI_FREQUENCY, endpoint="frequency" api.add_resource(H5pAPI, Config.SERVER_URI_H5P, endpoint="h5p") api.add_resource(KwicAPI, Config.SERVER_URI_KWIC, endpoint="kwic") api.add_resource(RawTextAPI, Config.SERVER_URI_RAW_TEXT, endpoint="rawtext") -api.add_resource(StaticExercisesAPI, Config.SERVER_URI_STATIC_EXERCISES, endpoint="exercises") api.add_resource(TextComplexityAPI, Config.SERVER_URI_TEXT_COMPLEXITY, endpoint='textcomplexity') api.add_resource(ValidReffAPI, Config.SERVER_URI_VALID_REFF, endpoint="validReff") api.add_resource(VectorNetworkAPI, Config.SERVER_URI_VECTOR_NETWORK, endpoint="vectorNetwork") diff --git a/mc_backend/mcserver/app/api/staticExercisesAPI.py b/mc_backend/mcserver/app/api/staticExercisesAPI.py index 524b6ec..5b9aec8 100644 --- a/mc_backend/mcserver/app/api/staticExercisesAPI.py +++ b/mc_backend/mcserver/app/api/staticExercisesAPI.py @@ -7,12 +7,12 @@ from decimal import Decimal, ROUND_HALF_UP from io import BytesIO from tempfile import mkstemp from time import time -from typing import Dict, List, Set, Match, Tuple +from typing import Dict, List, Set, Match, Tuple, Union from zipfile import ZipFile +import connexion import requests -from flask_restful import Resource, abort -from flask_restful.reqparse import RequestParser +from connexion.lifecycle import ConnexionResponse from requests import Response from mcserver.app.models import StaticExercise @@ -20,22 +20,14 @@ from mcserver.app.services import NetworkService, AnnotationService from mcserver.config import Config -class StaticExercisesAPI(Resource): - """The StaticExercises API resource. It guides users to static language exercises in the frontend.""" - - def __init__(self): - """Initialize possible arguments for calls to the StaticExercises REST API.""" - self.reqparse: RequestParser = NetworkService.base_request_parser.copy() - super(StaticExercisesAPI, self).__init__() - - def get(self): - """ The GET method for the StaticExercises REST API. It provides a list of static exercises - and their respective URLs in the frontend. """ - # TODO: WRITE AND READ LAST UPDATE TIME FROM THE DATABASE - if datetime.fromtimestamp(time() - Config.INTERVAL_STATIC_EXERCISES) > NetworkService.exercises_last_update or \ - len(NetworkService.exercises) == 0: - update_exercises() - return NetworkService.make_json_response({k: v.__dict__ for (k, v) in NetworkService.exercises.items()}) +def get() -> Union[Response, ConnexionResponse]: + """ The GET method for the StaticExercises REST API. It provides a list of static exercises + and their respective URLs in the frontend. """ + # TODO: WRITE AND READ LAST UPDATE TIME FROM THE DATABASE + if datetime.fromtimestamp(time() - Config.INTERVAL_STATIC_EXERCISES) > NetworkService.exercises_last_update \ + or len(NetworkService.exercises) == 0: + return update_exercises() + return NetworkService.make_json_response({k: v.__dict__ for (k, v) in NetworkService.exercises.items()}) def get_relevant_strings(response: Response): @@ -121,12 +113,13 @@ def handle_voc_list(content: dict, url: str, relevant_strings_dict: Dict[str, Se relevant_strings_dict[url].add(match_parts[0]) -def update_exercises(): +def update_exercises() -> Union[Response, ConnexionResponse]: """ Gets all static exercises from the frontend code repository and looks for the lemmata in them.""" # TODO: check last update of the directory before pulling the whole zip archive response: Response = requests.get(Config.STATIC_EXERCISES_REPOSITORY_URL, stream=True) if not response.ok: - abort(503) + return connexion.problem( + 503, Config.ERROR_TITLE_SERVICE_UNAVAILABLE, Config.ERROR_MESSAGE_SERVICE_UNAVAILABLE) relevant_strings_dict: Dict[str, Set[str]] = get_relevant_strings(response) file_dict: Dict = {} lemma_set: Set[str] = set() @@ -150,3 +143,4 @@ def update_exercises(): word = word[:-1] NetworkService.exercises[url].solutions.append(list(search_results[search_results_dict[word]])) NetworkService.exercises_last_update = datetime.fromtimestamp(time()) + return NetworkService.make_json_response({k: v.__dict__ for (k, v) in NetworkService.exercises.items()}) diff --git a/mc_backend/mcserver/app/models.py b/mc_backend/mcserver/app/models.py index 2904e7a..b3cc0f9 100644 --- a/mc_backend/mcserver/app/models.py +++ b/mc_backend/mcserver/app/models.py @@ -4,13 +4,13 @@ from enum import Enum import typing from mcserver.config import Config from mcserver.models_auto import TExercise, Corpus, TCorpus, Exercise, TLearningResult, LearningResult -from openapi.openapi_server.models import SolutionElement, Solution, Link, Node, TextComplexity, AnnisResponse, \ +from openapi.openapi_server.models import SolutionElement, Solution, Link, NodeMC, TextComplexity, AnnisResponse, \ GraphData AnnisResponse = AnnisResponse GraphData = GraphData LinkMC = Link -NodeMC = Node +NodeMC = NodeMC SolutionElement = SolutionElement TextComplexity = TextComplexity diff --git a/mc_backend/mcserver/app/services/corpusService.py b/mc_backend/mcserver/app/services/corpusService.py index c54384c..3ff75d0 100644 --- a/mc_backend/mcserver/app/services/corpusService.py +++ b/mc_backend/mcserver/app/services/corpusService.py @@ -104,7 +104,7 @@ class CorpusService: # there is actually no text, only a URN, so we need to get it ourselves url: str = f"{Config.INTERNET_PROTOCOL}{Config.HOST_IP_CSM}:{Config.CORPUS_STORAGE_MANAGER_PORT}/" response: requests.Response = requests.get(url, params=dict(urn=cts_urn)) - return AnnisResponse(graph_data=GraphData.from_dict(json.loads(response.text))) + return AnnisResponse.from_dict(json.loads(response.text)) @staticmethod def get_frequency_analysis(urn: str, is_csm: bool) -> FrequencyAnalysis: diff --git a/mc_backend/mcserver/config.py b/mc_backend/mcserver/config.py index eabf6c9..0020bb2 100644 --- a/mc_backend/mcserver/config.py +++ b/mc_backend/mcserver/config.py @@ -74,10 +74,14 @@ class Config(object): DOCKER_SERVICE_NAME_MCSERVER = "mcserver" ERROR_MESSAGE_CORPUS_NOT_FOUND = "A corpus with the specified ID was not found!" ERROR_MESSAGE_EXERCISE_NOT_FOUND = "An exercise with the specified ID was not found!" - ERROR_MESSAGE_INTERNAL_SERVER_ERROR = "The server encountered an unexpected condition that prevented it from " \ - "fulfilling the request." + ERROR_MESSAGE_INTERNAL_SERVER_ERROR = \ + "The server encountered an unexpected condition that prevented it from fulfilling the request." + ERROR_MESSAGE_SERVICE_UNAVAILABLE = \ + "The server is currently unable to handle the request due to a temporary overload or scheduled " \ + "maintenance, which will likely be alleviated after some delay." ERROR_TITLE_INTERNAL_SERVER_ERROR = "Internal Server Error" ERROR_TITLE_NOT_FOUND = "Not found" + ERROR_TITLE_SERVICE_UNAVAILABLE = "Service Unavailable" FAVICON_FILE_NAME = "favicon.ico" FLASK_MIGRATE = "migrate" GRAPHANNIS_DEPENDENCY_LINK = "dep" diff --git a/mc_backend/mcserver/mcserver_api.yaml b/mc_backend/mcserver/mcserver_api.yaml index 1a33924..e9ab2c4 100644 --- a/mc_backend/mcserver/mcserver_api.yaml +++ b/mc_backend/mcserver/mcserver_api.yaml @@ -118,38 +118,26 @@ paths: schema: $ref: '#/components/schemas/AnnisResponse' requestBody: - $ref: '#/components/requestBodies/ExerciseForm' -components: - requestBodies: - ExerciseForm: - required: true - content: - application/x-www-form-urlencoded: - schema: - x-body-name: exercise_data - type: object - allOf: - - $ref: '#/components/schemas/ExerciseBase' - required: - - instructions - - search_values - - description: Additional exercise data. + required: true + content: + application/x-www-form-urlencoded: + schema: + x-body-name: exercise_data + type: object + $ref: '#/components/schemas/ExerciseForm' + /exercises: + get: + summary: Returns metadata for static exercises. + operationId: mcserver.app.api.staticExercisesAPI.get + responses: + 200: + description: Metadata for static exercises, including their respective URIs in the frontend. + content: + application/json: + schema: +# TODO: specify object properties type: object - properties: - type: - type: string - description: Type of exercise, concerning interaction and layout. - example: markWords - type_translation: - type: string - description: Localized expression of the exercise type. - example: Cloze - urn: - type: string - description: CTS URN for the text passage from which the exercise was created. - example: urn:cts:latinLit:phi0448.phi001.perseus-lat2:1.1.1 - required: - - type +components: schemas: AnnisResponse: description: A response with graph data from ANNIS, possibly with additional data for exercises. @@ -167,24 +155,7 @@ components: type: array description: List of items with frequency data for linguistic phenomena. items: - type: object - properties: - count: - type: integer - description: How often the given combination of values occurred. - example: 1 - phenomena: - type: array - description: Labels for the phenomena described in this frequency entry. - example: [] - items: - type: string - values: - type: array - description: Values for the phenomena described in this frequency entry. - example: [] - items: - type: string + $ref: "#/components/schemas/FrequencyItem" graph_data: $ref: "#/components/schemas/GraphData" solutions: @@ -348,6 +319,48 @@ components: description: Title of the base text for the exercise. example: Noctes Atticae default: "" + required: + - instructions + - search_values + ExerciseForm: + allOf: + - $ref: '#/components/schemas/ExerciseBase' + - description: Additional exercise data. + type: object + properties: + type: + type: string + description: Type of exercise, concerning interaction and layout. + example: markWords + type_translation: + type: string + description: Localized expression of the exercise type. + example: Cloze + urn: + type: string + description: CTS URN for the text passage from which the exercise was created. + example: urn:cts:latinLit:phi0448.phi001.perseus-lat2:1.1.1 + required: + - type + FrequencyItem: + type: object + properties: + count: + type: integer + description: How often the given combination of values occurred. + example: 1 + phenomena: + type: array + description: Labels for the phenomena described in this frequency entry. + example: [] + items: + type: string + values: + type: array + description: Values for the phenomena described in this frequency entry. + example: [] + items: + type: string GraphData: type: object description: Nodes, edges and metadata for a graph. @@ -373,7 +386,7 @@ components: type: array description: List of nodes for the graph. items: - $ref: '#/components/schemas/Node' + $ref: '#/components/schemas/NodeMC' required: - links - nodes @@ -520,12 +533,7 @@ components: type: string description: Dependency relation described by the edge. example: "det" - required: - - annis_component_name - - annis_component_type - - source - - target - Node: + NodeMC: type: object properties: annis_node_name: @@ -572,14 +580,6 @@ components: type: string description: Solution value for this node in an exercise. example: "" - required: - - annis_node_name - - annis_node_type - - annis_tok - - annis_type - - id - - udep_lemma - - udep_upostag Solution: type: object description: Correct solution for an exercise. @@ -609,7 +609,6 @@ components: description: Unique identifier for the token in a sentence. example: 9 required: - - content - sentence_id - token_id TextComplexity: diff --git a/mc_backend/mcserver/models_auto.py b/mc_backend/mcserver/models_auto.py index 9ef6420..5ba6612 100644 --- a/mc_backend/mcserver/models_auto.py +++ b/mc_backend/mcserver/models_auto.py @@ -149,6 +149,8 @@ Corpus: TCorpus = models.Corpus # type: ignore class _ExerciseDictBase(typing.TypedDict, total=True): """TypedDict for properties that are required.""" + instructions: str + search_values: str eid: str last_access_time: float @@ -159,9 +161,7 @@ class ExerciseDict(_ExerciseDictBase, total=False): correct_feedback: str general_feedback: str incorrect_feedback: str - instructions: str partially_correct_feedback: str - search_values: str work_author: str work_title: str conll: str @@ -233,14 +233,14 @@ class TExercise(typing.Protocol): def __init__( self, + instructions: str, + search_values: str, eid: str, last_access_time: float, correct_feedback: str = "", general_feedback: str = "", incorrect_feedback: str = "", - instructions: str = "", partially_correct_feedback: str = "", - search_values: str = "[]", work_author: str = "", work_title: str = "", conll: str = "", @@ -289,14 +289,14 @@ class TExercise(typing.Protocol): @classmethod def from_dict( cls, + instructions: str, + search_values: str, eid: str, last_access_time: float, correct_feedback: str = "", general_feedback: str = "", incorrect_feedback: str = "", - instructions: str = "", partially_correct_feedback: str = "", - search_values: str = "[]", work_author: str = "", work_title: str = "", conll: str = "", diff --git a/mc_backend/mocks.py b/mc_backend/mocks.py index e14d2aa..7b61cb0 100644 --- a/mc_backend/mocks.py +++ b/mc_backend/mocks.py @@ -98,7 +98,7 @@ class TestHelper: if not len(Mocks.app_dict): with patch.object(TextService, "init_stop_words_latin"): Mocks.app_dict[class_name] = TestHelper(app_factory(TestingConfig)) - Mocks.app_dict[class_name].app.logger.setLevel(logging.CRITICAL) + Mocks.app_dict[class_name].app.logger.setLevel(logging.WARNING) Mocks.app_dict[class_name].app.testing = True db.session.commit() diff --git a/mc_backend/openapi/openapi_server/controllers/default_controller.py b/mc_backend/openapi/openapi_server/controllers/default_controller.py index 3cdc45c..31bdf0f 100644 --- a/mc_backend/openapi/openapi_server/controllers/default_controller.py +++ b/mc_backend/openapi/openapi_server/controllers/default_controller.py @@ -3,8 +3,6 @@ import six from openapi.openapi_server.models.annis_response import AnnisResponse # noqa: E501 from openapi.openapi_server.models.corpus import Corpus # noqa: E501 -from openapi.openapi_server.models.exercise_base import ExerciseBase # noqa: E501 -from openapi.openapi_server.models.unknownbasetype import UNKNOWN_BASE_TYPE # noqa: E501 from openapi.openapi_server import util @@ -79,16 +77,23 @@ def mcserver_app_api_exercise_api_get(eid): # noqa: E501 return 'do some magic!' -def mcserver_app_api_exercise_api_post(unknown_base_type): # noqa: E501 +def mcserver_app_api_exercise_api_post(): # noqa: E501 """Creates a new exercise. # noqa: E501 - :param unknown_base_type: - :type unknown_base_type: dict | bytes :rtype: AnnisResponse """ - if connexion.request.is_json: - unknown_base_type = UNKNOWN_BASE_TYPE.from_dict(connexion.request.get_json()) # noqa: E501 + return 'do some magic!' + + +def mcserver_app_api_static_exercises_api_get(): # noqa: E501 + """Returns metadata for static exercises. + + # noqa: E501 + + + :rtype: object + """ return 'do some magic!' diff --git a/mc_backend/openapi/openapi_server/models/__init__.py b/mc_backend/openapi/openapi_server/models/__init__.py index 64b3bd9..8bcc862 100644 --- a/mc_backend/openapi/openapi_server/models/__init__.py +++ b/mc_backend/openapi/openapi_server/models/__init__.py @@ -4,15 +4,17 @@ from __future__ import absolute_import # import models into model package from openapi.openapi_server.models.annis_response import AnnisResponse -from openapi.openapi_server.models.annis_response_frequency_analysis import AnnisResponseFrequencyAnalysis from openapi.openapi_server.models.corpus import Corpus from openapi.openapi_server.models.exercise import Exercise from openapi.openapi_server.models.exercise_all_of import ExerciseAllOf from openapi.openapi_server.models.exercise_base import ExerciseBase +from openapi.openapi_server.models.exercise_form import ExerciseForm +from openapi.openapi_server.models.exercise_form_all_of import ExerciseFormAllOf +from openapi.openapi_server.models.frequency_item import FrequencyItem from openapi.openapi_server.models.graph_data import GraphData from openapi.openapi_server.models.learning_result import LearningResult from openapi.openapi_server.models.link import Link -from openapi.openapi_server.models.node import Node +from openapi.openapi_server.models.node_mc import NodeMC from openapi.openapi_server.models.solution import Solution from openapi.openapi_server.models.solution_element import SolutionElement from openapi.openapi_server.models.text_complexity import TextComplexity diff --git a/mc_backend/openapi/openapi_server/models/annis_response.py b/mc_backend/openapi/openapi_server/models/annis_response.py index e906140..e09e97a 100644 --- a/mc_backend/openapi/openapi_server/models/annis_response.py +++ b/mc_backend/openapi/openapi_server/models/annis_response.py @@ -6,13 +6,13 @@ from datetime import date, datetime # noqa: F401 from typing import List, Dict # noqa: F401 from openapi.openapi_server.models.base_model_ import Model -from openapi.openapi_server.models.annis_response_frequency_analysis import AnnisResponseFrequencyAnalysis +from openapi.openapi_server.models.frequency_item import FrequencyItem from openapi.openapi_server.models.graph_data import GraphData from openapi.openapi_server.models.solution import Solution from openapi.openapi_server.models.text_complexity import TextComplexity from openapi.openapi_server import util -from openapi.openapi_server.models.annis_response_frequency_analysis import AnnisResponseFrequencyAnalysis # noqa: E501 +from openapi.openapi_server.models.frequency_item import FrequencyItem # noqa: E501 from openapi.openapi_server.models.graph_data import GraphData # noqa: E501 from openapi.openapi_server.models.solution import Solution # noqa: E501 from openapi.openapi_server.models.text_complexity import TextComplexity # noqa: E501 @@ -31,7 +31,7 @@ class AnnisResponse(Model): :param exercise_type: The exercise_type of this AnnisResponse. # noqa: E501 :type exercise_type: str :param frequency_analysis: The frequency_analysis of this AnnisResponse. # noqa: E501 - :type frequency_analysis: List[AnnisResponseFrequencyAnalysis] + :type frequency_analysis: List[FrequencyItem] :param graph_data: The graph_data of this AnnisResponse. # noqa: E501 :type graph_data: GraphData :param solutions: The solutions of this AnnisResponse. # noqa: E501 @@ -44,7 +44,7 @@ class AnnisResponse(Model): self.openapi_types = { 'exercise_id': str, 'exercise_type': str, - 'frequency_analysis': List[AnnisResponseFrequencyAnalysis], + 'frequency_analysis': List[FrequencyItem], 'graph_data': GraphData, 'solutions': List[Solution], 'text_complexity': TextComplexity, @@ -133,7 +133,7 @@ class AnnisResponse(Model): List of items with frequency data for linguistic phenomena. # noqa: E501 :return: The frequency_analysis of this AnnisResponse. - :rtype: List[AnnisResponseFrequencyAnalysis] + :rtype: List[FrequencyItem] """ return self._frequency_analysis @@ -144,7 +144,7 @@ class AnnisResponse(Model): List of items with frequency data for linguistic phenomena. # noqa: E501 :param frequency_analysis: The frequency_analysis of this AnnisResponse. - :type frequency_analysis: List[AnnisResponseFrequencyAnalysis] + :type frequency_analysis: List[FrequencyItem] """ self._frequency_analysis = frequency_analysis diff --git a/mc_backend/openapi/openapi_server/models/exercise.py b/mc_backend/openapi/openapi_server/models/exercise.py index fdeb700..ba5bf76 100644 --- a/mc_backend/openapi/openapi_server/models/exercise.py +++ b/mc_backend/openapi/openapi_server/models/exercise.py @@ -215,6 +215,8 @@ class Exercise(Model): :param instructions: The instructions of this Exercise. :type instructions: str """ + if instructions is None: + raise ValueError("Invalid value for `instructions`, must not be `None`") # noqa: E501 self._instructions = instructions @@ -261,6 +263,8 @@ class Exercise(Model): :param search_values: The search_values of this Exercise. :type search_values: str """ + if search_values is None: + raise ValueError("Invalid value for `search_values`, must not be `None`") # noqa: E501 self._search_values = search_values diff --git a/mc_backend/openapi/openapi_server/models/exercise_base.py b/mc_backend/openapi/openapi_server/models/exercise_base.py index ece91ee..5e5e99f 100644 --- a/mc_backend/openapi/openapi_server/models/exercise_base.py +++ b/mc_backend/openapi/openapi_server/models/exercise_base.py @@ -166,6 +166,8 @@ class ExerciseBase(Model): :param instructions: The instructions of this ExerciseBase. :type instructions: str """ + if instructions is None: + raise ValueError("Invalid value for `instructions`, must not be `None`") # noqa: E501 self._instructions = instructions @@ -212,6 +214,8 @@ class ExerciseBase(Model): :param search_values: The search_values of this ExerciseBase. :type search_values: str """ + if search_values is None: + raise ValueError("Invalid value for `search_values`, must not be `None`") # noqa: E501 self._search_values = search_values diff --git a/mc_backend/openapi/openapi_server/models/exercise_form.py b/mc_backend/openapi/openapi_server/models/exercise_form.py new file mode 100644 index 0000000..cd1f642 --- /dev/null +++ b/mc_backend/openapi/openapi_server/models/exercise_form.py @@ -0,0 +1,356 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from openapi.openapi_server.models.base_model_ import Model +from openapi.openapi_server.models.exercise_base import ExerciseBase +from openapi.openapi_server.models.exercise_form_all_of import ExerciseFormAllOf +from openapi.openapi_server import util + +from openapi.openapi_server.models.exercise_base import ExerciseBase # noqa: E501 +from openapi.openapi_server.models.exercise_form_all_of import ExerciseFormAllOf # noqa: E501 + +class ExerciseForm(Model): + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + + Do not edit the class manually. + """ + + def __init__(self, correct_feedback='', general_feedback='', incorrect_feedback='', instructions='', partially_correct_feedback='', search_values='[]', work_author='', work_title='', type=None, type_translation=None, urn=None): # noqa: E501 + """ExerciseForm - a model defined in OpenAPI + + :param correct_feedback: The correct_feedback of this ExerciseForm. # noqa: E501 + :type correct_feedback: str + :param general_feedback: The general_feedback of this ExerciseForm. # noqa: E501 + :type general_feedback: str + :param incorrect_feedback: The incorrect_feedback of this ExerciseForm. # noqa: E501 + :type incorrect_feedback: str + :param instructions: The instructions of this ExerciseForm. # noqa: E501 + :type instructions: str + :param partially_correct_feedback: The partially_correct_feedback of this ExerciseForm. # noqa: E501 + :type partially_correct_feedback: str + :param search_values: The search_values of this ExerciseForm. # noqa: E501 + :type search_values: str + :param work_author: The work_author of this ExerciseForm. # noqa: E501 + :type work_author: str + :param work_title: The work_title of this ExerciseForm. # noqa: E501 + :type work_title: str + :param type: The type of this ExerciseForm. # noqa: E501 + :type type: str + :param type_translation: The type_translation of this ExerciseForm. # noqa: E501 + :type type_translation: str + :param urn: The urn of this ExerciseForm. # noqa: E501 + :type urn: str + """ + self.openapi_types = { + 'correct_feedback': str, + 'general_feedback': str, + 'incorrect_feedback': str, + 'instructions': str, + 'partially_correct_feedback': str, + 'search_values': str, + 'work_author': str, + 'work_title': str, + 'type': str, + 'type_translation': str, + 'urn': str + } + + self.attribute_map = { + 'correct_feedback': 'correct_feedback', + 'general_feedback': 'general_feedback', + 'incorrect_feedback': 'incorrect_feedback', + 'instructions': 'instructions', + 'partially_correct_feedback': 'partially_correct_feedback', + 'search_values': 'search_values', + 'work_author': 'work_author', + 'work_title': 'work_title', + 'type': 'type', + 'type_translation': 'type_translation', + 'urn': 'urn' + } + + self._correct_feedback = correct_feedback + self._general_feedback = general_feedback + self._incorrect_feedback = incorrect_feedback + self._instructions = instructions + self._partially_correct_feedback = partially_correct_feedback + self._search_values = search_values + self._work_author = work_author + self._work_title = work_title + self._type = type + self._type_translation = type_translation + self._urn = urn + + @classmethod + def from_dict(cls, dikt) -> 'ExerciseForm': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The ExerciseForm of this ExerciseForm. # noqa: E501 + :rtype: ExerciseForm + """ + return util.deserialize_model(dikt, cls) + + @property + def correct_feedback(self): + """Gets the correct_feedback of this ExerciseForm. + + Feedback for successful completion of the exercise. # noqa: E501 + + :return: The correct_feedback of this ExerciseForm. + :rtype: str + """ + return self._correct_feedback + + @correct_feedback.setter + def correct_feedback(self, correct_feedback): + """Sets the correct_feedback of this ExerciseForm. + + Feedback for successful completion of the exercise. # noqa: E501 + + :param correct_feedback: The correct_feedback of this ExerciseForm. + :type correct_feedback: str + """ + + self._correct_feedback = correct_feedback + + @property + def general_feedback(self): + """Gets the general_feedback of this ExerciseForm. + + Feedback for finishing the exercise. # noqa: E501 + + :return: The general_feedback of this ExerciseForm. + :rtype: str + """ + return self._general_feedback + + @general_feedback.setter + def general_feedback(self, general_feedback): + """Sets the general_feedback of this ExerciseForm. + + Feedback for finishing the exercise. # noqa: E501 + + :param general_feedback: The general_feedback of this ExerciseForm. + :type general_feedback: str + """ + + self._general_feedback = general_feedback + + @property + def incorrect_feedback(self): + """Gets the incorrect_feedback of this ExerciseForm. + + Feedback for failing to complete the exercise successfully. # noqa: E501 + + :return: The incorrect_feedback of this ExerciseForm. + :rtype: str + """ + return self._incorrect_feedback + + @incorrect_feedback.setter + def incorrect_feedback(self, incorrect_feedback): + """Sets the incorrect_feedback of this ExerciseForm. + + Feedback for failing to complete the exercise successfully. # noqa: E501 + + :param incorrect_feedback: The incorrect_feedback of this ExerciseForm. + :type incorrect_feedback: str + """ + + self._incorrect_feedback = incorrect_feedback + + @property + def instructions(self): + """Gets the instructions of this ExerciseForm. + + Hints for how to complete the exercise. # noqa: E501 + + :return: The instructions of this ExerciseForm. + :rtype: str + """ + return self._instructions + + @instructions.setter + def instructions(self, instructions): + """Sets the instructions of this ExerciseForm. + + Hints for how to complete the exercise. # noqa: E501 + + :param instructions: The instructions of this ExerciseForm. + :type instructions: str + """ + if instructions is None: + raise ValueError("Invalid value for `instructions`, must not be `None`") # noqa: E501 + + self._instructions = instructions + + @property + def partially_correct_feedback(self): + """Gets the partially_correct_feedback of this ExerciseForm. + + Feedback for successfully completing certain parts of the exercise. # noqa: E501 + + :return: The partially_correct_feedback of this ExerciseForm. + :rtype: str + """ + return self._partially_correct_feedback + + @partially_correct_feedback.setter + def partially_correct_feedback(self, partially_correct_feedback): + """Sets the partially_correct_feedback of this ExerciseForm. + + Feedback for successfully completing certain parts of the exercise. # noqa: E501 + + :param partially_correct_feedback: The partially_correct_feedback of this ExerciseForm. + :type partially_correct_feedback: str + """ + + self._partially_correct_feedback = partially_correct_feedback + + @property + def search_values(self): + """Gets the search_values of this ExerciseForm. + + Search queries that were used to build the exercise. # noqa: E501 + + :return: The search_values of this ExerciseForm. + :rtype: str + """ + return self._search_values + + @search_values.setter + def search_values(self, search_values): + """Sets the search_values of this ExerciseForm. + + Search queries that were used to build the exercise. # noqa: E501 + + :param search_values: The search_values of this ExerciseForm. + :type search_values: str + """ + if search_values is None: + raise ValueError("Invalid value for `search_values`, must not be `None`") # noqa: E501 + + self._search_values = search_values + + @property + def work_author(self): + """Gets the work_author of this ExerciseForm. + + Name of the person who wrote the base text for the exercise. # noqa: E501 + + :return: The work_author of this ExerciseForm. + :rtype: str + """ + return self._work_author + + @work_author.setter + def work_author(self, work_author): + """Sets the work_author of this ExerciseForm. + + Name of the person who wrote the base text for the exercise. # noqa: E501 + + :param work_author: The work_author of this ExerciseForm. + :type work_author: str + """ + + self._work_author = work_author + + @property + def work_title(self): + """Gets the work_title of this ExerciseForm. + + Title of the base text for the exercise. # noqa: E501 + + :return: The work_title of this ExerciseForm. + :rtype: str + """ + return self._work_title + + @work_title.setter + def work_title(self, work_title): + """Sets the work_title of this ExerciseForm. + + Title of the base text for the exercise. # noqa: E501 + + :param work_title: The work_title of this ExerciseForm. + :type work_title: str + """ + + self._work_title = work_title + + @property + def type(self): + """Gets the type of this ExerciseForm. + + Type of exercise, concerning interaction and layout. # noqa: E501 + + :return: The type of this ExerciseForm. + :rtype: str + """ + return self._type + + @type.setter + def type(self, type): + """Sets the type of this ExerciseForm. + + Type of exercise, concerning interaction and layout. # noqa: E501 + + :param type: The type of this ExerciseForm. + :type type: str + """ + if type is None: + raise ValueError("Invalid value for `type`, must not be `None`") # noqa: E501 + + self._type = type + + @property + def type_translation(self): + """Gets the type_translation of this ExerciseForm. + + Localized expression of the exercise type. # noqa: E501 + + :return: The type_translation of this ExerciseForm. + :rtype: str + """ + return self._type_translation + + @type_translation.setter + def type_translation(self, type_translation): + """Sets the type_translation of this ExerciseForm. + + Localized expression of the exercise type. # noqa: E501 + + :param type_translation: The type_translation of this ExerciseForm. + :type type_translation: str + """ + + self._type_translation = type_translation + + @property + def urn(self): + """Gets the urn of this ExerciseForm. + + CTS URN for the text passage from which the exercise was created. # noqa: E501 + + :return: The urn of this ExerciseForm. + :rtype: str + """ + return self._urn + + @urn.setter + def urn(self, urn): + """Sets the urn of this ExerciseForm. + + CTS URN for the text passage from which the exercise was created. # noqa: E501 + + :param urn: The urn of this ExerciseForm. + :type urn: str + """ + + self._urn = urn diff --git a/mc_backend/openapi/openapi_server/models/exercise_form_all_of.py b/mc_backend/openapi/openapi_server/models/exercise_form_all_of.py new file mode 100644 index 0000000..360b217 --- /dev/null +++ b/mc_backend/openapi/openapi_server/models/exercise_form_all_of.py @@ -0,0 +1,124 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from openapi.openapi_server.models.base_model_ import Model +from openapi.openapi_server import util + + +class ExerciseFormAllOf(Model): + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + + Do not edit the class manually. + """ + + def __init__(self, type=None, type_translation=None, urn=None): # noqa: E501 + """ExerciseFormAllOf - a model defined in OpenAPI + + :param type: The type of this ExerciseFormAllOf. # noqa: E501 + :type type: str + :param type_translation: The type_translation of this ExerciseFormAllOf. # noqa: E501 + :type type_translation: str + :param urn: The urn of this ExerciseFormAllOf. # noqa: E501 + :type urn: str + """ + self.openapi_types = { + 'type': str, + 'type_translation': str, + 'urn': str + } + + self.attribute_map = { + 'type': 'type', + 'type_translation': 'type_translation', + 'urn': 'urn' + } + + self._type = type + self._type_translation = type_translation + self._urn = urn + + @classmethod + def from_dict(cls, dikt) -> 'ExerciseFormAllOf': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The ExerciseForm_allOf of this ExerciseFormAllOf. # noqa: E501 + :rtype: ExerciseFormAllOf + """ + return util.deserialize_model(dikt, cls) + + @property + def type(self): + """Gets the type of this ExerciseFormAllOf. + + Type of exercise, concerning interaction and layout. # noqa: E501 + + :return: The type of this ExerciseFormAllOf. + :rtype: str + """ + return self._type + + @type.setter + def type(self, type): + """Sets the type of this ExerciseFormAllOf. + + Type of exercise, concerning interaction and layout. # noqa: E501 + + :param type: The type of this ExerciseFormAllOf. + :type type: str + """ + if type is None: + raise ValueError("Invalid value for `type`, must not be `None`") # noqa: E501 + + self._type = type + + @property + def type_translation(self): + """Gets the type_translation of this ExerciseFormAllOf. + + Localized expression of the exercise type. # noqa: E501 + + :return: The type_translation of this ExerciseFormAllOf. + :rtype: str + """ + return self._type_translation + + @type_translation.setter + def type_translation(self, type_translation): + """Sets the type_translation of this ExerciseFormAllOf. + + Localized expression of the exercise type. # noqa: E501 + + :param type_translation: The type_translation of this ExerciseFormAllOf. + :type type_translation: str + """ + + self._type_translation = type_translation + + @property + def urn(self): + """Gets the urn of this ExerciseFormAllOf. + + CTS URN for the text passage from which the exercise was created. # noqa: E501 + + :return: The urn of this ExerciseFormAllOf. + :rtype: str + """ + return self._urn + + @urn.setter + def urn(self, urn): + """Sets the urn of this ExerciseFormAllOf. + + CTS URN for the text passage from which the exercise was created. # noqa: E501 + + :param urn: The urn of this ExerciseFormAllOf. + :type urn: str + """ + + self._urn = urn diff --git a/mc_backend/openapi/openapi_server/models/exercise_form_comp.py b/mc_backend/openapi/openapi_server/models/exercise_form_comp.py new file mode 100644 index 0000000..7bd9359 --- /dev/null +++ b/mc_backend/openapi/openapi_server/models/exercise_form_comp.py @@ -0,0 +1,356 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from openapi.openapi_server.models.base_model_ import Model +from openapi.openapi_server.models.exercise_base import ExerciseBase +from openapi.openapi_server.models.exercise_form import ExerciseForm +from openapi.openapi_server import util + +from openapi.openapi_server.models.exercise_base import ExerciseBase # noqa: E501 +from openapi.openapi_server.models.exercise_form import ExerciseForm # noqa: E501 + +class ExerciseFormComp(Model): + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + + Do not edit the class manually. + """ + + def __init__(self, correct_feedback='', general_feedback='', incorrect_feedback='', instructions='', partially_correct_feedback='', search_values='[]', work_author='', work_title='', type=None, type_translation=None, urn=None): # noqa: E501 + """ExerciseFormComp - a model defined in OpenAPI + + :param correct_feedback: The correct_feedback of this ExerciseFormComp. # noqa: E501 + :type correct_feedback: str + :param general_feedback: The general_feedback of this ExerciseFormComp. # noqa: E501 + :type general_feedback: str + :param incorrect_feedback: The incorrect_feedback of this ExerciseFormComp. # noqa: E501 + :type incorrect_feedback: str + :param instructions: The instructions of this ExerciseFormComp. # noqa: E501 + :type instructions: str + :param partially_correct_feedback: The partially_correct_feedback of this ExerciseFormComp. # noqa: E501 + :type partially_correct_feedback: str + :param search_values: The search_values of this ExerciseFormComp. # noqa: E501 + :type search_values: str + :param work_author: The work_author of this ExerciseFormComp. # noqa: E501 + :type work_author: str + :param work_title: The work_title of this ExerciseFormComp. # noqa: E501 + :type work_title: str + :param type: The type of this ExerciseFormComp. # noqa: E501 + :type type: str + :param type_translation: The type_translation of this ExerciseFormComp. # noqa: E501 + :type type_translation: str + :param urn: The urn of this ExerciseFormComp. # noqa: E501 + :type urn: str + """ + self.openapi_types = { + 'correct_feedback': str, + 'general_feedback': str, + 'incorrect_feedback': str, + 'instructions': str, + 'partially_correct_feedback': str, + 'search_values': str, + 'work_author': str, + 'work_title': str, + 'type': str, + 'type_translation': str, + 'urn': str + } + + self.attribute_map = { + 'correct_feedback': 'correct_feedback', + 'general_feedback': 'general_feedback', + 'incorrect_feedback': 'incorrect_feedback', + 'instructions': 'instructions', + 'partially_correct_feedback': 'partially_correct_feedback', + 'search_values': 'search_values', + 'work_author': 'work_author', + 'work_title': 'work_title', + 'type': 'type', + 'type_translation': 'type_translation', + 'urn': 'urn' + } + + self._correct_feedback = correct_feedback + self._general_feedback = general_feedback + self._incorrect_feedback = incorrect_feedback + self._instructions = instructions + self._partially_correct_feedback = partially_correct_feedback + self._search_values = search_values + self._work_author = work_author + self._work_title = work_title + self._type = type + self._type_translation = type_translation + self._urn = urn + + @classmethod + def from_dict(cls, dikt) -> 'ExerciseFormComp': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The ExerciseFormComp of this ExerciseFormComp. # noqa: E501 + :rtype: ExerciseFormComp + """ + return util.deserialize_model(dikt, cls) + + @property + def correct_feedback(self): + """Gets the correct_feedback of this ExerciseFormComp. + + Feedback for successful completion of the exercise. # noqa: E501 + + :return: The correct_feedback of this ExerciseFormComp. + :rtype: str + """ + return self._correct_feedback + + @correct_feedback.setter + def correct_feedback(self, correct_feedback): + """Sets the correct_feedback of this ExerciseFormComp. + + Feedback for successful completion of the exercise. # noqa: E501 + + :param correct_feedback: The correct_feedback of this ExerciseFormComp. + :type correct_feedback: str + """ + + self._correct_feedback = correct_feedback + + @property + def general_feedback(self): + """Gets the general_feedback of this ExerciseFormComp. + + Feedback for finishing the exercise. # noqa: E501 + + :return: The general_feedback of this ExerciseFormComp. + :rtype: str + """ + return self._general_feedback + + @general_feedback.setter + def general_feedback(self, general_feedback): + """Sets the general_feedback of this ExerciseFormComp. + + Feedback for finishing the exercise. # noqa: E501 + + :param general_feedback: The general_feedback of this ExerciseFormComp. + :type general_feedback: str + """ + + self._general_feedback = general_feedback + + @property + def incorrect_feedback(self): + """Gets the incorrect_feedback of this ExerciseFormComp. + + Feedback for failing to complete the exercise successfully. # noqa: E501 + + :return: The incorrect_feedback of this ExerciseFormComp. + :rtype: str + """ + return self._incorrect_feedback + + @incorrect_feedback.setter + def incorrect_feedback(self, incorrect_feedback): + """Sets the incorrect_feedback of this ExerciseFormComp. + + Feedback for failing to complete the exercise successfully. # noqa: E501 + + :param incorrect_feedback: The incorrect_feedback of this ExerciseFormComp. + :type incorrect_feedback: str + """ + + self._incorrect_feedback = incorrect_feedback + + @property + def instructions(self): + """Gets the instructions of this ExerciseFormComp. + + Hints for how to complete the exercise. # noqa: E501 + + :return: The instructions of this ExerciseFormComp. + :rtype: str + """ + return self._instructions + + @instructions.setter + def instructions(self, instructions): + """Sets the instructions of this ExerciseFormComp. + + Hints for how to complete the exercise. # noqa: E501 + + :param instructions: The instructions of this ExerciseFormComp. + :type instructions: str + """ + if instructions is None: + raise ValueError("Invalid value for `instructions`, must not be `None`") # noqa: E501 + + self._instructions = instructions + + @property + def partially_correct_feedback(self): + """Gets the partially_correct_feedback of this ExerciseFormComp. + + Feedback for successfully completing certain parts of the exercise. # noqa: E501 + + :return: The partially_correct_feedback of this ExerciseFormComp. + :rtype: str + """ + return self._partially_correct_feedback + + @partially_correct_feedback.setter + def partially_correct_feedback(self, partially_correct_feedback): + """Sets the partially_correct_feedback of this ExerciseFormComp. + + Feedback for successfully completing certain parts of the exercise. # noqa: E501 + + :param partially_correct_feedback: The partially_correct_feedback of this ExerciseFormComp. + :type partially_correct_feedback: str + """ + + self._partially_correct_feedback = partially_correct_feedback + + @property + def search_values(self): + """Gets the search_values of this ExerciseFormComp. + + Search queries that were used to build the exercise. # noqa: E501 + + :return: The search_values of this ExerciseFormComp. + :rtype: str + """ + return self._search_values + + @search_values.setter + def search_values(self, search_values): + """Sets the search_values of this ExerciseFormComp. + + Search queries that were used to build the exercise. # noqa: E501 + + :param search_values: The search_values of this ExerciseFormComp. + :type search_values: str + """ + if search_values is None: + raise ValueError("Invalid value for `search_values`, must not be `None`") # noqa: E501 + + self._search_values = search_values + + @property + def work_author(self): + """Gets the work_author of this ExerciseFormComp. + + Name of the person who wrote the base text for the exercise. # noqa: E501 + + :return: The work_author of this ExerciseFormComp. + :rtype: str + """ + return self._work_author + + @work_author.setter + def work_author(self, work_author): + """Sets the work_author of this ExerciseFormComp. + + Name of the person who wrote the base text for the exercise. # noqa: E501 + + :param work_author: The work_author of this ExerciseFormComp. + :type work_author: str + """ + + self._work_author = work_author + + @property + def work_title(self): + """Gets the work_title of this ExerciseFormComp. + + Title of the base text for the exercise. # noqa: E501 + + :return: The work_title of this ExerciseFormComp. + :rtype: str + """ + return self._work_title + + @work_title.setter + def work_title(self, work_title): + """Sets the work_title of this ExerciseFormComp. + + Title of the base text for the exercise. # noqa: E501 + + :param work_title: The work_title of this ExerciseFormComp. + :type work_title: str + """ + + self._work_title = work_title + + @property + def type(self): + """Gets the type of this ExerciseFormComp. + + Type of exercise, concerning interaction and layout. # noqa: E501 + + :return: The type of this ExerciseFormComp. + :rtype: str + """ + return self._type + + @type.setter + def type(self, type): + """Sets the type of this ExerciseFormComp. + + Type of exercise, concerning interaction and layout. # noqa: E501 + + :param type: The type of this ExerciseFormComp. + :type type: str + """ + if type is None: + raise ValueError("Invalid value for `type`, must not be `None`") # noqa: E501 + + self._type = type + + @property + def type_translation(self): + """Gets the type_translation of this ExerciseFormComp. + + Localized expression of the exercise type. # noqa: E501 + + :return: The type_translation of this ExerciseFormComp. + :rtype: str + """ + return self._type_translation + + @type_translation.setter + def type_translation(self, type_translation): + """Sets the type_translation of this ExerciseFormComp. + + Localized expression of the exercise type. # noqa: E501 + + :param type_translation: The type_translation of this ExerciseFormComp. + :type type_translation: str + """ + + self._type_translation = type_translation + + @property + def urn(self): + """Gets the urn of this ExerciseFormComp. + + CTS URN for the text passage from which the exercise was created. # noqa: E501 + + :return: The urn of this ExerciseFormComp. + :rtype: str + """ + return self._urn + + @urn.setter + def urn(self, urn): + """Sets the urn of this ExerciseFormComp. + + CTS URN for the text passage from which the exercise was created. # noqa: E501 + + :param urn: The urn of this ExerciseFormComp. + :type urn: str + """ + + self._urn = urn diff --git a/mc_backend/openapi/openapi_server/models/frequency_item.py b/mc_backend/openapi/openapi_server/models/frequency_item.py new file mode 100644 index 0000000..86c6b7e --- /dev/null +++ b/mc_backend/openapi/openapi_server/models/frequency_item.py @@ -0,0 +1,122 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from openapi.openapi_server.models.base_model_ import Model +from openapi.openapi_server import util + + +class FrequencyItem(Model): + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + + Do not edit the class manually. + """ + + def __init__(self, count=None, phenomena=None, values=None): # noqa: E501 + """FrequencyItem - a model defined in OpenAPI + + :param count: The count of this FrequencyItem. # noqa: E501 + :type count: int + :param phenomena: The phenomena of this FrequencyItem. # noqa: E501 + :type phenomena: List[str] + :param values: The values of this FrequencyItem. # noqa: E501 + :type values: List[str] + """ + self.openapi_types = { + 'count': int, + 'phenomena': List[str], + 'values': List[str] + } + + self.attribute_map = { + 'count': 'count', + 'phenomena': 'phenomena', + 'values': 'values' + } + + self._count = count + self._phenomena = phenomena + self._values = values + + @classmethod + def from_dict(cls, dikt) -> 'FrequencyItem': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The FrequencyItem of this FrequencyItem. # noqa: E501 + :rtype: FrequencyItem + """ + return util.deserialize_model(dikt, cls) + + @property + def count(self): + """Gets the count of this FrequencyItem. + + How often the given combination of values occurred. # noqa: E501 + + :return: The count of this FrequencyItem. + :rtype: int + """ + return self._count + + @count.setter + def count(self, count): + """Sets the count of this FrequencyItem. + + How often the given combination of values occurred. # noqa: E501 + + :param count: The count of this FrequencyItem. + :type count: int + """ + + self._count = count + + @property + def phenomena(self): + """Gets the phenomena of this FrequencyItem. + + Labels for the phenomena described in this frequency entry. # noqa: E501 + + :return: The phenomena of this FrequencyItem. + :rtype: List[str] + """ + return self._phenomena + + @phenomena.setter + def phenomena(self, phenomena): + """Sets the phenomena of this FrequencyItem. + + Labels for the phenomena described in this frequency entry. # noqa: E501 + + :param phenomena: The phenomena of this FrequencyItem. + :type phenomena: List[str] + """ + + self._phenomena = phenomena + + @property + def values(self): + """Gets the values of this FrequencyItem. + + Values for the phenomena described in this frequency entry. # noqa: E501 + + :return: The values of this FrequencyItem. + :rtype: List[str] + """ + return self._values + + @values.setter + def values(self, values): + """Sets the values of this FrequencyItem. + + Values for the phenomena described in this frequency entry. # noqa: E501 + + :param values: The values of this FrequencyItem. + :type values: List[str] + """ + + self._values = values diff --git a/mc_backend/openapi/openapi_server/models/graph_data.py b/mc_backend/openapi/openapi_server/models/graph_data.py index dc97ff8..0c49b40 100644 --- a/mc_backend/openapi/openapi_server/models/graph_data.py +++ b/mc_backend/openapi/openapi_server/models/graph_data.py @@ -7,11 +7,11 @@ from typing import List, Dict # noqa: F401 from openapi.openapi_server.models.base_model_ import Model from openapi.openapi_server.models.link import Link -from openapi.openapi_server.models.node import Node +from openapi.openapi_server.models.node_mc import NodeMC from openapi.openapi_server import util from openapi.openapi_server.models.link import Link # noqa: E501 -from openapi.openapi_server.models.node import Node # noqa: E501 +from openapi.openapi_server.models.node_mc import NodeMC # noqa: E501 class GraphData(Model): """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -31,14 +31,14 @@ class GraphData(Model): :param multigraph: The multigraph of this GraphData. # noqa: E501 :type multigraph: bool :param nodes: The nodes of this GraphData. # noqa: E501 - :type nodes: List[Node] + :type nodes: List[NodeMC] """ self.openapi_types = { 'directed': bool, 'graph': object, 'links': List[Link], 'multigraph': bool, - 'nodes': List[Node] + 'nodes': List[NodeMC] } self.attribute_map = { @@ -167,7 +167,7 @@ class GraphData(Model): List of nodes for the graph. # noqa: E501 :return: The nodes of this GraphData. - :rtype: List[Node] + :rtype: List[NodeMC] """ return self._nodes @@ -178,7 +178,7 @@ class GraphData(Model): List of nodes for the graph. # noqa: E501 :param nodes: The nodes of this GraphData. - :type nodes: List[Node] + :type nodes: List[NodeMC] """ if nodes is None: raise ValueError("Invalid value for `nodes`, must not be `None`") # noqa: E501 diff --git a/mc_backend/openapi/openapi_server/models/link.py b/mc_backend/openapi/openapi_server/models/link.py index 2510829..5658339 100644 --- a/mc_backend/openapi/openapi_server/models/link.py +++ b/mc_backend/openapi/openapi_server/models/link.py @@ -82,8 +82,6 @@ class Link(Model): :param annis_component_name: The annis_component_name of this Link. :type annis_component_name: str """ - if annis_component_name is None: - raise ValueError("Invalid value for `annis_component_name`, must not be `None`") # noqa: E501 self._annis_component_name = annis_component_name @@ -107,8 +105,6 @@ class Link(Model): :param annis_component_type: The annis_component_type of this Link. :type annis_component_type: str """ - if annis_component_type is None: - raise ValueError("Invalid value for `annis_component_type`, must not be `None`") # noqa: E501 self._annis_component_type = annis_component_type @@ -132,8 +128,6 @@ class Link(Model): :param source: The source of this Link. :type source: str """ - if source is None: - raise ValueError("Invalid value for `source`, must not be `None`") # noqa: E501 self._source = source @@ -157,8 +151,6 @@ class Link(Model): :param target: The target of this Link. :type target: str """ - if target is None: - raise ValueError("Invalid value for `target`, must not be `None`") # noqa: E501 self._target = target diff --git a/mc_backend/openapi/openapi_server/models/node_mc.py b/mc_backend/openapi/openapi_server/models/node_mc.py new file mode 100644 index 0000000..aa28998 --- /dev/null +++ b/mc_backend/openapi/openapi_server/models/node_mc.py @@ -0,0 +1,346 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from openapi.openapi_server.models.base_model_ import Model +from openapi.openapi_server import util + + +class NodeMC(Model): + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + + Do not edit the class manually. + """ + + def __init__(self, annis_node_name=None, annis_node_type=None, annis_tok=None, annis_type=None, id=None, is_oov=None, udep_lemma=None, udep_upostag=None, udep_xpostag=None, udep_feats=None, solution=None): # noqa: E501 + """NodeMC - a model defined in OpenAPI + + :param annis_node_name: The annis_node_name of this NodeMC. # noqa: E501 + :type annis_node_name: str + :param annis_node_type: The annis_node_type of this NodeMC. # noqa: E501 + :type annis_node_type: str + :param annis_tok: The annis_tok of this NodeMC. # noqa: E501 + :type annis_tok: str + :param annis_type: The annis_type of this NodeMC. # noqa: E501 + :type annis_type: str + :param id: The id of this NodeMC. # noqa: E501 + :type id: str + :param is_oov: The is_oov of this NodeMC. # noqa: E501 + :type is_oov: bool + :param udep_lemma: The udep_lemma of this NodeMC. # noqa: E501 + :type udep_lemma: str + :param udep_upostag: The udep_upostag of this NodeMC. # noqa: E501 + :type udep_upostag: str + :param udep_xpostag: The udep_xpostag of this NodeMC. # noqa: E501 + :type udep_xpostag: str + :param udep_feats: The udep_feats of this NodeMC. # noqa: E501 + :type udep_feats: str + :param solution: The solution of this NodeMC. # noqa: E501 + :type solution: str + """ + self.openapi_types = { + 'annis_node_name': str, + 'annis_node_type': str, + 'annis_tok': str, + 'annis_type': str, + 'id': str, + 'is_oov': bool, + 'udep_lemma': str, + 'udep_upostag': str, + 'udep_xpostag': str, + 'udep_feats': str, + 'solution': str + } + + self.attribute_map = { + 'annis_node_name': 'annis_node_name', + 'annis_node_type': 'annis_node_type', + 'annis_tok': 'annis_tok', + 'annis_type': 'annis_type', + 'id': 'id', + 'is_oov': 'is_oov', + 'udep_lemma': 'udep_lemma', + 'udep_upostag': 'udep_upostag', + 'udep_xpostag': 'udep_xpostag', + 'udep_feats': 'udep_feats', + 'solution': 'solution' + } + + self._annis_node_name = annis_node_name + self._annis_node_type = annis_node_type + self._annis_tok = annis_tok + self._annis_type = annis_type + self._id = id + self._is_oov = is_oov + self._udep_lemma = udep_lemma + self._udep_upostag = udep_upostag + self._udep_xpostag = udep_xpostag + self._udep_feats = udep_feats + self._solution = solution + + @classmethod + def from_dict(cls, dikt) -> 'NodeMC': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The NodeMC of this NodeMC. # noqa: E501 + :rtype: NodeMC + """ + return util.deserialize_model(dikt, cls) + + @property + def annis_node_name(self): + """Gets the annis_node_name of this NodeMC. + + Node name as given by ANNIS. # noqa: E501 + + :return: The annis_node_name of this NodeMC. + :rtype: str + """ + return self._annis_node_name + + @annis_node_name.setter + def annis_node_name(self, annis_node_name): + """Sets the annis_node_name of this NodeMC. + + Node name as given by ANNIS. # noqa: E501 + + :param annis_node_name: The annis_node_name of this NodeMC. + :type annis_node_name: str + """ + + self._annis_node_name = annis_node_name + + @property + def annis_node_type(self): + """Gets the annis_node_type of this NodeMC. + + Node type as given by ANNIS. # noqa: E501 + + :return: The annis_node_type of this NodeMC. + :rtype: str + """ + return self._annis_node_type + + @annis_node_type.setter + def annis_node_type(self, annis_node_type): + """Sets the annis_node_type of this NodeMC. + + Node type as given by ANNIS. # noqa: E501 + + :param annis_node_type: The annis_node_type of this NodeMC. + :type annis_node_type: str + """ + + self._annis_node_type = annis_node_type + + @property + def annis_tok(self): + """Gets the annis_tok of this NodeMC. + + Raw word form as given by ANNIS. # noqa: E501 + + :return: The annis_tok of this NodeMC. + :rtype: str + """ + return self._annis_tok + + @annis_tok.setter + def annis_tok(self, annis_tok): + """Sets the annis_tok of this NodeMC. + + Raw word form as given by ANNIS. # noqa: E501 + + :param annis_tok: The annis_tok of this NodeMC. + :type annis_tok: str + """ + + self._annis_tok = annis_tok + + @property + def annis_type(self): + """Gets the annis_type of this NodeMC. + + Node type as given by ANNIS (?). # noqa: E501 + + :return: The annis_type of this NodeMC. + :rtype: str + """ + return self._annis_type + + @annis_type.setter + def annis_type(self, annis_type): + """Sets the annis_type of this NodeMC. + + Node type as given by ANNIS (?). # noqa: E501 + + :param annis_type: The annis_type of this NodeMC. + :type annis_type: str + """ + + self._annis_type = annis_type + + @property + def id(self): + """Gets the id of this NodeMC. + + Unique identifier for the node in the SALT model. # noqa: E501 + + :return: The id of this NodeMC. + :rtype: str + """ + return self._id + + @id.setter + def id(self, id): + """Sets the id of this NodeMC. + + Unique identifier for the node in the SALT model. # noqa: E501 + + :param id: The id of this NodeMC. + :type id: str + """ + + self._id = id + + @property + def is_oov(self): + """Gets the is_oov of this NodeMC. + + Whether the raw word form is missing in a given vocabulary. # noqa: E501 + + :return: The is_oov of this NodeMC. + :rtype: bool + """ + return self._is_oov + + @is_oov.setter + def is_oov(self, is_oov): + """Sets the is_oov of this NodeMC. + + Whether the raw word form is missing in a given vocabulary. # noqa: E501 + + :param is_oov: The is_oov of this NodeMC. + :type is_oov: bool + """ + + self._is_oov = is_oov + + @property + def udep_lemma(self): + """Gets the udep_lemma of this NodeMC. + + Lemmatized word form. # noqa: E501 + + :return: The udep_lemma of this NodeMC. + :rtype: str + """ + return self._udep_lemma + + @udep_lemma.setter + def udep_lemma(self, udep_lemma): + """Sets the udep_lemma of this NodeMC. + + Lemmatized word form. # noqa: E501 + + :param udep_lemma: The udep_lemma of this NodeMC. + :type udep_lemma: str + """ + + self._udep_lemma = udep_lemma + + @property + def udep_upostag(self): + """Gets the udep_upostag of this NodeMC. + + Universal part of speech tag for the word form. # noqa: E501 + + :return: The udep_upostag of this NodeMC. + :rtype: str + """ + return self._udep_upostag + + @udep_upostag.setter + def udep_upostag(self, udep_upostag): + """Sets the udep_upostag of this NodeMC. + + Universal part of speech tag for the word form. # noqa: E501 + + :param udep_upostag: The udep_upostag of this NodeMC. + :type udep_upostag: str + """ + + self._udep_upostag = udep_upostag + + @property + def udep_xpostag(self): + """Gets the udep_xpostag of this NodeMC. + + Language-specific part of speech tag for the word form. # noqa: E501 + + :return: The udep_xpostag of this NodeMC. + :rtype: str + """ + return self._udep_xpostag + + @udep_xpostag.setter + def udep_xpostag(self, udep_xpostag): + """Sets the udep_xpostag of this NodeMC. + + Language-specific part of speech tag for the word form. # noqa: E501 + + :param udep_xpostag: The udep_xpostag of this NodeMC. + :type udep_xpostag: str + """ + + self._udep_xpostag = udep_xpostag + + @property + def udep_feats(self): + """Gets the udep_feats of this NodeMC. + + Additional morphological information. # noqa: E501 + + :return: The udep_feats of this NodeMC. + :rtype: str + """ + return self._udep_feats + + @udep_feats.setter + def udep_feats(self, udep_feats): + """Sets the udep_feats of this NodeMC. + + Additional morphological information. # noqa: E501 + + :param udep_feats: The udep_feats of this NodeMC. + :type udep_feats: str + """ + + self._udep_feats = udep_feats + + @property + def solution(self): + """Gets the solution of this NodeMC. + + Solution value for this node in an exercise. # noqa: E501 + + :return: The solution of this NodeMC. + :rtype: str + """ + return self._solution + + @solution.setter + def solution(self, solution): + """Sets the solution of this NodeMC. + + Solution value for this node in an exercise. # noqa: E501 + + :param solution: The solution of this NodeMC. + :type solution: str + """ + + self._solution = solution diff --git a/mc_backend/openapi/openapi_server/models/solution_element.py b/mc_backend/openapi/openapi_server/models/solution_element.py index 1e68e2f..b64194b 100644 --- a/mc_backend/openapi/openapi_server/models/solution_element.py +++ b/mc_backend/openapi/openapi_server/models/solution_element.py @@ -77,8 +77,6 @@ class SolutionElement(Model): :param content: The content of this SolutionElement. :type content: str """ - if content is None: - raise ValueError("Invalid value for `content`, must not be `None`") # noqa: E501 self._content = content diff --git a/mc_backend/openapi/openapi_server/openapi/openapi.yaml b/mc_backend/openapi/openapi_server/openapi/openapi.yaml index 656e96b..c0dac33 100644 --- a/mc_backend/openapi/openapi_server/openapi/openapi.yaml +++ b/mc_backend/openapi/openapi_server/openapi/openapi.yaml @@ -145,7 +145,11 @@ paths: post: operationId: mcserver_app_api_exercise_api_post requestBody: - $ref: '#/components/requestBodies/ExerciseForm' + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/ExerciseForm' + required: true responses: "200": content: @@ -155,35 +159,20 @@ paths: description: Exercise data object summary: Creates a new exercise. x-openapi-router-controller: openapi_server.controllers.default_controller + /exercises: + get: + operationId: mcserver_app_api_static_exercises_api_get + responses: + "200": + content: + application/json: + schema: + type: object + description: Metadata for static exercises, including their respective URIs + in the frontend. + summary: Returns metadata for static exercises. + x-openapi-router-controller: openapi_server.controllers.default_controller components: - requestBodies: - ExerciseForm: - content: - application/x-www-form-urlencoded: - schema: - allOf: - - $ref: '#/components/schemas/ExerciseBase' - - description: Additional exercise data. - properties: - type: - description: Type of exercise, concerning interaction and layout. - example: markWords - type: string - type_translation: - description: Localized expression of the exercise type. - example: Cloze - type: string - urn: - description: CTS URN for the text passage from which the exercise - was created. - example: urn:cts:latinLit:phi0448.phi001.perseus-lat2:1.1.1 - type: string - required: - - type - type: object - type: object - x-body-name: exercise_data - required: true schemas: AnnisResponse: description: A response with graph data from ANNIS, possibly with additional @@ -286,7 +275,7 @@ components: frequency_analysis: description: List of items with frequency data for linguistic phenomena. items: - $ref: '#/components/schemas/AnnisResponse_frequency_analysis' + $ref: '#/components/schemas/FrequencyItem' type: array graph_data: $ref: '#/components/schemas/GraphData' @@ -402,6 +391,36 @@ components: description: Title of the base text for the exercise. example: Noctes Atticae type: string + required: + - instructions + - search_values + type: object + ExerciseForm: + allOf: + - $ref: '#/components/schemas/ExerciseBase' + - $ref: '#/components/schemas/ExerciseForm_allOf' + FrequencyItem: + example: + values: [] + count: 1 + phenomena: [] + properties: + count: + description: How often the given combination of values occurred. + example: 1 + type: integer + phenomena: + description: Labels for the phenomena described in this frequency entry. + example: [] + items: + type: string + type: array + values: + description: Values for the phenomena described in this frequency entry. + example: [] + items: + type: string + type: array type: object GraphData: description: Nodes, edges and metadata for a graph. @@ -464,7 +483,7 @@ components: nodes: description: List of nodes for the graph. items: - $ref: '#/components/schemas/Node' + $ref: '#/components/schemas/NodeMC' type: array required: - links @@ -626,13 +645,8 @@ components: description: Dependency relation described by the edge. example: det type: string - required: - - annis_component_name - - annis_component_type - - source - - target type: object - Node: + NodeMC: example: annis_node_name: urn:custom:latinLit:proiel.caes-gal.lat:1.1.1/doc1#sent52548tok1 udep_xpostag: Ne @@ -689,14 +703,6 @@ components: solution: description: Solution value for this node in an exercise. type: string - required: - - annis_node_name - - annis_node_type - - annis_tok - - annis_type - - id - - udep_lemma - - udep_upostag type: object Solution: description: Correct solution for an exercise. @@ -742,7 +748,6 @@ components: example: 9 type: integer required: - - content - sentence_id - token_id type: object @@ -863,28 +868,6 @@ components: - resource_type type: object x-tablename: UpdateInfo - AnnisResponse_frequency_analysis: - example: - values: [] - count: 1 - phenomena: [] - properties: - count: - description: How often the given combination of values occurred. - example: 1 - type: integer - phenomena: - description: Labels for the phenomena described in this frequency entry. - example: [] - items: - type: string - type: array - values: - description: Values for the phenomena described in this frequency entry. - example: [] - items: - type: string - type: array Exercise_allOf: description: Data for creating and evaluating interactive exercises. properties: @@ -947,3 +930,20 @@ components: required: - eid - last_access_time + ExerciseForm_allOf: + description: Additional exercise data. + properties: + type: + description: Type of exercise, concerning interaction and layout. + example: markWords + type: string + type_translation: + description: Localized expression of the exercise type. + example: Cloze + type: string + urn: + description: CTS URN for the text passage from which the exercise was created. + example: urn:cts:latinLit:phi0448.phi001.perseus-lat2:1.1.1 + type: string + required: + - type diff --git a/mc_backend/openapi_generator.py b/mc_backend/openapi_generator.py index ff06801..4b940a8 100644 --- a/mc_backend/openapi_generator.py +++ b/mc_backend/openapi_generator.py @@ -4,7 +4,8 @@ import glob import os openapi_dir: str = "openapi" -for file_path in glob.iglob(os.path.join(openapi_dir, "**/*"), recursive=True): +change_count: int = 0 +for file_path in glob.iglob(os.path.join("mc_backend", openapi_dir, "**/*"), recursive=True): if not os.path.isdir(file_path) and file_path[-3:] == ".py": content: str with open(file_path) as f: @@ -13,3 +14,5 @@ for file_path in glob.iglob(os.path.join(openapi_dir, "**/*"), recursive=True): content = content.replace("import openapi_server", f"import {openapi_dir}.openapi_server") with open(file_path, "w+") as f2: f2.write(content) + change_count += 1 +print(f"Adjusted python imports in {change_count} files.") diff --git a/mc_backend/tests.py b/mc_backend/tests.py index c24331c..e7993e2 100644 --- a/mc_backend/tests.py +++ b/mc_backend/tests.py @@ -376,14 +376,14 @@ class McTestCase(unittest.TestCase): response: Response = Mocks.app_dict[self.class_name].client.get( TestingConfig.SERVER_URI_RAW_TEXT, query_string=dict(urn=Mocks.urn_custom)) self.assertEqual(response.status_code, 404) - mock_get_cs.return_value = MockResponse(json.dumps(Mocks.graph_data.to_dict())) + mock_get_cs.return_value = MockResponse(json.dumps(Mocks.annis_response.to_dict())) response = Mocks.app_dict[self.class_name].client.get(TestingConfig.SERVER_URI_RAW_TEXT, query_string=dict(urn=Mocks.urn_custom)) ar: AnnisResponse = AnnisResponse.from_dict(json.loads(response.get_data(as_text=True))) self.assertEqual(len(ar.graph_data.nodes), 52) - graph_data_raw: dict = dict(Mocks.annis_response_dict["graph_data_raw"]) - graph_data_raw["nodes"] = [] - mock_get_cs.return_value = MockResponse(json.dumps(graph_data_raw)) + ar_copy: AnnisResponse = AnnisResponse.from_dict(Mocks.annis_response.to_dict()) + ar_copy.graph_data.nodes = [] + mock_get_cs.return_value = MockResponse(json.dumps(ar_copy.to_dict())) response = Mocks.app_dict[self.class_name].client.get(TestingConfig.SERVER_URI_RAW_TEXT, query_string=dict(urn=Mocks.urn_custom)) self.assertEqual(response.status_code, 404) @@ -413,15 +413,19 @@ class McTestCase(unittest.TestCase): for exercise in exercises: shutil.rmtree(os.path.join(Config.TMP_DIRECTORY, exercise[0]), ignore_errors=True) zip_content: bytes = open(TestingConfig.STATIC_EXERCISES_ZIP_FILE_PATH, "rb").read() - with patch.object(mcserver.app.api.staticExercisesAPI.requests, "get", - side_effect=[MockResponse("{}", ok=False), MockResponse("{}", content=zip_content)]): - with patch.object(AnnotationService, "get_udpipe", return_value=Mocks.static_exercises_udpipe_string): - response = Mocks.app_dict[self.class_name].client.get(TestingConfig.SERVER_URI_STATIC_EXERCISES) - self.assertEqual(response.status_code, 503) + with patch.object( + mcserver.app.api.staticExercisesAPI.requests, "get", side_effect=[ + MockResponse("{}", ok=False), MockResponse("{}", content=zip_content)]): + with patch.object(AnnotationService, "get_udpipe", + return_value=Mocks.static_exercises_udpipe_string) as mock_udpipe: response: Response = Mocks.app_dict[self.class_name].client.get( TestingConfig.SERVER_URI_STATIC_EXERCISES) + self.assertEqual(response.status_code, 503) + response = Mocks.app_dict[self.class_name].client.get(TestingConfig.SERVER_URI_STATIC_EXERCISES) os.remove(TestingConfig.STATIC_EXERCISES_ZIP_FILE_PATH) - self.assertGreater(len(response.data.decode("utf-8")), 1900) + self.assertGreater(len(response.get_data(as_text=True)), 1900) + response = Mocks.app_dict[self.class_name].client.get(TestingConfig.SERVER_URI_STATIC_EXERCISES) + self.assertEqual(mock_udpipe.call_count, 1) @patch('mcserver.app.services.corpusService.requests.get', side_effect=mocked_requests_get) def test_api_subgraph_get(self, mock_get: MagicMock): @@ -490,7 +494,7 @@ class McTestCase(unittest.TestCase): def test_api_vocabulary_get(self, mock_post: MagicMock): """ Calculates lexical overlap between a text (specified by URN) and a static vocabulary. """ with patch.object(mcserver.app.services.corpusService.requests, "get", - return_value=MockResponse(json.dumps(Mocks.graph_data.to_dict()))): + return_value=MockResponse(json.dumps(Mocks.annis_response.to_dict()))): args: dict = dict(query_urn=Mocks.urn_custom, show_oov=True, vocabulary=VocabularyCorpus.agldt.name, frequency_upper_bound=500) response: Response = Mocks.app_dict[self.class_name].client.get(TestingConfig.SERVER_URI_VOCABULARY, @@ -838,7 +842,7 @@ class CommonTestCase(unittest.TestCase): """Initializes the testing environment.""" self.start_time = time.time() self.class_name: str = str(self.__class__) - TestHelper.update_flask_app(self.class_name, create_app) + TestHelper.update_flask_app(self.class_name, create_csm_app) def tearDown(self): """Finishes testing by removing the traces.""" @@ -1010,6 +1014,7 @@ class CommonTestCase(unittest.TestCase): def test_init_db_corpus(self): """Initializes the corpus table.""" + db.session.query(Corpus).delete() cc: CustomCorpus = CustomCorpusService.custom_corpora[0] old_corpus: Corpus = Mocks.corpora[0] old_corpus.source_urn = cc.corpus.source_urn diff --git a/mc_frontend/openapi/.gitignore b/mc_frontend/openapi/.gitignore new file mode 100644 index 0000000..149b576 --- /dev/null +++ b/mc_frontend/openapi/.gitignore @@ -0,0 +1,4 @@ +wwwroot/*.js +node_modules +typings +dist diff --git a/mc_frontend/openapi/.openapi-generator-ignore b/mc_frontend/openapi/.openapi-generator-ignore new file mode 100644 index 0000000..7484ee5 --- /dev/null +++ b/mc_frontend/openapi/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/mc_frontend/openapi/.openapi-generator/VERSION b/mc_frontend/openapi/.openapi-generator/VERSION new file mode 100644 index 0000000..ecedc98 --- /dev/null +++ b/mc_frontend/openapi/.openapi-generator/VERSION @@ -0,0 +1 @@ +4.3.1 \ No newline at end of file diff --git a/mc_frontend/openapi/README.md b/mc_frontend/openapi/README.md new file mode 100644 index 0000000..e0c9192 --- /dev/null +++ b/mc_frontend/openapi/README.md @@ -0,0 +1,205 @@ +## @ + +### Building + +To install the required dependencies and to build the typescript sources run: +``` +npm install +npm run build +``` + +### publishing + +First build the package then run ```npm publish``` + +### consuming + +Navigate to the folder of your consuming project and run one of next commands. + +_published:_ + +``` +npm install @ --save +``` + +_without publishing (not recommended):_ + +``` +npm install PATH_TO_GENERATED_PACKAGE/-.tgz --save +``` + +_It's important to take the tgz file, otherwise you'll get trouble with links on windows_ + +_using `npm link`:_ + +In PATH_TO_GENERATED_PACKAGE: +``` +npm link +``` + +In your project: +``` +npm link +``` + +__Note for Windows users:__ The Angular CLI has troubles to use linked npm packages. +Please refer to this issue https://github.com/angular/angular-cli/issues/8284 for a solution / workaround. +Published packages are not effected by this issue. + + +#### General usage + +In your Angular project: + + +``` +// without configuring providers +import { ApiModule } from ''; +import { HttpClientModule } from '@angular/common/http'; + + +@NgModule({ + imports: [ + ApiModule, + // make sure to import the HttpClientModule in the AppModule only, + // see https://github.com/angular/angular/issues/20575 + HttpClientModule + ], + declarations: [ AppComponent ], + providers: [], + bootstrap: [ AppComponent ] +}) +export class AppModule {} +``` + +``` +// configuring providers +import { ApiModule, Configuration, ConfigurationParameters } from ''; + +export function apiConfigFactory (): Configuration => { + const params: ConfigurationParameters = { + // set configuration parameters here. + } + return new Configuration(params); +} + +@NgModule({ + imports: [ ApiModule.forRoot(apiConfigFactory) ], + declarations: [ AppComponent ], + providers: [], + bootstrap: [ AppComponent ] +}) +export class AppModule {} +``` + +``` +// configuring providers with an authentication service that manages your access tokens +import { ApiModule, Configuration } from ''; + +@NgModule({ + imports: [ ApiModule ], + declarations: [ AppComponent ], + providers: [ + { + provide: Configuration, + useFactory: (authService: AuthService) => new Configuration( + { + basePath: environment.apiUrl, + accessToken: authService.getAccessToken.bind(authService) + } + ), + deps: [AuthService], + multi: false + } + ], + bootstrap: [ AppComponent ] +}) +export class AppModule {} +``` + +``` +import { DefaultApi } from ''; + +export class AppComponent { + constructor(private apiGateway: DefaultApi) { } +} +``` + +Note: The ApiModule is restricted to being instantiated once app wide. +This is to ensure that all services are treated as singletons. + +#### Using multiple OpenAPI files / APIs / ApiModules +In order to use multiple `ApiModules` generated from different OpenAPI files, +you can create an alias name when importing the modules +in order to avoid naming conflicts: +``` +import { ApiModule } from 'my-api-path'; +import { ApiModule as OtherApiModule } from 'my-other-api-path'; +import { HttpClientModule } from '@angular/common/http'; + + +@NgModule({ + imports: [ + ApiModule, + OtherApiModule, + // make sure to import the HttpClientModule in the AppModule only, + // see https://github.com/angular/angular/issues/20575 + HttpClientModule + ] +}) +export class AppModule { + +} +``` + + +### Set service base path +If different than the generated base path, during app bootstrap, you can provide the base path to your service. + +``` +import { BASE_PATH } from ''; + +bootstrap(AppComponent, [ + { provide: BASE_PATH, useValue: 'https://your-web-service.com' }, +]); +``` +or + +``` +import { BASE_PATH } from ''; + +@NgModule({ + imports: [], + declarations: [ AppComponent ], + providers: [ provide: BASE_PATH, useValue: 'https://your-web-service.com' ], + bootstrap: [ AppComponent ] +}) +export class AppModule {} +``` + + +#### Using @angular/cli +First extend your `src/environments/*.ts` files by adding the corresponding base path: + +``` +export const environment = { + production: false, + API_BASE_PATH: 'http://127.0.0.1:8080' +}; +``` + +In the src/app/app.module.ts: +``` +import { BASE_PATH } from ''; +import { environment } from '../environments/environment'; + +@NgModule({ + declarations: [ + AppComponent + ], + imports: [ ], + providers: [{ provide: BASE_PATH, useValue: environment.API_BASE_PATH }], + bootstrap: [ AppComponent ] +}) +export class AppModule { } +``` diff --git a/mc_frontend/openapi/api.module.ts b/mc_frontend/openapi/api.module.ts new file mode 100644 index 0000000..6cdacac --- /dev/null +++ b/mc_frontend/openapi/api.module.ts @@ -0,0 +1,32 @@ +import { NgModule, ModuleWithProviders, SkipSelf, Optional } from '@angular/core'; +import { Configuration } from './configuration'; +import { HttpClient } from '@angular/common/http'; + + +import { DefaultService } from './api/default.service'; + +@NgModule({ + imports: [], + declarations: [], + exports: [], + providers: [] +}) +export class ApiModule { + public static forRoot(configurationFactory: () => Configuration): ModuleWithProviders<ApiModule> { + return { + ngModule: ApiModule, + providers: [ { provide: Configuration, useFactory: configurationFactory } ] + }; + } + + constructor( @Optional() @SkipSelf() parentModule: ApiModule, + @Optional() http: HttpClient) { + if (parentModule) { + throw new Error('ApiModule is already loaded. Import in your base AppModule only.'); + } + if (!http) { + throw new Error('You need to import the HttpClientModule in your AppModule! \n' + + 'See also https://github.com/angular/angular/issues/20575'); + } + } +} diff --git a/mc_frontend/openapi/api/api.ts b/mc_frontend/openapi/api/api.ts new file mode 100644 index 0000000..8e76619 --- /dev/null +++ b/mc_frontend/openapi/api/api.ts @@ -0,0 +1,3 @@ +export * from './default.service'; +import { DefaultService } from './default.service'; +export const APIS = [DefaultService]; diff --git a/mc_frontend/openapi/api/default.service.ts b/mc_frontend/openapi/api/default.service.ts new file mode 100644 index 0000000..90aa61b --- /dev/null +++ b/mc_frontend/openapi/api/default.service.ts @@ -0,0 +1,430 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +/* tslint:disable:no-unused-variable member-ordering */ + +import { Inject, Injectable, Optional } from '@angular/core'; +import { HttpClient, HttpHeaders, HttpParams, + HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; +import { CustomHttpParameterCodec } from '../encoder'; +import { Observable } from 'rxjs'; + +import { AnnisResponse } from '../model/models'; +import { Corpus } from '../model/models'; + +import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; +import { Configuration } from '../configuration'; + + + +@Injectable({ + providedIn: 'root' +}) +export class DefaultService { + + protected basePath = 'http://localhost:5000/mc/api/v1.0'; + public defaultHeaders = new HttpHeaders(); + public configuration = new Configuration(); + public encoder: HttpParameterCodec; + + constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { + if (configuration) { + this.configuration = configuration; + } + if (typeof this.configuration.basePath !== 'string') { + if (typeof basePath !== 'string') { + basePath = this.basePath; + } + this.configuration.basePath = basePath; + } + this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); + } + + + + private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { + if (typeof value === "object" && value instanceof Date === false) { + httpParams = this.addToHttpParamsRecursive(httpParams, value); + } else { + httpParams = this.addToHttpParamsRecursive(httpParams, value, key); + } + return httpParams; + } + + private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { + if (value == null) { + return httpParams; + } + + if (typeof value === "object") { + if (Array.isArray(value)) { + (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); + } else if (value instanceof Date) { + if (key != null) { + httpParams = httpParams.append(key, + (value as Date).toISOString().substr(0, 10)); + } else { + throw Error("key may not be null if value is Date"); + } + } else { + Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( + httpParams, value[k], key != null ? `${key}.${k}` : k)); + } + } else if (key != null) { + httpParams = httpParams.append(key, value); + } else { + throw Error("key may not be null if value is not object or array"); + } + return httpParams; + } + + /** + * Deletes a single corpus by ID. + * @param cid Corpus identifier. + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public mcserverAppApiCorpusAPIDelete(cid: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<boolean>; + public mcserverAppApiCorpusAPIDelete(cid: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<boolean>>; + public mcserverAppApiCorpusAPIDelete(cid: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<boolean>>; + public mcserverAppApiCorpusAPIDelete(cid: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable<any> { + if (cid === null || cid === undefined) { + throw new Error('Required parameter cid was null or undefined when calling mcserverAppApiCorpusAPIDelete.'); + } + + let headers = this.defaultHeaders; + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.delete<boolean>(`${this.configuration.basePath}/corpora/${encodeURIComponent(String(cid))}`, + { + responseType: <any>responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Returns a single corpus by ID. + * @param cid Corpus identifier. + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public mcserverAppApiCorpusAPIGet(cid: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<Corpus>; + public mcserverAppApiCorpusAPIGet(cid: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<Corpus>>; + public mcserverAppApiCorpusAPIGet(cid: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<Corpus>>; + public mcserverAppApiCorpusAPIGet(cid: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable<any> { + if (cid === null || cid === undefined) { + throw new Error('Required parameter cid was null or undefined when calling mcserverAppApiCorpusAPIGet.'); + } + + let headers = this.defaultHeaders; + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.get<Corpus>(`${this.configuration.basePath}/corpora/${encodeURIComponent(String(cid))}`, + { + responseType: <any>responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Updates a single corpus by ID. + * @param cid Corpus identifier. + * @param author Author of the texts in the corpus. + * @param sourceUrn CTS base URN for referencing the corpus. + * @param title Corpus title. + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public mcserverAppApiCorpusAPIPatch(cid: number, author?: string, sourceUrn?: string, title?: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<Corpus>; + public mcserverAppApiCorpusAPIPatch(cid: number, author?: string, sourceUrn?: string, title?: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<Corpus>>; + public mcserverAppApiCorpusAPIPatch(cid: number, author?: string, sourceUrn?: string, title?: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<Corpus>>; + public mcserverAppApiCorpusAPIPatch(cid: number, author?: string, sourceUrn?: string, title?: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable<any> { + if (cid === null || cid === undefined) { + throw new Error('Required parameter cid was null or undefined when calling mcserverAppApiCorpusAPIPatch.'); + } + + let queryParameters = new HttpParams({encoder: this.encoder}); + if (author !== undefined && author !== null) { + queryParameters = this.addToHttpParams(queryParameters, + <any>author, 'author'); + } + if (sourceUrn !== undefined && sourceUrn !== null) { + queryParameters = this.addToHttpParams(queryParameters, + <any>sourceUrn, 'source_urn'); + } + if (title !== undefined && title !== null) { + queryParameters = this.addToHttpParams(queryParameters, + <any>title, 'title'); + } + + let headers = this.defaultHeaders; + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.patch<Corpus>(`${this.configuration.basePath}/corpora/${encodeURIComponent(String(cid))}`, + null, + { + params: queryParameters, + responseType: <any>responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Returns a list of corpora. + * @param lastUpdateTime Time (in milliseconds) of the last update. + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public mcserverAppApiCorpusListAPIGet(lastUpdateTime: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<Corpus>; + public mcserverAppApiCorpusListAPIGet(lastUpdateTime: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<Corpus>>; + public mcserverAppApiCorpusListAPIGet(lastUpdateTime: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<Corpus>>; + public mcserverAppApiCorpusListAPIGet(lastUpdateTime: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable<any> { + if (lastUpdateTime === null || lastUpdateTime === undefined) { + throw new Error('Required parameter lastUpdateTime was null or undefined when calling mcserverAppApiCorpusListAPIGet.'); + } + + let queryParameters = new HttpParams({encoder: this.encoder}); + if (lastUpdateTime !== undefined && lastUpdateTime !== null) { + queryParameters = this.addToHttpParams(queryParameters, + <any>lastUpdateTime, 'last_update_time'); + } + + let headers = this.defaultHeaders; + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.get<Corpus>(`${this.configuration.basePath}/corpora`, + { + params: queryParameters, + responseType: <any>responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Returns exercise data by ID. + * @param eid Unique identifier (UUID) for the exercise. + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public mcserverAppApiExerciseAPIGet(eid: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<AnnisResponse>; + public mcserverAppApiExerciseAPIGet(eid: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<AnnisResponse>>; + public mcserverAppApiExerciseAPIGet(eid: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<AnnisResponse>>; + public mcserverAppApiExerciseAPIGet(eid: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable<any> { + if (eid === null || eid === undefined) { + throw new Error('Required parameter eid was null or undefined when calling mcserverAppApiExerciseAPIGet.'); + } + + let queryParameters = new HttpParams({encoder: this.encoder}); + if (eid !== undefined && eid !== null) { + queryParameters = this.addToHttpParams(queryParameters, + <any>eid, 'eid'); + } + + let headers = this.defaultHeaders; + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.get<AnnisResponse>(`${this.configuration.basePath}/exercise`, + { + params: queryParameters, + responseType: <any>responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Creates a new exercise. + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public mcserverAppApiExerciseAPIPost(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<AnnisResponse>; + public mcserverAppApiExerciseAPIPost(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<AnnisResponse>>; + public mcserverAppApiExerciseAPIPost(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<AnnisResponse>>; + public mcserverAppApiExerciseAPIPost(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable<any> { + + let headers = this.defaultHeaders; + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.post<AnnisResponse>(`${this.configuration.basePath}/exercise`, + null, + { + responseType: <any>responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Returns metadata for static exercises. + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public mcserverAppApiStaticExercisesAPIGet(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<object>; + public mcserverAppApiStaticExercisesAPIGet(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<object>>; + public mcserverAppApiStaticExercisesAPIGet(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<object>>; + public mcserverAppApiStaticExercisesAPIGet(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable<any> { + + let headers = this.defaultHeaders; + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.get<object>(`${this.configuration.basePath}/exercises`, + { + responseType: <any>responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + +} diff --git a/mc_frontend/openapi/configuration.ts b/mc_frontend/openapi/configuration.ts new file mode 100644 index 0000000..c038bbc --- /dev/null +++ b/mc_frontend/openapi/configuration.ts @@ -0,0 +1,84 @@ +import { HttpParameterCodec } from '@angular/common/http'; + +export interface ConfigurationParameters { + apiKeys?: {[ key: string ]: string}; + username?: string; + password?: string; + accessToken?: string | (() => string); + basePath?: string; + withCredentials?: boolean; + encoder?: HttpParameterCodec; +} + +export class Configuration { + apiKeys?: {[ key: string ]: string}; + username?: string; + password?: string; + accessToken?: string | (() => string); + basePath?: string; + withCredentials?: boolean; + encoder?: HttpParameterCodec; + + constructor(configurationParameters: ConfigurationParameters = {}) { + this.apiKeys = configurationParameters.apiKeys; + this.username = configurationParameters.username; + this.password = configurationParameters.password; + this.accessToken = configurationParameters.accessToken; + this.basePath = configurationParameters.basePath; + this.withCredentials = configurationParameters.withCredentials; + this.encoder = configurationParameters.encoder; + } + + /** + * Select the correct content-type to use for a request. + * Uses {@link Configuration#isJsonMime} to determine the correct content-type. + * If no content type is found return the first found type if the contentTypes is not empty + * @param contentTypes - the array of content types that are available for selection + * @returns the selected content-type or <code>undefined</code> if no selection could be made. + */ + public selectHeaderContentType (contentTypes: string[]): string | undefined { + if (contentTypes.length === 0) { + return undefined; + } + + const type = contentTypes.find((x: string) => this.isJsonMime(x)); + if (type === undefined) { + return contentTypes[0]; + } + return type; + } + + /** + * Select the correct accept content-type to use for a request. + * Uses {@link Configuration#isJsonMime} to determine the correct accept content-type. + * If no content type is found return the first found type if the contentTypes is not empty + * @param accepts - the array of content types that are available for selection. + * @returns the selected content-type or <code>undefined</code> if no selection could be made. + */ + public selectHeaderAccept(accepts: string[]): string | undefined { + if (accepts.length === 0) { + return undefined; + } + + const type = accepts.find((x: string) => this.isJsonMime(x)); + if (type === undefined) { + return accepts[0]; + } + return type; + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + public isJsonMime(mime: string): boolean { + const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i'); + return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json'); + } +} diff --git a/mc_frontend/openapi/encoder.ts b/mc_frontend/openapi/encoder.ts new file mode 100644 index 0000000..cbefb4a --- /dev/null +++ b/mc_frontend/openapi/encoder.ts @@ -0,0 +1,21 @@ +import { HttpParameterCodec } from '@angular/common/http'; + +/** + * Custom HttpParameterCodec + * Workaround for https://github.com/angular/angular/issues/18261 + */ +export class CustomHttpParameterCodec implements HttpParameterCodec { + encodeKey(k: string): string { + return encodeURIComponent(k); + } + encodeValue(v: string): string { + return encodeURIComponent(v); + } + decodeKey(k: string): string { + return decodeURIComponent(k); + } + decodeValue(v: string): string { + return decodeURIComponent(v); + } +} + diff --git a/mc_frontend/openapi/git_push.sh b/mc_frontend/openapi/git_push.sh new file mode 100644 index 0000000..ced3be2 --- /dev/null +++ b/mc_frontend/openapi/git_push.sh @@ -0,0 +1,58 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-pestore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ "$git_host" = "" ]; then + git_host="github.com" + echo "[INFO] No command line input provided. Set \$git_host to $git_host" +fi + +if [ "$git_user_id" = "" ]; then + git_user_id="GIT_USER_ID" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="GIT_REPO_ID" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=`git remote` +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:${GIT_TOKEN}@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' + diff --git a/mc_frontend/openapi/index.ts b/mc_frontend/openapi/index.ts new file mode 100644 index 0000000..c312b70 --- /dev/null +++ b/mc_frontend/openapi/index.ts @@ -0,0 +1,5 @@ +export * from './api/api'; +export * from './model/models'; +export * from './variables'; +export * from './configuration'; +export * from './api.module'; \ No newline at end of file diff --git a/mc_frontend/openapi/model/annisResponse.ts b/mc_frontend/openapi/model/annisResponse.ts new file mode 100644 index 0000000..cbec68d --- /dev/null +++ b/mc_frontend/openapi/model/annisResponse.ts @@ -0,0 +1,45 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +import { FrequencyItem } from './frequencyItem'; +import { TextComplexity } from './textComplexity'; +import { GraphData } from './graphData'; +import { Solution } from './solution'; + + +/** + * A response with graph data from ANNIS, possibly with additional data for exercises. + */ +export interface AnnisResponse { + /** + * Unique identifier (UUID) for the exercise. + */ + exercise_id?: string; + /** + * Type of exercise, concerning interaction and layout. + */ + exercise_type?: string; + /** + * List of items with frequency data for linguistic phenomena. + */ + frequency_analysis?: Array<FrequencyItem>; + graph_data?: GraphData; + /** + * Correct solutions for this exercise. + */ + solutions?: Array<Solution>; + text_complexity?: TextComplexity; + /** + * URI for accessing the exercise in this API. + */ + uri?: string; +} + diff --git a/mc_frontend/openapi/model/annisResponseFrequencyAnalysis.ts b/mc_frontend/openapi/model/annisResponseFrequencyAnalysis.ts new file mode 100644 index 0000000..e9f2e78 --- /dev/null +++ b/mc_frontend/openapi/model/annisResponseFrequencyAnalysis.ts @@ -0,0 +1,28 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface AnnisResponseFrequencyAnalysis { + /** + * How often the given combination of values occurred. + */ + count?: number; + /** + * Labels for the phenomena described in this frequency entry. + */ + phenomena?: Array<string>; + /** + * Values for the phenomena described in this frequency entry. + */ + values?: Array<string>; +} + diff --git a/mc_frontend/openapi/model/corpus.ts b/mc_frontend/openapi/model/corpus.ts new file mode 100644 index 0000000..f7fbe45 --- /dev/null +++ b/mc_frontend/openapi/model/corpus.ts @@ -0,0 +1,47 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * Collection of texts. + */ +export interface Corpus { + /** + * Author of the texts in the corpus. + */ + author?: string; + /** + * Unique identifier for the corpus. + */ + cid?: number; + /** + * First level for citing the corpus. + */ + citation_level_1?: string; + /** + * Second level for citing the corpus. + */ + citation_level_2?: string; + /** + * Third level for citing the corpus. + */ + citation_level_3?: string; + /** + * CTS base URN for referencing the corpus. + */ + source_urn: string; + /** + * Corpus title. + */ + title?: string; +} + diff --git a/mc_frontend/openapi/model/exercise.ts b/mc_frontend/openapi/model/exercise.ts new file mode 100644 index 0000000..b19cf21 --- /dev/null +++ b/mc_frontend/openapi/model/exercise.ts @@ -0,0 +1,86 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +import { ExerciseAllOf } from './exerciseAllOf'; +import { ExerciseBase } from './exerciseBase'; + + +export interface Exercise { + /** + * Feedback for successful completion of the exercise. + */ + correct_feedback?: string; + /** + * Feedback for finishing the exercise. + */ + general_feedback?: string; + /** + * Feedback for failing to complete the exercise successfully. + */ + incorrect_feedback?: string; + /** + * Hints for how to complete the exercise. + */ + instructions: string; + /** + * Feedback for successfully completing certain parts of the exercise. + */ + partially_correct_feedback?: string; + /** + * Search queries that were used to build the exercise. + */ + search_values: string; + /** + * Name of the person who wrote the base text for the exercise. + */ + work_author?: string; + /** + * Title of the base text for the exercise. + */ + work_title?: string; + /** + * CONLL-formatted linguistic annotations represented as a single string. + */ + conll?: string; + /** + * Unique identifier (UUID) for the exercise. + */ + eid: string; + /** + * Type of exercise, concerning interaction and layout. + */ + exercise_type?: string; + /** + * Localized expression of the exercise type. + */ + exercise_type_translation?: string; + /** + * ISO 639-1 Language Code for the localization of exercise content. + */ + language?: string; + /** + * When the exercise was last accessed (as POSIX timestamp). + */ + last_access_time: number; + /** + * Correct solutions for the exercise. + */ + solutions?: string; + /** + * Overall text complexity as measured by the software\'s internal language analysis. + */ + text_complexity?: number; + /** + * CTS URN for the text passage from which the exercise was created. + */ + urn?: string; +} + diff --git a/mc_frontend/openapi/model/exerciseAllOf.ts b/mc_frontend/openapi/model/exerciseAllOf.ts new file mode 100644 index 0000000..2b66c2e --- /dev/null +++ b/mc_frontend/openapi/model/exerciseAllOf.ts @@ -0,0 +1,55 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * Data for creating and evaluating interactive exercises. + */ +export interface ExerciseAllOf { + /** + * CONLL-formatted linguistic annotations represented as a single string. + */ + conll?: string; + /** + * Unique identifier (UUID) for the exercise. + */ + eid: string; + /** + * Type of exercise, concerning interaction and layout. + */ + exercise_type?: string; + /** + * Localized expression of the exercise type. + */ + exercise_type_translation?: string; + /** + * ISO 639-1 Language Code for the localization of exercise content. + */ + language?: string; + /** + * When the exercise was last accessed (as POSIX timestamp). + */ + last_access_time: number; + /** + * Correct solutions for the exercise. + */ + solutions?: string; + /** + * Overall text complexity as measured by the software\'s internal language analysis. + */ + text_complexity?: number; + /** + * CTS URN for the text passage from which the exercise was created. + */ + urn?: string; +} + diff --git a/mc_frontend/openapi/model/exerciseBase.ts b/mc_frontend/openapi/model/exerciseBase.ts new file mode 100644 index 0000000..2c7bc22 --- /dev/null +++ b/mc_frontend/openapi/model/exerciseBase.ts @@ -0,0 +1,51 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * Base data for creating and evaluating interactive exercises. + */ +export interface ExerciseBase { + /** + * Feedback for successful completion of the exercise. + */ + correct_feedback?: string; + /** + * Feedback for finishing the exercise. + */ + general_feedback?: string; + /** + * Feedback for failing to complete the exercise successfully. + */ + incorrect_feedback?: string; + /** + * Hints for how to complete the exercise. + */ + instructions: string; + /** + * Feedback for successfully completing certain parts of the exercise. + */ + partially_correct_feedback?: string; + /** + * Search queries that were used to build the exercise. + */ + search_values: string; + /** + * Name of the person who wrote the base text for the exercise. + */ + work_author?: string; + /** + * Title of the base text for the exercise. + */ + work_title?: string; +} + diff --git a/mc_frontend/openapi/model/exerciseForm.ts b/mc_frontend/openapi/model/exerciseForm.ts new file mode 100644 index 0000000..9203fa7 --- /dev/null +++ b/mc_frontend/openapi/model/exerciseForm.ts @@ -0,0 +1,62 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +import { ExerciseFormAllOf } from './exerciseFormAllOf'; +import { ExerciseBase } from './exerciseBase'; + + +export interface ExerciseForm { + /** + * Feedback for successful completion of the exercise. + */ + correct_feedback?: string; + /** + * Feedback for finishing the exercise. + */ + general_feedback?: string; + /** + * Feedback for failing to complete the exercise successfully. + */ + incorrect_feedback?: string; + /** + * Hints for how to complete the exercise. + */ + instructions: string; + /** + * Feedback for successfully completing certain parts of the exercise. + */ + partially_correct_feedback?: string; + /** + * Search queries that were used to build the exercise. + */ + search_values: string; + /** + * Name of the person who wrote the base text for the exercise. + */ + work_author?: string; + /** + * Title of the base text for the exercise. + */ + work_title?: string; + /** + * Type of exercise, concerning interaction and layout. + */ + type: string; + /** + * Localized expression of the exercise type. + */ + type_translation?: string; + /** + * CTS URN for the text passage from which the exercise was created. + */ + urn?: string; +} + diff --git a/mc_frontend/openapi/model/exerciseFormAllOf.ts b/mc_frontend/openapi/model/exerciseFormAllOf.ts new file mode 100644 index 0000000..791849c --- /dev/null +++ b/mc_frontend/openapi/model/exerciseFormAllOf.ts @@ -0,0 +1,31 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * Additional exercise data. + */ +export interface ExerciseFormAllOf { + /** + * Type of exercise, concerning interaction and layout. + */ + type: string; + /** + * Localized expression of the exercise type. + */ + type_translation?: string; + /** + * CTS URN for the text passage from which the exercise was created. + */ + urn?: string; +} + diff --git a/mc_frontend/openapi/model/exerciseFormComp.ts b/mc_frontend/openapi/model/exerciseFormComp.ts new file mode 100644 index 0000000..d9cb53b --- /dev/null +++ b/mc_frontend/openapi/model/exerciseFormComp.ts @@ -0,0 +1,62 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +import { ExerciseForm } from './exerciseForm'; +import { ExerciseBase } from './exerciseBase'; + + +export interface ExerciseFormComp { + /** + * Feedback for successful completion of the exercise. + */ + correct_feedback?: string; + /** + * Feedback for finishing the exercise. + */ + general_feedback?: string; + /** + * Feedback for failing to complete the exercise successfully. + */ + incorrect_feedback?: string; + /** + * Hints for how to complete the exercise. + */ + instructions: string; + /** + * Feedback for successfully completing certain parts of the exercise. + */ + partially_correct_feedback?: string; + /** + * Search queries that were used to build the exercise. + */ + search_values: string; + /** + * Name of the person who wrote the base text for the exercise. + */ + work_author?: string; + /** + * Title of the base text for the exercise. + */ + work_title?: string; + /** + * Type of exercise, concerning interaction and layout. + */ + type: string; + /** + * Localized expression of the exercise type. + */ + type_translation?: string; + /** + * CTS URN for the text passage from which the exercise was created. + */ + urn?: string; +} + diff --git a/mc_frontend/openapi/model/frequencyItem.ts b/mc_frontend/openapi/model/frequencyItem.ts new file mode 100644 index 0000000..f91e07d --- /dev/null +++ b/mc_frontend/openapi/model/frequencyItem.ts @@ -0,0 +1,28 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface FrequencyItem { + /** + * How often the given combination of values occurred. + */ + count?: number; + /** + * Labels for the phenomena described in this frequency entry. + */ + phenomena?: Array<string>; + /** + * Values for the phenomena described in this frequency entry. + */ + values?: Array<string>; +} + diff --git a/mc_frontend/openapi/model/graphData.ts b/mc_frontend/openapi/model/graphData.ts new file mode 100644 index 0000000..32b8076 --- /dev/null +++ b/mc_frontend/openapi/model/graphData.ts @@ -0,0 +1,41 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +import { NodeMC } from './nodeMC'; +import { Link } from './link'; + + +/** + * Nodes, edges and metadata for a graph. + */ +export interface GraphData { + /** + * Whether edges in the returned graph are directed. + */ + directed?: boolean; + /** + * Additional graph data. + */ + graph?: object; + /** + * List of edges for the graph. + */ + links: Array<Link>; + /** + * Whether the graph consists of multiple subgraphs. + */ + multigraph?: boolean; + /** + * List of nodes for the graph. + */ + nodes: Array<NodeMC>; +} + diff --git a/mc_frontend/openapi/model/learningResult.ts b/mc_frontend/openapi/model/learningResult.ts new file mode 100644 index 0000000..e6a1cde --- /dev/null +++ b/mc_frontend/openapi/model/learningResult.ts @@ -0,0 +1,107 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * Learner data for completed exercises. + */ +export interface LearningResult { + /** + * H5P user ID, usually unique per device. + */ + actor_account_name?: string; + /** + * Describes the kind of object that was recognized as actor. + */ + actor_object_type?: string; + /** + * Link to the exercise type specification. + */ + category_id?: string; + /** + * Describes the kind of object that was recognized as exercise. + */ + category_object_type?: string; + /** + * JSON string containing a list of possible choices, each with ID and description. + */ + choices?: string; + /** + * Whether the exercise was fully processed or not. + */ + completion: boolean; + /** + * JSON string containing a list of possible solutions to the exercise, given as patterns of answers. + */ + correct_responses_pattern: string; + /** + * When the learner data was received (POSIX timestamp). + */ + created_time: number; + /** + * How many seconds it took a learner to complete the exercise. + */ + duration?: string; + /** + * JSON string containing a mapping of keys and values (usually the local content ID, i.e. a versioning mechanism). + */ + extensions?: string; + /** + * Exercise type. + */ + interaction_type?: string; + /** + * Exercise content, possibly including instructions. + */ + object_definition_description: string; + /** + * Type of object definition that is presented to the user. + */ + object_definition_type?: string; + /** + * Type of object that is presented to the user. + */ + object_object_type?: string; + /** + * Answer provided by the user, possibly as a pattern. + */ + response: string; + /** + * Maximum possible score to be achieved in this exercise. + */ + score_max: number; + /** + * Minimum score to be achieved in this exercise. + */ + score_min: number; + /** + * Score that was actually achieved by the user in this exercise. + */ + score_raw: number; + /** + * Relative score (between 0 and 1) that was actually achieved by the user in this exercise. + */ + score_scaled?: number; + /** + * Whether the exercise was successfully completed or not. + */ + success: boolean; + /** + * Type of action that was performed by the user. + */ + verb_display?: string; + /** + * Link to the type of action that was performed by the user. + */ + verb_id?: string; +} + diff --git a/mc_frontend/openapi/model/link.ts b/mc_frontend/openapi/model/link.ts new file mode 100644 index 0000000..fa66988 --- /dev/null +++ b/mc_frontend/openapi/model/link.ts @@ -0,0 +1,36 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface Link { + /** + * Component name as given by ANNIS. + */ + annis_component_name?: string; + /** + * Component type as given by ANNIS. + */ + annis_component_type?: string; + /** + * ID of the source node for the edge. + */ + source?: string; + /** + * ID of the target node for the edge. + */ + target?: string; + /** + * Dependency relation described by the edge. + */ + udep_deprel?: string; +} + diff --git a/mc_frontend/openapi/model/models.ts b/mc_frontend/openapi/model/models.ts new file mode 100644 index 0000000..915ffde --- /dev/null +++ b/mc_frontend/openapi/model/models.ts @@ -0,0 +1,16 @@ +export * from './annisResponse'; +export * from './corpus'; +export * from './exercise'; +export * from './exerciseAllOf'; +export * from './exerciseBase'; +export * from './exerciseForm'; +export * from './exerciseFormAllOf'; +export * from './frequencyItem'; +export * from './graphData'; +export * from './learningResult'; +export * from './link'; +export * from './nodeMC'; +export * from './solution'; +export * from './solutionElement'; +export * from './textComplexity'; +export * from './updateInfo'; diff --git a/mc_frontend/openapi/model/node.ts b/mc_frontend/openapi/model/node.ts new file mode 100644 index 0000000..894599f --- /dev/null +++ b/mc_frontend/openapi/model/node.ts @@ -0,0 +1,60 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface Node { + /** + * Node name as given by ANNIS. + */ + annis_node_name: string; + /** + * Node type as given by ANNIS. + */ + annis_node_type: string; + /** + * Raw word form as given by ANNIS. + */ + annis_tok: string; + /** + * Node type as given by ANNIS (?). + */ + annis_type: string; + /** + * Unique identifier for the node in the SALT model. + */ + id: string; + /** + * Whether the raw word form is missing in a given vocabulary. + */ + is_oov?: boolean; + /** + * Lemmatized word form. + */ + udep_lemma: string; + /** + * Universal part of speech tag for the word form. + */ + udep_upostag: string; + /** + * Language-specific part of speech tag for the word form. + */ + udep_xpostag?: string; + /** + * Additional morphological information. + */ + udep_feats?: string; + /** + * Solution value for this node in an exercise. + */ + solution?: string; +} + diff --git a/mc_frontend/openapi/model/nodeMC.ts b/mc_frontend/openapi/model/nodeMC.ts new file mode 100644 index 0000000..a2d05dd --- /dev/null +++ b/mc_frontend/openapi/model/nodeMC.ts @@ -0,0 +1,60 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface NodeMC { + /** + * Node name as given by ANNIS. + */ + annis_node_name?: string; + /** + * Node type as given by ANNIS. + */ + annis_node_type?: string; + /** + * Raw word form as given by ANNIS. + */ + annis_tok?: string; + /** + * Node type as given by ANNIS (?). + */ + annis_type?: string; + /** + * Unique identifier for the node in the SALT model. + */ + id?: string; + /** + * Whether the raw word form is missing in a given vocabulary. + */ + is_oov?: boolean; + /** + * Lemmatized word form. + */ + udep_lemma?: string; + /** + * Universal part of speech tag for the word form. + */ + udep_upostag?: string; + /** + * Language-specific part of speech tag for the word form. + */ + udep_xpostag?: string; + /** + * Additional morphological information. + */ + udep_feats?: string; + /** + * Solution value for this node in an exercise. + */ + solution?: string; +} + diff --git a/mc_frontend/openapi/model/solution.ts b/mc_frontend/openapi/model/solution.ts new file mode 100644 index 0000000..31a5799 --- /dev/null +++ b/mc_frontend/openapi/model/solution.ts @@ -0,0 +1,22 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +import { SolutionElement } from './solutionElement'; + + +/** + * Correct solution for an exercise. + */ +export interface Solution { + target?: SolutionElement; + value?: SolutionElement; +} + diff --git a/mc_frontend/openapi/model/solutionElement.ts b/mc_frontend/openapi/model/solutionElement.ts new file mode 100644 index 0000000..4adb2da --- /dev/null +++ b/mc_frontend/openapi/model/solutionElement.ts @@ -0,0 +1,35 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * Target or value of a correct solution for an exercise. + */ +export interface SolutionElement { + /** + * Content of the solution element. + */ + content?: string; + /** + * Unique identifier for the node in the SALT model. + */ + salt_id?: string; + /** + * Unique identifier for the sentence in a corpus. + */ + sentence_id: number; + /** + * Unique identifier for the token in a sentence. + */ + token_id: number; +} + diff --git a/mc_frontend/openapi/model/textComplexity.ts b/mc_frontend/openapi/model/textComplexity.ts new file mode 100644 index 0000000..8633455 --- /dev/null +++ b/mc_frontend/openapi/model/textComplexity.ts @@ -0,0 +1,79 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * Mapping of various elements of text complexity to their corresponding values. + */ +export interface TextComplexity { + /** + * Overall text complexity of the given corpus. + */ + all?: number; + /** + * Average length of a word in the given corpus. + */ + avg_w_len?: number; + /** + * Average number of words per sentence. + */ + avg_w_per_sent?: number; + /** + * Lexical density of the given corpus. + */ + lex_den?: number; + /** + * Number of ablativi absoluti in the given corpus. + */ + n_abl_abs?: number; + /** + * Number of clauses in the given corpus. + */ + n_clause?: number; + /** + * Number of gerunds in the given corpus. + */ + n_gerund?: number; + /** + * Number of infinitives in the given corpus. + */ + n_inf?: number; + /** + * Number of participles in the given corpus. + */ + n_part?: number; + /** + * Number of punctuation signs in the given corpus. + */ + n_punct?: number; + /** + * Number of sentences in the given corpus. + */ + n_sent?: number; + /** + * Number of subclauses in the given corpus. + */ + n_subclause?: number; + /** + * Number of distinct word forms in the given corpus. + */ + n_types?: number; + /** + * Number of words in the given corpus. + */ + n_w?: number; + /** + * Number of distinct part of speech tags in the given corpus. + */ + pos?: number; +} + diff --git a/mc_frontend/openapi/model/updateInfo.ts b/mc_frontend/openapi/model/updateInfo.ts new file mode 100644 index 0000000..61ac427 --- /dev/null +++ b/mc_frontend/openapi/model/updateInfo.ts @@ -0,0 +1,40 @@ +/** + * Machina Callida Backend REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * Timestamps for updates of various resources. + */ +export interface UpdateInfo { + /** + * When the resource was created (as POSIX timestamp). + */ + created_time: number; + /** + * When the resource was last modified (as POSIX timestamp). + */ + last_modified_time: number; + /** + * Name of the resource for which update timestamps are indexed. + */ + resource_type: UpdateInfo.ResourceTypeEnum; +} +export namespace UpdateInfo { + export type ResourceTypeEnum = 'cts_data' | 'exercise_list' | 'file_api_clean'; + export const ResourceTypeEnum = { + CtsData: 'cts_data' as ResourceTypeEnum, + ExerciseList: 'exercise_list' as ResourceTypeEnum, + FileApiClean: 'file_api_clean' as ResourceTypeEnum + }; +} + + diff --git a/mc_frontend/openapi/variables.ts b/mc_frontend/openapi/variables.ts new file mode 100644 index 0000000..6fe5854 --- /dev/null +++ b/mc_frontend/openapi/variables.ts @@ -0,0 +1,9 @@ +import { InjectionToken } from '@angular/core'; + +export const BASE_PATH = new InjectionToken<string>('basePath'); +export const COLLECTION_FORMATS = { + 'csv': ',', + 'tsv': ' ', + 'ssv': ' ', + 'pipes': '|' +} diff --git a/mc_frontend/src/app/corpus.service.spec.ts b/mc_frontend/src/app/corpus.service.spec.ts index 665a02c..0de90a5 100644 --- a/mc_frontend/src/app/corpus.service.spec.ts +++ b/mc_frontend/src/app/corpus.service.spec.ts @@ -10,21 +10,18 @@ import {HttpClient, HttpErrorResponse} from '@angular/common/http'; import {HelperService} from './helper.service'; import MockMC from './models/mockMC'; import {CaseValue, DependencyValue, ExerciseType, PartOfSpeechValue, Phenomenon} from './models/enum'; -import {AnnisResponse} from './models/annisResponse'; import {ApplicationState} from './models/applicationState'; import {QueryMC} from './models/queryMC'; import {PhenomenonMapContent} from './models/phenomenonMap'; -import {FrequencyItem} from './models/frequencyItem'; import {UpdateInfo} from './models/updateInfo'; import configMC from '../configMC'; import {TextData} from './models/textData'; import {CorpusMC} from './models/corpusMC'; -import {LinkMC} from './models/linkMC'; -import {NodeMC} from './models/nodeMC'; import {Author} from './models/author'; import {take} from 'rxjs/operators'; import {TextRange} from './models/textRange'; import Spy = jasmine.Spy; +import {AnnisResponse, NodeMC} from '../../openapi'; describe('CorpusService', () => { let httpClient: HttpClient; @@ -62,7 +59,7 @@ describe('CorpusService', () => { spyOn(corpusService, 'getSortedQueryValues').and.returnValue([PartOfSpeechValue.adjective.toString()]); helperService.applicationState.next(helperService.deepCopy(MockMC.applicationState) as ApplicationState); corpusService.exercise.type = ExerciseType.matching; - corpusService.annisResponse = new AnnisResponse({frequency_analysis: []}); + corpusService.annisResponse = {frequency_analysis: []}; corpusService.adjustTranslations().then(() => { expect(corpusService.exercise.queryItems.length).toBe(2); corpusService.exercise.type = ExerciseType.cloze; @@ -146,7 +143,7 @@ describe('CorpusService', () => { spy.and.returnValue(Promise.resolve( (helperService.deepCopy(MockMC.applicationState) as ApplicationState).mostRecentSetup.annisResponse)); corpusService.getCTStextPassage('').then((ar: AnnisResponse) => { - expect(ar.nodes.length).toBe(1); + expect(ar.graph_data.nodes.length).toBe(1); done(); }); }); @@ -167,7 +164,7 @@ describe('CorpusService', () => { it('should get a frequency analysis', (done) => { const spy: Spy = spyOn(helperService, 'makeGetRequest').and.callFake(() => Promise.reject()); - corpusService.annisResponse = new AnnisResponse({frequency_analysis: []}); + corpusService.annisResponse = {frequency_analysis: []}; corpusService.getFrequencyAnalysis().then(() => { }, () => { expect(spy).toHaveBeenCalledTimes(1); @@ -193,18 +190,18 @@ describe('CorpusService', () => { result = corpusService.getSortedQueryValues(query, 0); expect(result.length).toBe(3); corpusService.exercise.type = ExerciseType.matching; - corpusService.annisResponse = new AnnisResponse({ - frequency_analysis: [new FrequencyItem({ + corpusService.annisResponse = { + frequency_analysis: [{ values: [PartOfSpeechValue.adjective.toString(), 'a'], phenomena: ['', Phenomenon.partOfSpeech.toString()] - }), new FrequencyItem({ + }, { values: [PartOfSpeechValue.adjective.toString(), 'b'], phenomena: ['', Phenomenon.partOfSpeech.toString()] - }), new FrequencyItem({ + }, { values: [PartOfSpeechValue.adjective.toString(), 'c'], phenomena: ['', Phenomenon.partOfSpeech.toString()] - })] - }); + }] + }; result = corpusService.getSortedQueryValues(query, 1); expect(result.length).toBe(3); corpusService.annisResponse.frequency_analysis.forEach(fi => fi.phenomena = [Phenomenon.partOfSpeech.toString()]); @@ -300,13 +297,15 @@ describe('CorpusService', () => { }); it('should process an ANNIS response', () => { - const ar: AnnisResponse = new AnnisResponse({ - links: [new LinkMC({annis_component_type: 'Pointing', udep_deprel: 'nsubj'})], - nodes: [new NodeMC({annis_tok: 'tok .'})] - }); + const ar: AnnisResponse = { + graph_data: { + links: [{annis_component_type: 'Pointing', udep_deprel: 'nsubj'}], + nodes: [{annis_tok: 'tok .'}] + } + }; corpusService.processAnnisResponse(ar); expect(corpusService.phenomenonMap.dependency.specificValues[DependencyValue.subject]).toBe(1); - ar.links.push(ar.links[0]); + ar.graph_data.links.push(ar.graph_data.links[0]); corpusService.processAnnisResponse(ar); expect(corpusService.phenomenonMap.dependency.specificValues[DependencyValue.subject]).toBe(2); }); @@ -327,12 +326,12 @@ describe('CorpusService', () => { }); it('should process nodes', () => { - const node: NodeMC = new NodeMC({ + const node: NodeMC = { udep_lemma: 'lemma', udep_upostag: 'NOUN', udep_feats: `${Phenomenon.case.toString()}=Nom` - }); - const ar: AnnisResponse = new AnnisResponse({nodes: [node, new NodeMC({...node})]}); + }; + const ar: AnnisResponse = {graph_data: {nodes: [node, {...node}], links: []}}; corpusService.phenomenonMap.lemma = new PhenomenonMapContent({specificValues: {}, translationValues: {}}); corpusService.phenomenonMap.case = new PhenomenonMapContent({specificValues: {}, translationValues: {}}); corpusService.phenomenonMap.partOfSpeech = new PhenomenonMapContent({ @@ -355,7 +354,7 @@ describe('CorpusService', () => { getTextSpy.and.returnValue(Promise.resolve()); helperService.applicationState.next(new ApplicationState({ mostRecentSetup: new TextData({ - annisResponse: new AnnisResponse({nodes: []}) + annisResponse: {graph_data: {nodes: [], links: []}} }) })); corpusService.restoreLastCorpus().then(() => { @@ -365,7 +364,7 @@ describe('CorpusService', () => { expect(getTextSpy).toHaveBeenCalledTimes(2); helperService.applicationState.next(helperService.deepCopy(MockMC.applicationState) as ApplicationState); corpusService.restoreLastCorpus().then(() => { - expect(corpusService.annisResponse.nodes.length).toBe(1); + expect(corpusService.annisResponse.graph_data.nodes.length).toBe(1); done(); }); }); @@ -401,9 +400,9 @@ describe('CorpusService', () => { it('should update the base word', () => { const adjustSpy: Spy = spyOn(corpusService, 'adjustQueryValue'); const queryValuesSpy: Spy = spyOn(corpusService, 'getSortedQueryValues').and.returnValue([]); - corpusService.annisResponse = new AnnisResponse({ + corpusService.annisResponse = { frequency_analysis: helperService.deepCopy(MockMC.apiResponseFrequencyAnalysisGet) - }); + }; corpusService.annisResponse.frequency_analysis[0].phenomena.push(Phenomenon.case.toString()); corpusService.exercise.type = ExerciseType.matching; corpusService.exercise.queryItems.push(new QueryMC()); diff --git a/mc_frontend/src/app/corpus.service.ts b/mc_frontend/src/app/corpus.service.ts index 0bd251c..7d92ea2 100644 --- a/mc_frontend/src/app/corpus.service.ts +++ b/mc_frontend/src/app/corpus.service.ts @@ -7,7 +7,6 @@ import {HttpClient, HttpErrorResponse, HttpParams} from '@angular/common/http'; import {TranslateService} from '@ngx-translate/core'; import {ToastController} from '@ionic/angular'; import {HelperService} from 'src/app/helper.service'; -import {AnnisResponse} from 'src/app/models/annisResponse'; import { CaseTranslations, CaseValue, @@ -20,13 +19,10 @@ import { PartOfSpeechValue, Phenomenon } from 'src/app/models/enum'; -import {NodeMC} from 'src/app/models/nodeMC'; -import {LinkMC} from 'src/app/models/linkMC'; import {QueryMC} from 'src/app/models/queryMC'; import {Exercise} from 'src/app/models/exercise'; import {Feedback} from 'src/app/models/feedback'; import {PhenomenonMap, PhenomenonMapContent} from 'src/app/models/phenomenonMap'; -import {FrequencyItem} from 'src/app/models/frequencyItem'; import {ReplaySubject} from 'rxjs'; import {ApplicationState} from './models/applicationState'; import {take} from 'rxjs/operators'; @@ -34,6 +30,7 @@ import {TextData} from './models/textData'; import {Storage} from '@ionic/storage'; import {UpdateInfo} from './models/updateInfo'; import configMC from '../configMC'; +import {AnnisResponse, FrequencyItem, Link, NodeMC} from '../../openapi'; @Injectable({ providedIn: 'root' @@ -409,8 +406,8 @@ export class CorpusService { }); this.phenomenonMap.lemma.translationValues = {}; this.processNodes(ar); - const pointingLinks: LinkMC[] = ar.links.filter(x => x.annis_component_type === 'Pointing'); - pointingLinks.forEach((link: LinkMC) => { + const pointingLinks: Link[] = ar.graph_data.links.filter(x => x.annis_component_type === 'Pointing'); + pointingLinks.forEach((link: Link) => { const dep: DependencyValue = this.helperService.dependencyMap[link.udep_deprel]; if (dep) { const existingValue = this.phenomenonMap.dependency.specificValues[dep]; @@ -418,13 +415,13 @@ export class CorpusService { } }); // need to add root dependencies manually because they are tricky to handle - const nodeIds: string[] = ar.nodes.map(x => x.id); + const nodeIds: string[] = ar.graph_data.nodes.map(x => x.id); const nodesWithDependencySet: Set<string> = new Set<string>(pointingLinks.map(x => x.target)); const rootNodeIds: string[] = nodeIds.filter(x => !nodesWithDependencySet.has(x)); this.phenomenonMap.dependency.specificValues[DependencyValue.root] = rootNodeIds.length; this.adjustQueryValue(this.exercise.queryItems[0], 0); // remove whitespace before punctuation - this.currentText = ar.nodes.map(x => x.annis_tok).join(' ').replace(/[ ]([.,\/#!$%\^&\*;:{}=\-_`~()])/g, (x: string) => { + this.currentText = ar.graph_data.nodes.map(x => x.annis_tok).join(' ').replace(/[ ]([.,\/#!$%\^&\*;:{}=\-_`~()])/g, (x: string) => { return x.trim(); }); this.annisResponse = ar; @@ -480,7 +477,7 @@ export class CorpusService { } processNodes(ar: AnnisResponse): void { - ar.nodes.forEach((node: NodeMC) => { + ar.graph_data.nodes.forEach((node: NodeMC) => { let existingValue = this.phenomenonMap.lemma.specificValues[node.udep_lemma]; this.phenomenonMap.lemma.specificValues[node.udep_lemma] = (existingValue ? existingValue : 0) + 1; this.phenomenonMap.lemma.translationValues[node.udep_lemma] = node.udep_lemma; @@ -508,14 +505,15 @@ export class CorpusService { this.currentCorpusCache = state.mostRecentSetup.currentCorpus; this.currentTextRangeCache = state.mostRecentSetup.currentTextRange; this.isTextRangeCorrect = true; - if (this.annisResponse && this.annisResponse.nodes.length) { + if (this.annisResponse && this.annisResponse.graph_data.nodes.length) { this.processAnnisResponse(this.annisResponse, false); return resolve(); } else if (this.currentText) { // check if the data is already present return resolve(); } else { - const saveToCache: boolean = !state.mostRecentSetup.annisResponse || !state.mostRecentSetup.annisResponse.nodes.length; + const saveToCache: boolean = !state.mostRecentSetup.annisResponse || + !state.mostRecentSetup.annisResponse.graph_data.nodes.length; this.getText(saveToCache).then(() => { return resolve(); }, () => { diff --git a/mc_frontend/src/app/exercise-list/exercise-list.page.spec.ts b/mc_frontend/src/app/exercise-list/exercise-list.page.spec.ts index 6fd6c06..5f65ed1 100644 --- a/mc_frontend/src/app/exercise-list/exercise-list.page.spec.ts +++ b/mc_frontend/src/app/exercise-list/exercise-list.page.spec.ts @@ -13,7 +13,6 @@ import {ExerciseType, MoodleExerciseType, SortingCategory, VocabularyCorpus} fro import MockMC from '../models/mockMC'; import {ApplicationState} from '../models/applicationState'; import {ExerciseMC} from '../models/exerciseMC'; -import {AnnisResponse} from '../models/annisResponse'; import Spy = jasmine.Spy; import configMC from '../../configMC'; import {UpdateInfo} from '../models/updateInfo'; @@ -100,7 +99,7 @@ describe('ExerciseListPage', () => { const requestSpy: Spy = spyOn(exerciseListPage.helperService, 'makeGetRequest').and.callFake(() => Promise.reject()); spyOn(exerciseListPage.helperService, 'goToPreviewPage').and.returnValue(Promise.resolve(true)); exerciseListPage.showExercise(new ExerciseMC()).then(() => { - requestSpy.and.returnValue(Promise.resolve(new AnnisResponse())); + requestSpy.and.returnValue(Promise.resolve({})); exerciseListPage.showExercise(new ExerciseMC({exercise_type: MoodleExerciseType.markWords.toString()})).then(() => { expect(exerciseListPage.corpusService.exercise.type).toBe(ExerciseType.markWords); done(); diff --git a/mc_frontend/src/app/exercise-list/exercise-list.page.ts b/mc_frontend/src/app/exercise-list/exercise-list.page.ts index a26e10c..ce55450 100644 --- a/mc_frontend/src/app/exercise-list/exercise-list.page.ts +++ b/mc_frontend/src/app/exercise-list/exercise-list.page.ts @@ -13,7 +13,6 @@ import { VocabularyCorpusTranslation } from '../models/enum'; import {TranslateService} from '@ngx-translate/core'; -import {AnnisResponse} from 'src/app/models/annisResponse'; import {CorpusService} from 'src/app/corpus.service'; import {VocabularyService} from 'src/app/vocabulary.service'; import {Storage} from '@ionic/storage'; @@ -21,6 +20,7 @@ import configMC from '../../configMC'; import {UpdateInfo} from '../models/updateInfo'; import {take} from 'rxjs/operators'; import {ApplicationState} from '../models/applicationState'; +import {AnnisResponse} from '../../../openapi'; @Component({ selector: 'app-exercise-list', diff --git a/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.spec.ts b/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.spec.ts index 0a3ff0c..2b65252 100644 --- a/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.spec.ts +++ b/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.spec.ts @@ -8,7 +8,6 @@ import {RouterModule} from '@angular/router'; import {TranslateTestingModule} from '../translate-testing/translate-testing.module'; import {FormsModule} from '@angular/forms'; import {APP_BASE_HREF} from '@angular/common'; -import {AnnisResponse} from '../models/annisResponse'; import {TextRange} from '../models/textRange'; import {ReplaySubject} from 'rxjs'; import configMC from '../../configMC'; @@ -16,7 +15,6 @@ import {ExerciseType, PartOfSpeechValue, Phenomenon} from '../models/enum'; import {ToastController} from '@ionic/angular'; import {QueryMC} from '../models/queryMC'; import {PhenomenonMapContent} from '../models/phenomenonMap'; -import {FrequencyItem} from '../models/frequencyItem'; import Spy = jasmine.Spy; import MockMC from '../models/mockMC'; @@ -54,7 +52,7 @@ describe('ExerciseParametersPage', () => { }); it('should generate an exercise', (done) => { - exerciseParametersPage.corpusService.annisResponse = new AnnisResponse({solutions: []}); + exerciseParametersPage.corpusService.annisResponse = {solutions: []}; exerciseParametersPage.corpusService.initCurrentCorpus().then(() => { exerciseParametersPage.corpusService.currentTextRange = new ReplaySubject<TextRange>(1); exerciseParametersPage.corpusService.currentTextRange.next(new TextRange({start: [], end: []})); @@ -89,16 +87,16 @@ describe('ExerciseParametersPage', () => { let displayValue: string = exerciseParametersPage.getDisplayValue(new QueryMC({phenomenon: Phenomenon.lemma}), key); expect(displayValue.length).toBe(9); exerciseParametersPage.corpusService.exercise.type = ExerciseType.matching; - exerciseParametersPage.corpusService.annisResponse = new AnnisResponse({ - frequency_analysis: [new FrequencyItem({values: [PartOfSpeechValue.adjective.toString(), key], count: 10})] - }); + exerciseParametersPage.corpusService.annisResponse = { + frequency_analysis: [{values: [PartOfSpeechValue.adjective.toString(), key], count: 10}] + }; displayValue = exerciseParametersPage.getDisplayValue(new QueryMC({phenomenon: Phenomenon.lemma}), key, 1); expect(displayValue.length).toBe(10); - exerciseParametersPage.corpusService.annisResponse.frequency_analysis[0] = new FrequencyItem({ + exerciseParametersPage.corpusService.annisResponse.frequency_analysis[0] = { phenomena: [Phenomenon.lemma.toString()], values: [key], count: 100 - }); + }; exerciseParametersPage.corpusService.annisResponse.frequency_analysis.push( exerciseParametersPage.corpusService.annisResponse.frequency_analysis[0]); displayValue = exerciseParametersPage.getDisplayValue(new QueryMC({phenomenon: Phenomenon.lemma}), key, 0); @@ -125,10 +123,9 @@ describe('ExerciseParametersPage', () => { }); it('should get a H5P exercise', (done) => { - const requestSpy: Spy = spyOn(exerciseParametersPage.helperService, 'makePostRequest').and.returnValue( - Promise.resolve(new AnnisResponse())); + const requestSpy: Spy = spyOn(exerciseParametersPage.helperService, 'makePostRequest').and.returnValue(Promise.resolve({})); const navSpy: Spy = spyOn(exerciseParametersPage.helperService, 'goToPreviewPage').and.returnValue(Promise.resolve(true)); - exerciseParametersPage.corpusService.annisResponse = new AnnisResponse(); + exerciseParametersPage.corpusService.annisResponse = {}; exerciseParametersPage.helperService.applicationState.next(exerciseParametersPage.helperService.deepCopy(MockMC.applicationState)); exerciseParametersPage.getH5Pexercise(new FormData()).then(() => { expect(navSpy).toHaveBeenCalledTimes(1); diff --git a/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.ts b/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.ts index 8ec79a8..07910a1 100644 --- a/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.ts +++ b/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.ts @@ -6,7 +6,6 @@ import { Phenomenon, PhenomenonTranslation } from '../models/enum'; -import {AnnisResponse} from 'src/app/models/annisResponse'; import {NavController, ToastController} from '@ionic/angular'; import {HttpClient} from '@angular/common/http'; import {Component, OnInit} from '@angular/core'; @@ -16,12 +15,12 @@ import {HelperService} from 'src/app/helper.service'; import {CorpusService} from 'src/app/corpus.service'; import {QueryMC} from 'src/app/models/queryMC'; import {PhenomenonMapContent} from 'src/app/models/phenomenonMap'; -import {FrequencyItem} from 'src/app/models/frequencyItem'; import {CorpusMC} from '../models/corpusMC'; import {ApplicationState} from '../models/applicationState'; import {take} from 'rxjs/operators'; import {TextRange} from '../models/textRange'; import configMC from '../../configMC'; +import {AnnisResponse, FrequencyItem} from '../../../openapi'; @Component({ selector: 'app-exercise-parameters', diff --git a/mc_frontend/src/app/exercise/exercise.page.spec.ts b/mc_frontend/src/app/exercise/exercise.page.spec.ts index 0133b0b..2bacd44 100644 --- a/mc_frontend/src/app/exercise/exercise.page.spec.ts +++ b/mc_frontend/src/app/exercise/exercise.page.spec.ts @@ -7,7 +7,6 @@ import {ActivatedRoute, RouterModule} from '@angular/router'; import {TranslateTestingModule} from '../translate-testing/translate-testing.module'; import {APP_BASE_HREF} from '@angular/common'; import {of} from 'rxjs'; -import {AnnisResponse} from '../models/annisResponse'; import {ExerciseType, MoodleExerciseType} from '../models/enum'; import Spy = jasmine.Spy; import configMC from '../../configMC'; @@ -37,14 +36,17 @@ describe('ExercisePage', () => { schemas: [CUSTOM_ELEMENTS_SCHEMA], }) .compileComponents().then(); + })); + + beforeEach(() => { fixture = TestBed.createComponent(ExercisePage); exercisePage = fixture.componentInstance; h5pSpy = spyOn(exercisePage.exerciseService, 'initH5P').and.returnValue(Promise.resolve()); checkSpy = spyOn(exercisePage.corpusService, 'checkAnnisResponse').and.callFake(() => Promise.reject()); getRequestSpy = spyOn(exercisePage.helperService, 'makeGetRequest').and.returnValue(Promise.resolve( - new AnnisResponse({exercise_type: MoodleExerciseType.cloze.toString()}))); + {exercise_type: MoodleExerciseType.cloze.toString()})); fixture.detectChanges(); - })); + }); it('should create', () => { expect(exercisePage).toBeTruthy(); @@ -66,7 +68,7 @@ describe('ExercisePage', () => { exercisePage.helperService.applicationState.next(exercisePage.helperService.deepCopy(MockMC.applicationState)); exercisePage.loadExercise().then(() => { expect(exercisePage.corpusService.exercise.type).toBe(ExerciseType.cloze); - getRequestSpy.and.returnValue(Promise.resolve(new AnnisResponse({exercise_type: MoodleExerciseType.markWords.toString()}))); + getRequestSpy.and.returnValue(Promise.resolve({exercise_type: MoodleExerciseType.markWords.toString()})); exercisePage.loadExercise().then(() => { expect(h5pSpy).toHaveBeenCalledWith(configMC.excerciseTypePathMarkWords); getRequestSpy.and.callFake(() => Promise.reject()); diff --git a/mc_frontend/src/app/exercise/exercise.page.ts b/mc_frontend/src/app/exercise/exercise.page.ts index 141d4dd..f8c18e4 100644 --- a/mc_frontend/src/app/exercise/exercise.page.ts +++ b/mc_frontend/src/app/exercise/exercise.page.ts @@ -6,13 +6,13 @@ import {ActivatedRoute} from '@angular/router'; import {TranslateService} from '@ngx-translate/core'; import {ExerciseService} from 'src/app/exercise.service'; import {HttpClient, HttpParams} from '@angular/common/http'; -import {AnnisResponse} from 'src/app/models/annisResponse'; import {ExerciseType, MoodleExerciseType} from 'src/app/models/enum'; import {CorpusService} from 'src/app/corpus.service'; import {ApplicationState} from '../models/applicationState'; import {take} from 'rxjs/operators'; import configMC from '../../configMC'; import {Storage} from '@ionic/storage'; +import {AnnisResponse} from '../../../openapi'; @Component({ selector: 'app-exercise', diff --git a/mc_frontend/src/app/helper.service.spec.ts b/mc_frontend/src/app/helper.service.spec.ts index 4fc17a5..f5edab8 100644 --- a/mc_frontend/src/app/helper.service.spec.ts +++ b/mc_frontend/src/app/helper.service.spec.ts @@ -198,7 +198,7 @@ describe('HelperService', () => { helperService.saveApplicationState(helperService.deepCopy(MockMC.applicationState)).then(() => { helperService.storage.get(configMC.localStorageKeyApplicationState).then((jsonString: string) => { const state: ApplicationState = JSON.parse(jsonString) as ApplicationState; - expect(state.mostRecentSetup.annisResponse.nodes.length).toBe(1); + expect(state.mostRecentSetup.annisResponse.graph_data.nodes.length).toBe(1); done(); }); }); diff --git a/mc_frontend/src/app/helper.service.ts b/mc_frontend/src/app/helper.service.ts index f1d3ebd..52040e3 100644 --- a/mc_frontend/src/app/helper.service.ts +++ b/mc_frontend/src/app/helper.service.ts @@ -11,6 +11,7 @@ import {Language} from 'src/app/models/language'; import {ReplaySubject} from 'rxjs'; import {TextData} from './models/textData'; import configMC from '../configMC'; +import {GraphData} from '../../openapi/model/graphData'; @Injectable({ providedIn: 'root' @@ -281,7 +282,8 @@ export class HelperService { exerciseList: [] }); if (jsonString) { - const state: ApplicationState = JSON.parse(jsonString) as ApplicationState; + const jsonObject: any = this.updateAnnisResponse(jsonString); + const state: ApplicationState = jsonObject as ApplicationState; state.exerciseList = state.exerciseList ? state.exerciseList : []; this.applicationStateCache = state; } @@ -364,4 +366,21 @@ export class HelperService { position }).then((toast: HTMLIonToastElement) => toast.present()); } + + updateAnnisResponse(jsonString: string): any { + const jsonObject: any = JSON.parse(jsonString); + // backwards compatibility + [jsonObject.currentSetup, jsonObject.mostRecentSetup].forEach((textdata: any) => { + if (textdata && textdata.annisResponse && !textdata.annisResponse.graph_data) { + const annisResp: any = textdata.annisResponse; + const props: string[] = ['links', 'nodes', 'directed', 'graph', 'multigraph']; + annisResp.graph_data = {}; + props.forEach((prop: string) => { + annisResp.graph_data[prop] = annisResp[prop]; + delete annisResp[prop]; + }); + } + }); + return jsonObject; + } } diff --git a/mc_frontend/src/app/models/annisResponse.ts b/mc_frontend/src/app/models/annisResponse.ts deleted file mode 100644 index 8877b41..0000000 --- a/mc_frontend/src/app/models/annisResponse.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* tslint:disable:variable-name */ -import {Solution} from 'src/app/models/solution'; -import {LinkMC} from 'src/app/models/linkMC'; -import {NodeMC} from 'src/app/models/nodeMC'; -import {FrequencyItem} from 'src/app/models/frequencyItem'; -import {TextComplexity} from 'src/app/models/textComplexity'; - -export class AnnisResponse { - public directed: boolean; - public exercise_id: string; - public exercise_type: string; - public frequency_analysis: FrequencyItem[]; - public links: LinkMC[]; - public multigraph: boolean; - public nodes: NodeMC[]; - public solutions: Solution[]; - public text_complexity: TextComplexity; - public uri: string; - - constructor(init?: Partial<AnnisResponse>) { - Object.assign(this, init); - } -} diff --git a/mc_frontend/src/app/models/exerciseMC.ts b/mc_frontend/src/app/models/exerciseMC.ts index 9c3fc43..f69b749 100644 --- a/mc_frontend/src/app/models/exerciseMC.ts +++ b/mc_frontend/src/app/models/exerciseMC.ts @@ -1,5 +1,6 @@ /* tslint:disable:variable-name */ -import {Solution} from 'src/app/models/solution'; + +import {Solution} from '../../../openapi'; export class ExerciseMC { public conll: string; diff --git a/mc_frontend/src/app/models/frequencyItem.ts b/mc_frontend/src/app/models/frequencyItem.ts deleted file mode 100644 index 9ec4f32..0000000 --- a/mc_frontend/src/app/models/frequencyItem.ts +++ /dev/null @@ -1,10 +0,0 @@ -export class FrequencyItem { - public values: string[]; - public phenomena: string[]; - public count: number; - - constructor(init?: Partial<FrequencyItem>) { - Object.assign(this, init); - } -} - diff --git a/mc_frontend/src/app/models/linkMC.ts b/mc_frontend/src/app/models/linkMC.ts deleted file mode 100644 index 6a7f74d..0000000 --- a/mc_frontend/src/app/models/linkMC.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* tslint:disable:variable-name */ -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); - } -} diff --git a/mc_frontend/src/app/models/mockMC.ts b/mc_frontend/src/app/models/mockMC.ts index 94d0c26..7d9a5ab 100644 --- a/mc_frontend/src/app/models/mockMC.ts +++ b/mc_frontend/src/app/models/mockMC.ts @@ -1,16 +1,14 @@ import {CorpusMC} from './corpusMC'; import {ExerciseMC} from './exerciseMC'; import {PartOfSpeechValue, Phenomenon} from './enum'; -import {FrequencyItem} from './frequencyItem'; import {ApplicationState} from './applicationState'; import {TextData} from './textData'; -import {AnnisResponse} from './annisResponse'; -import {NodeMC} from './nodeMC'; import {TestResultMC} from './testResultMC'; import StatementBase from './xAPI/StatementBase'; import Result from './xAPI/Result'; import Score from './xAPI/Score'; import {TextRange} from './textRange'; +import {AnnisResponse, FrequencyItem} from '../../../openapi'; export default class MockMC { static apiResponseCorporaGet: CorpusMC[] = [new CorpusMC({ @@ -18,20 +16,28 @@ export default class MockMC { source_urn: 'urn', title: 'title', })]; - static apiResponseFrequencyAnalysisGet: FrequencyItem[] = [new FrequencyItem({ + static apiResponseFrequencyAnalysisGet: FrequencyItem[] = [{ phenomena: [Phenomenon.partOfSpeech.toString()], values: [PartOfSpeechValue.adjective.toString()] - })]; - static apiResponseTextGet: AnnisResponse = new AnnisResponse({ - nodes: [new NodeMC({udep_lemma: 'lemma', annis_tok: 'tok'})], - links: [] - }); + }]; + static apiResponseTextGet: AnnisResponse = { + graph_data: { + nodes: [{udep_lemma: 'lemma', annis_tok: 'tok'}], + links: [] + } + }; static applicationState: ApplicationState = new ApplicationState({ currentSetup: new TextData({ currentCorpus: new CorpusMC({citations: {}}), currentTextRange: new TextRange({start: ['1', '2'], end: ['1', '2']}) }), - mostRecentSetup: new TextData({annisResponse: new AnnisResponse({nodes: [new NodeMC()], links: []})}), + mostRecentSetup: new TextData({ + annisResponse: { + graph_data: { + nodes: [{}], links: [] + } + } + }), exerciseList: [new ExerciseMC()] }); static popoverController: any = {create: () => Promise.resolve({present: () => Promise.resolve()})}; diff --git a/mc_frontend/src/app/models/nodeMC.ts b/mc_frontend/src/app/models/nodeMC.ts deleted file mode 100644 index 1d6c1df..0000000 --- a/mc_frontend/src/app/models/nodeMC.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* tslint:disable:variable-name */ -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 udep_feats: string; - public solution: string; - public is_oov: boolean; - constructor(init?: Partial<NodeMC>) { - Object.assign(this, init); - } -} diff --git a/mc_frontend/src/app/models/solution.ts b/mc_frontend/src/app/models/solution.ts deleted file mode 100644 index 027f476..0000000 --- a/mc_frontend/src/app/models/solution.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {SolutionElement} from 'src/app/models/solutionElement'; - -export class Solution { - public target: SolutionElement; - public value: SolutionElement; - - constructor(init?: Partial<Solution>) { - Object.assign(this, init); - } -} diff --git a/mc_frontend/src/app/models/solutionElement.ts b/mc_frontend/src/app/models/solutionElement.ts deleted file mode 100644 index dbfe8cb..0000000 --- a/mc_frontend/src/app/models/solutionElement.ts +++ /dev/null @@ -1,10 +0,0 @@ -export class SolutionElement { - public sentence_id: number; - public token_id: number; - public content: string; - public salt_id: string; - - constructor(init?: Partial<SolutionElement>) { - Object.assign(this, init); - } -} \ No newline at end of file diff --git a/mc_frontend/src/app/models/textData.ts b/mc_frontend/src/app/models/textData.ts index 98efa67..b897b53 100644 --- a/mc_frontend/src/app/models/textData.ts +++ b/mc_frontend/src/app/models/textData.ts @@ -1,7 +1,7 @@ import {CorpusMC} from 'src/app/models/corpusMC'; import {TextRange} from 'src/app/models/textRange'; -import {AnnisResponse} from 'src/app/models/annisResponse'; import {Author} from 'src/app/models/author'; +import {AnnisResponse} from '../../../openapi'; export class TextData { public annisResponse: AnnisResponse; diff --git a/mc_frontend/src/app/preview/preview.page.html b/mc_frontend/src/app/preview/preview.page.html index 57bc606..c9b890b 100644 --- a/mc_frontend/src/app/preview/preview.page.html +++ b/mc_frontend/src/app/preview/preview.page.html @@ -71,8 +71,8 @@ beginning that it is going to be a download (instead of an ordinary link or clic </ion-button> </ion-col> <ion-col style="text-align: left;"> - <ion-button id="showShareLinkButton" style="max-width: 6em" (click)="showShareLink = !showShareLink"> - <ion-icon slot="start" name="share"></ion-icon> + <ion-button id="showShareLinkButton" style="max-width: 8em" (click)="showShareLink = !showShareLink"> + <ion-icon slot="start" name="share-social-outline"></ion-icon> {{ "SHARE" | translate}} </ion-button> </ion-col> diff --git a/mc_frontend/src/app/preview/preview.page.spec.ts b/mc_frontend/src/app/preview/preview.page.spec.ts index 1bf652a..795eb62 100644 --- a/mc_frontend/src/app/preview/preview.page.spec.ts +++ b/mc_frontend/src/app/preview/preview.page.spec.ts @@ -8,26 +8,21 @@ import {RouterModule} from '@angular/router'; import {TranslateTestingModule} from '../translate-testing/translate-testing.module'; import {FormsModule} from '@angular/forms'; import {APP_BASE_HREF} from '@angular/common'; -import {CorpusService} from '../corpus.service'; import {ToastController} from '@ionic/angular'; import MockMC from '../models/mockMC'; -import {AnnisResponse} from '../models/annisResponse'; -import {Solution} from '../models/solution'; import {ExerciseType} from '../models/enum'; -import {SolutionElement} from '../models/solutionElement'; import Spy = jasmine.Spy; -import {NodeMC} from '../models/nodeMC'; import {TestResultMC} from '../models/testResultMC'; import H5PeventDispatcherMock from '../models/h5pEventDispatcherMock'; import Result from '../models/xAPI/Result'; import configMC from '../../configMC'; +import {AnnisResponse, Solution} from '../../../openapi'; declare var H5P: any; describe('PreviewPage', () => { let previewPage: PreviewPage; let fixture: ComponentFixture<PreviewPage>; - let corpusService: CorpusService; let checkAnnisResponseSpy: Spy; let xapiSpy: Spy; @@ -48,15 +43,14 @@ describe('PreviewPage', () => { schemas: [CUSTOM_ELEMENTS_SCHEMA], }) .compileComponents().then(); - corpusService = TestBed.inject(CorpusService); + })); + + beforeEach(() => { fixture = TestBed.createComponent(PreviewPage); previewPage = fixture.componentInstance; xapiSpy = spyOn(previewPage, 'setXAPIeventHandler'); - checkAnnisResponseSpy = spyOn(corpusService, 'checkAnnisResponse').and.callFake(() => Promise.reject()); + checkAnnisResponseSpy = spyOn(previewPage.corpusService, 'checkAnnisResponse').and.callFake(() => Promise.reject()); fixture.detectChanges(); - })); - - beforeEach(() => { }); it('should create', () => { @@ -65,7 +59,7 @@ describe('PreviewPage', () => { it('should copy the link', () => { previewPage.helperService.isVocabularyCheck = true; - previewPage.corpusService.annisResponse = new AnnisResponse({solutions: []}); + previewPage.corpusService.annisResponse = {solutions: []}; fixture.detectChanges(); const button: HTMLIonButtonElement = document.querySelector('#showShareLinkButton'); button.click(); @@ -78,7 +72,7 @@ describe('PreviewPage', () => { it('should initialize H5P', () => { spyOn(previewPage.exerciseService, 'initH5P').and.returnValue(Promise.resolve()); - previewPage.corpusService.annisResponse = new AnnisResponse({exercise_id: '', solutions: [new Solution()]}); + previewPage.corpusService.annisResponse = {exercise_id: '', solutions: [{}]}; previewPage.currentSolutions = previewPage.corpusService.annisResponse.solutions; previewPage.initH5P(); expect(previewPage.solutionIndicesString.length).toBe(0); @@ -104,7 +98,7 @@ describe('PreviewPage', () => { checkAnnisResponseSpy.and.returnValue(Promise.resolve()); spyOn(previewPage, 'initH5P'); spyOn(previewPage, 'processAnnisResponse'); - previewPage.currentSolutions = [new Solution()]; + previewPage.currentSolutions = [{}]; previewPage.ngOnInit().then(() => { expect(previewPage.currentSolutions.length).toBe(0); iframe = document.querySelector(previewPage.exerciseService.h5pIframeString); @@ -116,41 +110,40 @@ describe('PreviewPage', () => { }); it('should process an ANNIS response', () => { - previewPage.corpusService.annisResponse = new AnnisResponse({}); - const ar: AnnisResponse = new AnnisResponse({ - solutions: [new Solution({target: new SolutionElement({content: 'content'})})] - }); + previewPage.corpusService.annisResponse = {graph_data: {links: [], nodes: []}}; + const solution: Solution = {target: {content: 'content', sentence_id: 1, token_id: 1}}; + const ar: AnnisResponse = {solutions: [solution], graph_data: {links: [], nodes: []}}; previewPage.processAnnisResponse(ar); expect(previewPage.corpusService.annisResponse.solutions.length).toBe(1); previewPage.corpusService.currentUrn = 'urn:'; previewPage.processAnnisResponse(ar); - expect(previewPage.corpusService.annisResponse.nodes).toEqual(ar.nodes); + expect(previewPage.corpusService.annisResponse.graph_data.nodes).toEqual(ar.graph_data.nodes); }); it('should process solutions', () => { const solutions: Solution[] = [ - new Solution({ - target: new SolutionElement({content: 'content2', salt_id: 'id'}), - value: new SolutionElement({salt_id: 'id'}) - }), - new Solution({ - target: new SolutionElement({content: 'content1', salt_id: 'id'}), - value: new SolutionElement({salt_id: 'id'}) - }), - new Solution({ - target: new SolutionElement({content: 'content1', salt_id: 'id'}), - value: new SolutionElement({salt_id: 'id'}) - }), - new Solution({ - target: new SolutionElement({content: 'content3', salt_id: 'id'}), - value: new SolutionElement({salt_id: 'id'}) - })]; + { + target: {content: 'content2', salt_id: 'id', sentence_id: 1, token_id: 1}, + value: {salt_id: 'id', content: '', sentence_id: 1, token_id: 1} + }, + { + target: {content: 'content1', salt_id: 'id', sentence_id: 1, token_id: 1}, + value: {salt_id: 'id', content: '', sentence_id: 1, token_id: 1} + }, + { + target: {content: 'content1', salt_id: 'id', sentence_id: 1, token_id: 1}, + value: {salt_id: 'id', content: '', sentence_id: 1, token_id: 1} + }, + { + target: {content: 'content3', salt_id: 'id', sentence_id: 1, token_id: 1}, + value: {salt_id: 'id', content: '', sentence_id: 1, token_id: 1} + }]; previewPage.corpusService.exercise.type = ExerciseType.markWords; previewPage.exerciseService.excludeOOV = true; - previewPage.corpusService.annisResponse = new AnnisResponse({ - nodes: [new NodeMC({is_oov: false, id: 'id'})], + previewPage.corpusService.annisResponse = { + graph_data: {nodes: [{is_oov: false, id: 'id'}], links: []}, solutions - }); + }; previewPage.processSolutions(solutions); expect(previewPage.currentSolutions[2]).toBe(solutions[0]); }); @@ -170,8 +163,8 @@ describe('PreviewPage', () => { }); it('should switch OOV', () => { - previewPage.currentSolutions = [new Solution()]; - previewPage.corpusService.annisResponse = new AnnisResponse(); + previewPage.currentSolutions = [{}]; + previewPage.corpusService.annisResponse = {}; spyOn(previewPage, 'processSolutions'); spyOn(previewPage, 'initH5P'); previewPage.switchOOV(); diff --git a/mc_frontend/src/app/preview/preview.page.ts b/mc_frontend/src/app/preview/preview.page.ts index decf3ef..3ada6e1 100644 --- a/mc_frontend/src/app/preview/preview.page.ts +++ b/mc_frontend/src/app/preview/preview.page.ts @@ -1,5 +1,4 @@ /* tslint:disable:no-string-literal */ -import {AnnisResponse} from 'src/app/models/annisResponse'; import {ExerciseType, FileType} from 'src/app/models/enum'; import {HelperService} from 'src/app/helper.service'; import {NavController, ToastController} from '@ionic/angular'; @@ -7,12 +6,12 @@ import {ExerciseService} from 'src/app/exercise.service'; import {CorpusService} from 'src/app/corpus.service'; import {Component, OnDestroy, OnInit} from '@angular/core'; import {TranslateService} from '@ngx-translate/core'; -import {Solution} from 'src/app/models/solution'; import {HttpClient} from '@angular/common/http'; import {XAPIevent} from 'src/app/models/xAPIevent'; import {TestResultMC} from 'src/app/models/testResultMC'; import configMC from '../../configMC'; import {Storage} from '@ionic/storage'; +import {AnnisResponse, Solution} from '../../../openapi'; declare var H5P: any; @@ -90,19 +89,19 @@ export class PreviewPage implements OnDestroy, OnInit { this.processSolutions(ar.solutions); this.corpusService.annisResponse.uri = ar.uri; const isUrn: boolean = this.corpusService.currentUrn && this.corpusService.currentUrn.startsWith('urn:'); - this.corpusService.annisResponse.nodes = isUrn ? this.corpusService.annisResponse.nodes : ar.nodes; - this.corpusService.annisResponse.links = isUrn ? this.corpusService.annisResponse.links : ar.links; + this.corpusService.annisResponse.graph_data.nodes = isUrn ? this.corpusService.annisResponse.graph_data.nodes : ar.graph_data.nodes; + this.corpusService.annisResponse.graph_data.links = isUrn ? this.corpusService.annisResponse.graph_data.links : ar.graph_data.links; } processSolutions(solutions: Solution[]): void { const isCloze: boolean = this.corpusService.exercise.type === ExerciseType.cloze; if (this.exerciseService.excludeOOV) { - const nodeIdSet: Set<string> = new Set(this.corpusService.annisResponse.nodes.filter( + const nodeIdSet: Set<string> = new Set(this.corpusService.annisResponse.graph_data.nodes.filter( x => !x.is_oov).map(x => x.id)); solutions = this.corpusService.annisResponse.solutions.filter( x => nodeIdSet.has(x.target.salt_id) && (isCloze || nodeIdSet.has(x.value.salt_id))); } - let newSolutions: Solution[] = []; + let newSolutions: Solution[]; if (isCloze) { this.maxGapLength = Math.max.apply(Math, solutions.map(x => x.target.content.length)); this.solutionNodeIdSet = new Set(solutions.map(x => x.target.salt_id)); diff --git a/mc_frontend/src/app/ranking/ranking.page.spec.ts b/mc_frontend/src/app/ranking/ranking.page.spec.ts index d13b82b..8c51faf 100644 --- a/mc_frontend/src/app/ranking/ranking.page.spec.ts +++ b/mc_frontend/src/app/ranking/ranking.page.spec.ts @@ -7,16 +7,13 @@ import {IonicStorageModule} from '@ionic/storage'; import {RouterModule} from '@angular/router'; import {TranslateTestingModule} from '../translate-testing/translate-testing.module'; import {APP_BASE_HREF} from '@angular/common'; -import {CorpusService} from '../corpus.service'; import {Sentence} from '../models/sentence'; import Spy = jasmine.Spy; -import {AnnisResponse} from '../models/annisResponse'; -import {NodeMC} from '../models/nodeMC'; +import {AnnisResponse} from '../../../openapi'; describe('RankingPage', () => { let rankingPage: RankingPage; let fixture: ComponentFixture<RankingPage>; - let corpusService: CorpusService; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -33,7 +30,6 @@ describe('RankingPage', () => { schemas: [CUSTOM_ELEMENTS_SCHEMA], }) .compileComponents().then(); - corpusService = TestBed.inject(CorpusService); })); beforeEach(() => { @@ -48,8 +44,8 @@ describe('RankingPage', () => { it('should show the text', (done) => { rankingPage.helperService.isVocabularyCheck = false; - const vocCheckSpy: Spy = spyOn(rankingPage.vocService, 'getVocabularyCheck').and.returnValue(Promise.resolve( - new AnnisResponse({nodes: [new NodeMC({id: 'id/id:1-2/id'})]}))); + const ar: AnnisResponse = {graph_data: {nodes: [{id: 'id/id:1-2/id'}], links: []}}; + const vocCheckSpy: Spy = spyOn(rankingPage.vocService, 'getVocabularyCheck').and.returnValue(Promise.resolve(ar)); spyOn(rankingPage.corpusService, 'processAnnisResponse'); spyOn(rankingPage.helperService, 'goToShowTextPage').and.returnValue(Promise.resolve(true)); rankingPage.showText([new Sentence({id: 1})]).then(() => { diff --git a/mc_frontend/src/app/ranking/ranking.page.ts b/mc_frontend/src/app/ranking/ranking.page.ts index 331a3e5..b359f58 100644 --- a/mc_frontend/src/app/ranking/ranking.page.ts +++ b/mc_frontend/src/app/ranking/ranking.page.ts @@ -1,12 +1,12 @@ import {Component} from '@angular/core'; import {VocabularyService} from 'src/app/vocabulary.service'; -import {AnnisResponse} from 'src/app/models/annisResponse'; import {ExerciseService} from 'src/app/exercise.service'; import {HelperService} from 'src/app/helper.service'; import {NavController, ToastController} from '@ionic/angular'; import {CorpusService} from 'src/app/corpus.service'; import {HttpErrorResponse} from '@angular/common/http'; import {Sentence} from 'src/app/models/sentence'; +import {AnnisResponse} from '../../../openapi'; @Component({ selector: 'app-ranking', @@ -30,8 +30,8 @@ export class RankingPage { return new Promise<void>((resolve, reject) => { this.corpusService.currentUrn = this.corpusService.baseUrn + `@${rank[0].id}-${rank[rank.length - 1].id}`; this.vocService.getVocabularyCheck(this.corpusService.currentUrn, true).then((ar: AnnisResponse) => { - const urnStart: string = ar.nodes[0].id.split('/')[1]; - const urnEnd: string = ar.nodes.slice(-1)[0].id.split('/')[1]; + const urnStart: string = ar.graph_data.nodes[0].id.split('/')[1]; + const urnEnd: string = ar.graph_data.nodes.slice(-1)[0].id.split('/')[1]; this.corpusService.currentUrn = urnStart.concat('-', urnEnd.split(':').slice(-1)[0]); this.corpusService.processAnnisResponse(ar); this.helperService.isVocabularyCheck = true; diff --git a/mc_frontend/src/app/semantics/semantics.page.spec.ts b/mc_frontend/src/app/semantics/semantics.page.spec.ts index 5c86a7e..b30aa61 100644 --- a/mc_frontend/src/app/semantics/semantics.page.spec.ts +++ b/mc_frontend/src/app/semantics/semantics.page.spec.ts @@ -31,10 +31,13 @@ describe('SemanticsPage', () => { ], schemas: [CUSTOM_ELEMENTS_SCHEMA], }).compileComponents().then(); + })); + + beforeEach(() => { fixture = TestBed.createComponent(SemanticsPage); semanticsPage = fixture.componentInstance; fixture.detectChanges(); - })); + }); it('should create', () => { expect(semanticsPage).toBeTruthy(); diff --git a/mc_frontend/src/app/show-text/show-text.page.html b/mc_frontend/src/app/show-text/show-text.page.html index a439707..c5b202f 100644 --- a/mc_frontend/src/app/show-text/show-text.page.html +++ b/mc_frontend/src/app/show-text/show-text.page.html @@ -39,8 +39,9 @@ <ion-col class="text" size="8"> <div *ngIf="highlightOOV; else noOOV"> <!-- do not add whitespace/newline etc. between the span elements! --> - <span *ngFor="let node of corpusService.annisResponse.nodes; index as i"><span class="tok" - [class.oov]="node.is_oov">{{node.annis_tok}}</span>{{ getWhiteSpace(i) }}</span> + <span *ngFor="let node of corpusService.annisResponse.graph_data.nodes; index as i"><span + class="tok" + [class.oov]="node.is_oov">{{node.annis_tok}}</span>{{ getWhiteSpace(i) }}</span> </div> <ng-template #noOOV> <span class="tok">{{corpusService.currentText}}</span> diff --git a/mc_frontend/src/app/show-text/show-text.page.spec.ts b/mc_frontend/src/app/show-text/show-text.page.spec.ts index 87ccf6a..04152dc 100644 --- a/mc_frontend/src/app/show-text/show-text.page.spec.ts +++ b/mc_frontend/src/app/show-text/show-text.page.spec.ts @@ -8,8 +8,6 @@ import {RouterModule} from '@angular/router'; import {TranslateTestingModule} from '../translate-testing/translate-testing.module'; import {FormsModule} from '@angular/forms'; import {APP_BASE_HREF} from '@angular/common'; -import {AnnisResponse} from '../models/annisResponse'; -import {NodeMC} from '../models/nodeMC'; import {VocabularyCorpus} from '../models/enum'; import Spy = jasmine.Spy; import MockMC from '../models/mockMC'; @@ -68,13 +66,13 @@ describe('ShowTextPage', () => { }); it('should get whitespace', () => { - showTextPage.corpusService.annisResponse = new AnnisResponse({nodes: []}); + showTextPage.corpusService.annisResponse = {graph_data: {nodes: [], links: []}}; let result: string = showTextPage.getWhiteSpace(0); expect(result.length).toBe(0); - showTextPage.corpusService.annisResponse.nodes = [new NodeMC(), new NodeMC()]; + showTextPage.corpusService.annisResponse.graph_data.nodes = [{}, {}]; result = showTextPage.getWhiteSpace(0); expect(result.length).toBe(1); - showTextPage.corpusService.annisResponse.nodes[1].annis_tok = '.'; + showTextPage.corpusService.annisResponse.graph_data.nodes[1].annis_tok = '.'; result = showTextPage.getWhiteSpace(0); expect(result.length).toBe(0); }); diff --git a/mc_frontend/src/app/show-text/show-text.page.ts b/mc_frontend/src/app/show-text/show-text.page.ts index dc87fee..890e818 100644 --- a/mc_frontend/src/app/show-text/show-text.page.ts +++ b/mc_frontend/src/app/show-text/show-text.page.ts @@ -84,9 +84,10 @@ export class ShowTextPage implements OnInit { } getWhiteSpace(index: number): string { - if (this.corpusService.annisResponse.nodes[index + 1]) { - if (this.corpusService.annisResponse.nodes[index + 1].annis_tok && - this.corpusService.annisResponse.nodes[index + 1].annis_tok.search(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g) >= 0) { + if (this.corpusService.annisResponse.graph_data.nodes[index + 1]) { + if (this.corpusService.annisResponse.graph_data.nodes[index + 1].annis_tok && + this.corpusService.annisResponse.graph_data.nodes[index + 1].annis_tok + .search(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g) >= 0) { return ''; } return ' '; diff --git a/mc_frontend/src/app/vocabulary.service.spec.ts b/mc_frontend/src/app/vocabulary.service.spec.ts index 311b97d..b800c5f 100644 --- a/mc_frontend/src/app/vocabulary.service.spec.ts +++ b/mc_frontend/src/app/vocabulary.service.spec.ts @@ -6,9 +6,9 @@ import {IonicStorageModule} from '@ionic/storage'; import {TranslateTestingModule} from './translate-testing/translate-testing.module'; import {VocabularyCorpus} from './models/enum'; import {Sentence} from './models/sentence'; -import {AnnisResponse} from './models/annisResponse'; import {HttpErrorResponse} from '@angular/common/http'; import Spy = jasmine.Spy; +import {AnnisResponse} from '../../openapi'; describe('VocabularyService', () => { let vocabularyService: VocabularyService; @@ -37,7 +37,8 @@ describe('VocabularyService', () => { vocabularyService.getVocabularyCheck('', false).then(() => { }, async (response: HttpErrorResponse) => { expect(response.status).toBe(500); - requestSpy.and.returnValue(Promise.resolve(new AnnisResponse())); + const ar: AnnisResponse = {}; + requestSpy.and.returnValue(Promise.resolve(ar)); const result: AnnisResponse | Sentence[] = await vocabularyService.getVocabularyCheck('', true); expect(result.hasOwnProperty('length')).toBe(false); done(); diff --git a/mc_frontend/src/app/vocabulary.service.ts b/mc_frontend/src/app/vocabulary.service.ts index d2fb10e..13ff907 100644 --- a/mc_frontend/src/app/vocabulary.service.ts +++ b/mc_frontend/src/app/vocabulary.service.ts @@ -7,8 +7,8 @@ import {Sentence} from 'src/app/models/sentence'; import {HelperService} from 'src/app/helper.service'; import {TestResultMC} from 'src/app/models/testResultMC'; import {ToastController} from '@ionic/angular'; -import {AnnisResponse} from 'src/app/models/annisResponse'; import configMC from '../configMC'; +import {AnnisResponse} from '../../openapi'; @Injectable({ providedIn: 'root' -- GitLab