Compare commits

..

20 Commits

Author SHA1 Message Date
Karl-Philipp Wulfert
e8da621558 0.18.0 2019-05-14 14:42:35 +02:00
Rainer Killinger
6f12fbda94 test: add tests concerning the translators cache 2019-05-13 15:04:31 +02:00
Rainer Killinger
cf83692e71 refactor: simpify translator class functions 2019-05-13 15:04:31 +02:00
Jovan Krunić
27417e80e1 build: replace missing package
Closes #65
2019-05-09 17:15:03 +02:00
Sebastian Lange
bab675b806 refactor: remodel settings inputType
Flatten inputType to fit new core translation

Closes #59
2019-04-29 12:48:43 +02:00
Rainer Killinger
d3790adbd8 feat: add study module interface 2019-04-17 14:50:25 +02:00
Karl-Philipp Wulfert
d8aa023b28 docs: update changelog 2019-04-16 14:25:36 +02:00
Karl-Philipp Wulfert
4217e237fb 0.17.0 2019-04-16 14:25:33 +02:00
Karl-Philipp Wulfert
c7e2584472 build: adjust build script and add contributors 2019-04-16 14:09:29 +02:00
Karl-Philipp Wulfert
e30e04384f build: update dependencies 2019-04-16 14:08:16 +02:00
Karl-Philipp Wulfert
6fb9ccb821 docs: update changelog 2019-04-15 17:41:51 +02:00
Karl-Philipp Wulfert
9fb0a7c885 0.16.0 2019-04-15 17:41:48 +02:00
Karl-Philipp Wulfert
b5e0b76c24 build: exclude docs from package
Fixes #56
2019-04-15 15:51:25 +02:00
Karl-Philipp Wulfert
a2f44762f9 docs: update changelog 2019-04-09 17:14:55 +02:00
Karl-Philipp Wulfert
d46abbe29b 0.15.0 2019-04-09 17:14:52 +02:00
Rainer Killinger
4986042428 fix: change SCThingMeta getInstance() return value 2019-04-09 14:08:07 +00:00
Michel Jonathan Schmitz
3242411768 feat: provide context based search 2019-04-09 13:47:53 +00:00
Karl-Philipp Wulfert
37e5f6c490 build: update dependencies
Fixes #53
2019-04-09 12:36:22 +02:00
Karl-Philipp Wulfert
623ed613a9 fix: resolve issues with things that can be offered
Fixes #41
2019-04-04 13:47:30 +02:00
Karl-Philipp Wulfert
fd994e2c08 docs: update changelog 2019-04-03 16:23:56 +02:00
24 changed files with 1938 additions and 3439 deletions

View File

@@ -1,4 +1,3 @@
# EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs
# editorconfig.org # editorconfig.org
root = true root = true
@@ -7,11 +6,10 @@ root = true
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
# We recommend you to keep these unchanged
end_of_line = lf end_of_line = lf
charset = utf-8 charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
[*.md] [*.md]
trim_trailing_whitespace = false trim_trailing_whitespace = false

29
.gitignore vendored
View File

@@ -20,7 +20,7 @@ coverage
# nyc test coverage # nyc test coverage
.nyc_output .nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt .grunt
# Bower dependency directory (https://bower.io/) # Bower dependency directory (https://bower.io/)
@@ -29,14 +29,14 @@ bower_components
# node-waf configuration # node-waf configuration
.lock-wscript .lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html) # Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release build/Release
# Dependency directories # Dependency directories
node_modules/ node_modules/
jspm_packages/ jspm_packages/
# Typescript v1 declaration files # TypeScript v1 declaration files
typings/ typings/
# Optional npm cache directory # Optional npm cache directory
@@ -57,6 +57,29 @@ typings/
# dotenv environment variables file # dotenv environment variables file
.env .env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
#DynamoDB Local files
.dynamodb/
########## end of https://github.com/github/gitignore/blob/master/Node.gitignore
# ignore ide files # ignore ide files
.idea .idea
.vscode .vscode

View File

@@ -1,14 +1,12 @@
# Ignore all files/folders by default # Ignore all files/folders by default
# See https://stackoverflow.com/a/29932318 # See https://stackoverflow.com/a/29932318
/* /*
# Except these files/folders
# Execept this files/folders
!docs
!lib !lib
lib/tsconfig.tsbuildinfo
!LICENSE !LICENSE
!package.json !package.json
!package-lock.json !package-lock.json
!README.md !README.md
!src !src
test/*
!test/resources/ !test/resources/

View File

@@ -1,3 +1,35 @@
# [0.17.0](https://gitlab.com/openstapps/core/compare/v0.16.0...v0.17.0) (2019-04-16)
# [0.16.0](https://gitlab.com/openstapps/core/compare/v0.15.0...v0.16.0) (2019-04-15)
# [0.15.0](https://gitlab.com/openstapps/core/compare/v0.14.0...v0.15.0) (2019-04-09)
### Bug Fixes
* change SCThingMeta getInstance() return value ([4986042](https://gitlab.com/openstapps/core/commit/4986042))
* resolve issues with things that can be offered ([623ed61](https://gitlab.com/openstapps/core/commit/623ed61)), closes [#41](https://gitlab.com/openstapps/core/issues/41)
### Features
* provide context based search ([3242411](https://gitlab.com/openstapps/core/commit/3242411))
# [0.14.0](https://gitlab.com/openstapps/core/compare/v0.13.0...v0.14.0) (2019-04-03)
### Features
* add model for plugin register route ([8188731](https://gitlab.com/openstapps/core/commit/8188731))
# [0.13.0](https://gitlab.com/openstapps/core/compare/v0.12.0...v0.13.0) (2019-04-02) # [0.13.0](https://gitlab.com/openstapps/core/compare/v0.12.0...v0.13.0) (2019-04-02)

4171
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "@openstapps/core", "name": "@openstapps/core",
"version": "0.14.0", "version": "0.18.0",
"description": "StAppsCore - Generalized model of data", "description": "StAppsCore - Generalized model of data",
"keywords": [ "keywords": [
"Model", "Model",
@@ -14,10 +14,10 @@
"main": "./lib/index.js", "main": "./lib/index.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
"scripts": { "scripts": {
"build": "npm run tslint && npm run compile && npm run pack && npm run schema && npm run documentation", "build": "npm run tslint && npm run compile && npm run pack && npm run schema",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md && git commit -m 'docs: update changelog'", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md && git commit -m 'docs: update changelog'",
"check-configuration": "openstapps-configuration", "check-configuration": "openstapps-configuration",
"compile": "tsc", "compile": "rimraf lib && tsc",
"documentation": "typedoc --name \"@openstapps/core\" --includeDeclarations --mode modules --out docs --readme README.md --listInvalidSymbolLinks lib", "documentation": "typedoc --name \"@openstapps/core\" --includeDeclarations --mode modules --out docs --readme README.md --listInvalidSymbolLinks lib",
"pack": "openstapps-core-tools pack", "pack": "openstapps-core-tools pack",
"prepublishOnly": "npm ci && npm run build", "prepublishOnly": "npm ci && npm run build",
@@ -28,38 +28,41 @@
"author": "Karl-Philipp Wulfert <krlwlfrt@gmail.com>", "author": "Karl-Philipp Wulfert <krlwlfrt@gmail.com>",
"contributors": [ "contributors": [
"Anselm Stordeur <anselmstordeur@gmail.com>", "Anselm Stordeur <anselmstordeur@gmail.com>",
"Jovan Krunic <jovan.krunic@gmail.com>", "Jovan Krunić <jovan.krunic@gmail.com>",
"Andreas Lehmann", "Andreas Lehmann",
"Sebastian Lange", "Sebastian Lange",
"Rainer Killinger", "Rainer Killinger",
"Imran Hossain" "Imran Hossain",
"Michel Jonathan Schmitz",
"Wieland Schöbl"
], ],
"dependencies": { "dependencies": {
"@types/geojson": "1.0.6", "@types/geojson": "1.0.6",
"@types/json-patch": "0.0.30", "@types/json-patch": "0.0.30",
"fast-clone": "1.5.13",
"json-patch": "0.7.0", "json-patch": "0.7.0",
"jsonschema": "1.2.4", "jsonschema": "1.2.4",
"ts-optchain": "0.1.3" "ts-optchain": "0.1.3"
}, },
"devDependencies": { "devDependencies": {
"@openstapps/configuration": "0.8.0", "@krlwlfrt/async-pool": "0.1.0",
"@openstapps/core-tools": "0.3.0", "@openstapps/configuration": "0.12.0",
"@openstapps/core-tools": "0.6.0",
"@openstapps/logger": "0.0.5", "@openstapps/logger": "0.0.5",
"@types/chai": "4.1.7", "@types/chai": "4.1.7",
"@types/node": "10.14.4", "@types/node": "10.14.4",
"@types/rimraf": "2.0.2", "@types/rimraf": "2.0.2",
"async-pool-native": "0.1.0",
"chai": "4.2.0", "chai": "4.2.0",
"commander": "2.19.0", "commander": "2.20.0",
"conventional-changelog-cli": "2.0.12", "conventional-changelog-cli": "2.0.12",
"mocha": "6.0.2", "mocha": "6.1.4",
"mocha-typescript": "1.1.17", "mocha-typescript": "1.1.17",
"nyc": "13.3.0", "nyc": "14.0.0",
"rimraf": "2.6.3", "rimraf": "2.6.3",
"ts-node": "8.0.3", "ts-node": "8.1.0",
"tslint": "5.15.0", "tslint": "5.15.0",
"typedoc": "0.14.2", "typedoc": "0.14.2",
"typescript": "3.4.1" "typescript": "3.4.3"
}, },
"nyc": { "nyc": {
"check-coverage": true, "check-coverage": true,

View File

@@ -32,6 +32,7 @@ import {SCRoom, SCRoomMeta, SCRoomWithoutReferences} from './things/Room';
import {SCSemester, SCSemesterMeta, SCSemesterWithoutReferences} from './things/Semester'; import {SCSemester, SCSemesterMeta, SCSemesterWithoutReferences} from './things/Semester';
import {SCSetting, SCSettingMeta, SCSettingWithoutReferences} from './things/Setting'; import {SCSetting, SCSettingMeta, SCSettingWithoutReferences} from './things/Setting';
import {SCSportCourse, SCSportCourseMeta, SCSportCourseWithoutReferences} from './things/SportCourse'; import {SCSportCourse, SCSportCourseMeta, SCSportCourseWithoutReferences} from './things/SportCourse';
import {SCStudyModule, SCStudyModuleMeta, SCStudyModuleWithoutReferences} from './things/StudyModule';
import {SCTicket, SCTicketMeta, SCTicketWithoutReferences} from './things/Ticket'; import {SCTicket, SCTicketMeta, SCTicketWithoutReferences} from './things/Ticket';
import {SCToDo, SCToDoMeta, SCToDoWithoutReferences} from './things/ToDo'; import {SCToDo, SCToDoMeta, SCToDoWithoutReferences} from './things/ToDo';
import {SCTour, SCTourMeta, SCTourWithoutReferences} from './things/Tour'; import {SCTour, SCTourMeta, SCTourWithoutReferences} from './things/Tour';
@@ -62,6 +63,7 @@ export const SCClasses: { [K in SCThingType]: any } = {
'semester': SCSemesterMeta, 'semester': SCSemesterMeta,
'setting': SCSettingMeta, 'setting': SCSettingMeta,
'sport course': SCSportCourseMeta, 'sport course': SCSportCourseMeta,
'study module': SCStudyModuleMeta,
'ticket': SCTicketMeta, 'ticket': SCTicketMeta,
'todo': SCToDoMeta, 'todo': SCToDoMeta,
'tour': SCTourMeta, 'tour': SCTourMeta,
@@ -87,6 +89,7 @@ export type SCThingsWithoutDiff =
| SCSemester | SCSemester
| SCSetting | SCSetting
| SCSportCourse | SCSportCourse
| SCStudyModule
| SCTicket | SCTicket
| SCToDo | SCToDo
| SCTour | SCTour
@@ -127,11 +130,12 @@ export type SCAssociatedThingWithoutReferences<THING extends SCThings> =
THING extends SCSemester ? SCSemesterWithoutReferences : THING extends SCSemester ? SCSemesterWithoutReferences :
THING extends SCSetting ? SCSettingWithoutReferences : THING extends SCSetting ? SCSettingWithoutReferences :
THING extends SCSportCourse ? SCSportCourseWithoutReferences : THING extends SCSportCourse ? SCSportCourseWithoutReferences :
THING extends SCTicket ? SCTicketWithoutReferences : THING extends SCStudyModule ? SCStudyModuleWithoutReferences :
THING extends SCToDo ? SCToDoWithoutReferences : THING extends SCTicket ? SCTicketWithoutReferences :
THING extends SCTour ? SCTourWithoutReferences : THING extends SCToDo ? SCToDoWithoutReferences :
THING extends SCVideo ? SCVideoWithoutReferences : THING extends SCTour ? SCTourWithoutReferences :
never; THING extends SCVideo ? SCVideoWithoutReferences :
never;
/** /**
* Thing for a thing without references * Thing for a thing without references
@@ -156,8 +160,9 @@ export type SCAssociatedThing<THING extends SCThings> =
THING extends SCSemesterWithoutReferences ? SCSemester : THING extends SCSemesterWithoutReferences ? SCSemester :
THING extends SCSettingWithoutReferences ? SCSetting : THING extends SCSettingWithoutReferences ? SCSetting :
THING extends SCSportCourseWithoutReferences ? SCSportCourse : THING extends SCSportCourseWithoutReferences ? SCSportCourse :
THING extends SCTicketWithoutReferences ? SCTicket : THING extends SCStudyModuleWithoutReferences ? SCStudyModule :
THING extends SCToDoWithoutReferences ? SCToDo : THING extends SCTicketWithoutReferences ? SCTicket :
THING extends SCTourWithoutReferences ? SCTour : THING extends SCToDoWithoutReferences ? SCToDo :
THING extends SCVideoWithoutReferences ? SCVideo : THING extends SCTourWithoutReferences ? SCTour :
never; THING extends SCVideoWithoutReferences ? SCVideo :
never;

View File

@@ -41,6 +41,7 @@ export enum SCThingType {
Semester = 'semester', Semester = 'semester',
Setting = 'setting', Setting = 'setting',
SportCourse = 'sport course', SportCourse = 'sport course',
StudyModule = 'study module',
Ticket = 'ticket', Ticket = 'ticket',
ToDo = 'todo', ToDo = 'todo',
Tour = 'tour', Tour = 'tour',
@@ -222,7 +223,7 @@ export class SCThingMeta implements SCMetaTranslations<SCThing> {
/** /**
* Set type definiton for singleton instance * Set type definiton for singleton instance
*/ */
protected static _instance: SCThingMeta; protected static _instance = new Map<string, unknown>();
/** /**
* Translations of fields * Translations of fields
@@ -268,8 +269,10 @@ export class SCThingMeta implements SCMetaTranslations<SCThing> {
* Function to retrieve typed singleton instance * Function to retrieve typed singleton instance
*/ */
public static getInstance<T extends SCThingMeta>(): T { public static getInstance<T extends SCThingMeta>(): T {
const typedThis = this as any as typeof SCThingMeta; if (!this._instance.has(this.name)) {
return (typedThis._instance || (typedThis._instance = new this())) as T; this._instance.set(this.name, new this());
}
return this._instance.get(this.name) as T;
} }
protected constructor() {} protected constructor() {}

View File

@@ -12,30 +12,33 @@
* You should have received a copy of the GNU General Public License along with * You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {SCClasses, SCThingsField} from './Classes'; import {SCClasses} from './Classes';
import {SCThing, SCThingType} from './Thing'; import {SCThing, SCThingType} from './Thing';
import {isThing} from './types/Guards';
import {SCTranslations} from './types/i18n'; import {SCTranslations} from './types/i18n';
import clone = require('fast-clone');
import {Defined, OCType} from 'ts-optchain'; import {Defined, OCType} from 'ts-optchain';
/** /**
* SCThingTranslator class * SCThingTranslator class
*/ */
export class SCThingTranslator { export class SCThingTranslator {
/**
* Property representing the translators base language.
* This means every translation is given for this language.
*/
private baseLanguage: keyof SCTranslations<SCThing>;
/** /**
* Property representing the translators target language * Property representing the translators target language
*/ */
private language: keyof SCTranslations<SCThing>; private _language: keyof SCTranslations<SCThing>;
/** /**
* Property provinding a mapping from a SCThingType to its known own meta class. * Property representing the translators base language
* This means every translation is given for this language
*/
private cache: LRUCache<SCThing>;
/**
* Property providing a mapping from a SCThingType to its known own meta class
*/ */
private metaClasses: typeof SCClasses; private metaClasses: typeof SCClasses;
@@ -45,42 +48,44 @@ export class SCThingTranslator {
* // returns translator instance for german * // returns translator instance for german
* new SCThingTranslator('de'); * new SCThingTranslator('de');
*/ */
constructor(language: keyof SCTranslations<SCThing>, baseLanguage?: keyof SCTranslations<SCThing>) { constructor(language: keyof SCTranslations<SCThing>, cacheCapacity: number = 200) {
this.baseLanguage = baseLanguage ? baseLanguage : 'en'; this.cache = new LRUCache(cacheCapacity);
this.language = language; this._language = language;
this.metaClasses = SCClasses; this.metaClasses = SCClasses;
} }
/**
* Getter for language property
*/
get language(): keyof SCTranslations<SCThing> {
return this._language;
}
/**
* Setter for language property. Also flushes translation cache
*
* @param language The language the translator instance will use from now on
*/
set language(language: keyof SCTranslations<SCThing>) {
if (language !== this._language) {
this.cache.flush();
}
this._language = language;
}
/** /**
* Get field value translation recursively * Get field value translation recursively
* *
* @param firstObject Top level object that gets passed through the recursion
* @param data The intermediate object / primitive returned by the Proxys get() method * @param data The intermediate object / primitive returned by the Proxys get() method
* @param keyPath The keypath that (in the end) leads to the translatable property (when added to firstObject)
* @returns an OCType<T> object allowing for access to translations or a translated value(s) * @returns an OCType<T> object allowing for access to translations or a translated value(s)
*/ */
private deeptranslate<T, K extends SCThing>(firstObject: K, data?: T, keyPath?: string): OCType<T> { private deeptranslate<T>(data?: T): OCType<T> {
const proxy = new Proxy( const proxy = new Proxy(
((defaultValue?: Defined<T>) => (data == null ? defaultValue : data)) as OCType<T>, ((defaultValue?: Defined<T>) => (data == null ? defaultValue : data)) as OCType<T>,
{ {
get: (target, key) => { get: (target, key) => {
const obj: any = target(); const obj: any = target();
const extendedKeyPath = [keyPath, key.toString()].filter((e) => e != null).join('.'); return this.deeptranslate(obj[key]);
let possiblePrimitive = obj[key];
// check if obj[key] is an array that contains primitive type (arrays in SCThings are not mixing types)
if (obj[key] instanceof Array && obj[key].length) {
possiblePrimitive = obj[key][0];
}
if (typeof possiblePrimitive === 'string' ||
typeof possiblePrimitive === 'number' ||
typeof possiblePrimitive === 'boolean') {
// returns final translation for primitive data types
return this.deeptranslate(firstObject,
this.getFieldValueTranslation(firstObject, extendedKeyPath),
extendedKeyPath);
}
// recursion to get more calls to the Proxy handler 'get()' (key path not complete)
return this.deeptranslate(firstObject, obj[key], extendedKeyPath);
}, },
}, },
); );
@@ -98,19 +103,19 @@ export class SCThingTranslator {
language: keyof SCTranslations<T>): object | undefined { language: keyof SCTranslations<T>): object | undefined {
const fieldTranslations = {}; const fieldTranslations = {};
const metaClass = this.getMetaClassInstance(thingType); const metaClass = this.getMetaClassInstance(thingType);
if (metaClass === undefined) { if (typeof metaClass === 'undefined') {
return undefined; return undefined;
} }
// Assigns every property in fieldTranslations to the known base language translation // Assigns every property in fieldTranslations to the known base language translation
if (metaClass.fieldTranslations[this.baseLanguage] !== undefined) { if (typeof metaClass.fieldTranslations.en !== 'undefined') {
Object.keys(metaClass.fieldTranslations[this.baseLanguage]).forEach((key) => { Object.keys(metaClass.fieldTranslations.en).forEach((key) => {
(fieldTranslations as any)[key] = metaClass.fieldTranslations[this.baseLanguage][key]; (fieldTranslations as any)[key] = metaClass.fieldTranslations.en[key];
}); });
} }
// Assigns every property in fieldTranslations to the known translation in given language // Assigns every property in fieldTranslations to the known translation in given language
if (metaClass.fieldTranslations[language] !== undefined) { if (typeof metaClass.fieldTranslations[language] !== 'undefined') {
Object.keys(metaClass.fieldTranslations[language]).forEach((key) => { Object.keys(metaClass.fieldTranslations[language]).forEach((key) => {
(fieldTranslations as any)[key] = metaClass.fieldTranslations[language][key]; (fieldTranslations as any)[key] = metaClass.fieldTranslations[language][key];
}); });
@@ -132,120 +137,34 @@ export class SCThingTranslator {
} }
/** /**
* Returns property value at a certain (key) path of an object. * Applies known field value translations of the given SCThings meta class to an instance
* @example * Translated values overwrite current values inplace (destructive)
* // returns value of dish.offers[0].inPlace.categories[1] *
* const dish: SCDish = {...}; * @param language The language the thing is translated to
* this.valueFromPath(dish, 'offers[0].inPlace.categories[1]'); * @param thing The thing that will be translated
* @param path Key path to evaluate * @returns The thing with translated meta field values
* @param obj Object to evaluate the key path upon
* @param separator Key path seperation element. Defaults to '.'
* @returns Property value at at key path
*/ */
private valueFromPath<T extends SCThing>(path: string, obj: T, separator = '.') { private replaceAvailableMetaFieldValueTranslations(instance: any,
path = path.replace(/\[/g, '.'); language: keyof SCTranslations<any>): any {
path = path.replace(/\]/g, '.'); const metaClass = this.getMetaClassInstance(instance.type);
path = path.replace(/\.\./g, '.'); if (typeof metaClass === 'undefined') {
path = path.replace(/\.$/, ''); return instance;
const properties = path.split(separator);
return properties.reduce((prev: any, curr: any) => prev && prev[curr], obj);
}
/**
* Get field value translation
* @example
* // returns translation of the property (if available) in the language defined when creating the translator object
* const dish: SCDish = {...};
* translator.translate(dish, 'offers[0].inPlace.categories[1]');
* @param thing SCThing to get value translation for
* @param field Field to get value translation for (keypath allowed)
* @returns Translated value(s) or value(s) itself
*/
public getFieldValueTranslation<T extends SCThing>(thing: T,
field: SCThingsField): string | string[] {
let translationPath = 'translations.' + this.language + '.' + field;
const regexTrimProperties = /.*(?:(\..*)(\[\d+\])|(\.[^\d]*$)|(\..*)(\.[\d]*$))/;
const pathMatch = field.match(regexTrimProperties);
// when translation is given in thing
let translation = this.valueFromPath(translationPath, thing);
if (translation) {
return translation;
} else if (pathMatch && pathMatch[1] && pathMatch[2] || pathMatch && pathMatch[4] && pathMatch[5]) {
// accessing iteratable of nested thing
const keyPath = (pathMatch[1] ? pathMatch[1] : pathMatch[4]) + (pathMatch[2] ? pathMatch[2] : pathMatch[5]);
const redactedField = field.replace(keyPath, '');
// when translation is given in nested thing
translationPath = `${redactedField}.translations.${this.language}${keyPath}`;
translation = this.valueFromPath(translationPath, thing);
if (translation) {
return translation;
}
// when translation is given in nested meta thing via iterateable index
const nestedType = this.valueFromPath(field.replace(keyPath, '.type'), thing) as SCThingType;
translationPath = `fieldValueTranslations.${this.language}${keyPath}`;
translation = this.valueFromPath(translationPath.replace(
/\[(?=[^\[]*$).*|(?=[\d+]*$).*/, '[' + this.valueFromPath(field, thing) + ']'),
this.getMetaClassInstance(nestedType));
if (translation) {
return translation;
}
} else if (pathMatch && pathMatch[3]) {
// accessing meta or instance of nested thing primitive value depth > 0
const keyPath = pathMatch[3];
const redactedField = field.replace(pathMatch[3], '');
// when translation is given in nested thing
translationPath = `${redactedField}.translations.${this.language}${keyPath}`;
if (this.valueFromPath(translationPath, thing)) {
return this.valueFromPath(translationPath, thing);
}
// when translation is given in nested meta thing
const nestedType = this.valueFromPath(field.replace(keyPath, '.type'), thing) as SCThingType;
translationPath = `fieldValueTranslations.${this.language}${keyPath}`;
translation = this.valueFromPath(translationPath, this.getMetaClassInstance(nestedType));
if (translation instanceof Object) { // lookup translated keys in meta thing property
const translations: string[] = [];
this.valueFromPath(field, thing).forEach((key: string) => {
translationPath = `fieldValueTranslations.${this.language}${keyPath}.${key}`;
translations.push(this.valueFromPath(translationPath, this.getMetaClassInstance(nestedType)));
});
return translations;
}
if (!translation) { // translation not given, return as is
return this.valueFromPath(field, thing) as string;
}
return translation;
} }
// accessing meta thing primitive value depth = 0 if (typeof metaClass.fieldValueTranslations[language] !== 'undefined') {
translationPath = `fieldValueTranslations.${this.language}.${field}`; Object.keys(metaClass.fieldValueTranslations[language]).forEach((key) => {
translation = this.valueFromPath(translationPath, this.getMetaClassInstance(thing.type)); if (metaClass.fieldValueTranslations[language][key] instanceof Object) {
if (translation) { // Assigns known translations of subproperties to property in given language (e.g. categories)
if (translation instanceof Object) { // lookup translated keys in meta thing property Object.keys((instance as any)[key]).forEach((subKey) => {
const translations: string[] = []; (instance as any)[key][subKey] =
this.valueFromPath(field, thing).forEach((key: string) => { metaClass.fieldValueTranslations[language][key][(instance as any)[key][subKey]];
translationPath = `fieldValueTranslations.${this.language}.${field}.${key}`; });
translations.push(this.valueFromPath(translationPath, this.getMetaClassInstance(thing.type))); } else {
}); // Assigns property to known translation of fieldValueTranslations in given language
return translations; (instance as any)[key] = metaClass.fieldValueTranslations[language][key];
} }
return translation; });
} }
return instance;
// accessing meta thing primitive via iteratable index value depth = 0
translation = this.valueFromPath(translationPath.replace(
/\[(?=[^\[]*$).*|(?=[\d+]*$).*/, '[' + this.valueFromPath(field, thing) + ']'),
this.getMetaClassInstance(thing.type));
if (translation) {
return translation;
}
// last resort: return as is
return this.valueFromPath(field, thing) as string;
} }
/** /**
@@ -261,33 +180,27 @@ export class SCThingTranslator {
* @param data Top level object that gets passed through the recursion * @param data Top level object that gets passed through the recursion
* @returns an OCType<T> object allowing for access to translations or a translated value(s) * @returns an OCType<T> object allowing for access to translations or a translated value(s)
*/ */
public translate<T extends SCThing>(data?: T): OCType<T> { public translate<T extends SCThing>(data: T): OCType<T> {
return new Proxy( return new Proxy(
((defaultValue?: Defined<T>) => (data == null ? defaultValue : data)) as OCType<T>, ((defaultValue?: Defined<T>) => (data == null ? defaultValue : data)) as OCType<T>,
{ {
get: (target, key) => { get: (target, key) => {
const obj: any = target(); const obj: any = target();
let translatable = obj[key]; const objTranslatedFromCache = this.cache.get(data);
if (obj[key] instanceof Array && obj[key].length) { if (typeof objTranslatedFromCache !== 'undefined') {
translatable = obj[key][0]; return this.deeptranslate((objTranslatedFromCache as any)[key]);
if (typeof obj[key][0] === 'object' && !obj[key][0].origin) {
translatable = obj[key][0][Object.keys(obj[key][0])[0]];
}
} }
if (typeof translatable === 'string') { const objTranslated = this.translateWholeThingDestructively(clone(obj));
// retrieve final translation this.cache.putObject(objTranslated);
return this.deeptranslate(data!, this.getFieldValueTranslation(data!, key.toString()), key.toString()); return this.deeptranslate(objTranslated[key]);
}
// recursion to get more calls to the Proxy handler 'get()' (key path not complete)
return this.deeptranslate(data!, obj[key], key.toString());
}, },
}, },
); );
} }
/** /**
* Given a SCThingType this function returns an object with the same basic structure as the corresponding SCThing. * Given a SCThingType this function returns an object with the same basic structure as the corresponding SCThing
* All the values will be set to the known translations of the property/key name. * All the values will be set to the known translations of the property/key name
* @example * @example
* const translatedMetaDish = translator.translatedPropertyNames<SCCourseOfStudies>(SCThingType.CourseOfStudies); * const translatedMetaDish = translator.translatedPropertyNames<SCCourseOfStudies>(SCThingType.CourseOfStudies);
* @param language The language the object is translated to * @param language The language the object is translated to
@@ -300,4 +213,129 @@ export class SCThingTranslator {
// return {...{}, ...this.getAllMetaFieldTranslations(thing.type, targetLanguage) as T}; // return {...{}, ...this.getAllMetaFieldTranslations(thing.type, targetLanguage) as T};
return this.getAllMetaFieldTranslations(thing.type, targetLanguage) as T; return this.getAllMetaFieldTranslations(thing.type, targetLanguage) as T;
} }
/**
* Recursively translates the given object in-place
* Translated values overwrite current values (destructive)
*
* @param language The language the thing is translated to
* @param thing The thing that will be translated
* @returns The thing translated
*/
public translateWholeThingDestructively(instance: any,
language?: keyof SCTranslations<any>): any {
const targetLanguage = (language) ? language : this.language;
// Recursively call this function on all nested SCThings, arrays and objects
Object.keys(instance).forEach((key) => {
if (
isThing((instance as any)[key]) ||
instance[key] instanceof Array ||
instance[key] instanceof Object) {
instance[key] = this.translateWholeThingDestructively(instance[key], targetLanguage);
}
});
// Spread variable translations given by the connector into thing
if (typeof instance.translations !== 'undefined') {
if (typeof instance.translations![targetLanguage] !== 'undefined') {
instance = {...instance, ...instance.translations![targetLanguage]} as typeof instance;
}
}
// Spread known translations from meta classes into (partly) translated thing
this.replaceAvailableMetaFieldValueTranslations(instance, targetLanguage);
return instance;
}
}
/**
* LRUCache class
* Small last recently used cache intended to get used by SCThingTranslator
*/
class LRUCache<T> {
/**
* Map property that manages cached content
*/
private entries: Map<string, T> = new Map<string, T>();
/**
* Property representing cache maximum capacity
*/
private maxEntries: number;
/**
* @constructor
* @example
* // returns LRUCache instance with a maximum capacity of 500
* new LRUCache(500);
*/
constructor(maxEntries: number) {
this.maxEntries = maxEntries;
}
/**
* Flushes cache / removes all entries
*/
public flush() {
this.entries.clear();
}
/**
* Get content from cache by key
*/
public get(somethingOrKey: string): T | undefined;
/**
* Get content from cache by another objects uid
*/
public get<U extends SCThing>(something: U): T | undefined;
/**
* Get content from cache by key or by another objects uid
*
* @param somethingOrKey The key which maps to the cached content or an object for which content has been cached
* @returns If available the content connected to the key or somethingOrKey.uid property
*/
public get<U extends SCThing>(somethingOrKey: string | U): T | undefined {
let key: string;
if (typeof somethingOrKey === 'string') {
key = somethingOrKey;
} else if (isThing(somethingOrKey)) {
key = somethingOrKey.uid;
} else {
throw new Error(`Passed argument ${somethingOrKey} cannot be key in LRUCache`);
}
const entry = this.entries.get(key);
if (entry) {
// LRU behavior
this.entries.delete(key);
this.entries.set(key, entry);
}
return entry;
}
/**
* Place content in cache by key
*
* @param key The key for which content should be cached
* @param content The content that should be cached
*/
public put(key: string, content: T) {
if (this.entries.size >= this.maxEntries) {
// LRU behavior
const keyToDelete = this.entries.keys().next().value;
this.entries.delete(keyToDelete);
}
this.entries.set(key, content);
}
/**
* Place content in cache by another objects uid
*
* @param something The object that should be cached under something.uid
*/
public putObject<U extends SCThing>(something: U) {
this.put(something.uid, (something as any) as T);
}
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018-2019 StApps * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -16,8 +16,8 @@ import {SCThing, SCThingTranslatableProperties} from '../Thing';
import {SCOrganizationWithoutReferences} from '../things/Organization'; import {SCOrganizationWithoutReferences} from '../things/Organization';
import {SCPersonWithoutReferences} from '../things/Person'; import {SCPersonWithoutReferences} from '../things/Person';
import {SCTranslations} from '../types/i18n'; import {SCTranslations} from '../types/i18n';
import {SCInPlace} from '../types/Places';
import {SCISO8601Date} from '../types/Time'; import {SCISO8601Date} from '../types/Time';
import {SCInPlace} from './../types/Places';
/** /**
* Default price without distinction * Default price without distinction
@@ -50,7 +50,17 @@ export interface SCAcademicPriceGroup extends SCPriceGroup {
} }
/** /**
* A thing without references that has a price tag * A thing without references that can be offered
*/
export interface SCThingThatCanBeOfferedWithoutReferences extends SCThing {
/**
* Translations of a thing that can be offered
*/
translations?: SCTranslations<SCThingThatCanBeOfferedTranslatableProperties>;
}
/**
* A thing that can be offered
*/ */
export interface SCThingThatCanBeOffered<T extends SCPriceGroup> export interface SCThingThatCanBeOffered<T extends SCPriceGroup>
extends SCThing { extends SCThing {
@@ -58,11 +68,6 @@ export interface SCThingThatCanBeOffered<T extends SCPriceGroup>
* List of offers for that thing * List of offers for that thing
*/ */
offers?: Array<SCThingThatCanBeOfferedOffer<T>>; offers?: Array<SCThingThatCanBeOfferedOffer<T>>;
/**
* Translations of a thing that can be offered
*/
translations?: SCTranslations<SCThingThatCanBeOfferedTranslatableProperties>;
} }
/** /**

View File

@@ -13,6 +13,7 @@
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {SCAbstractRoute, SCRouteHttpVerbs} from '../../../Route'; import {SCAbstractRoute, SCRouteHttpVerbs} from '../../../Route';
import {SCSearchContext} from '../../../types/config/Backend';
import {SCSearchFilter} from '../../../types/filters/Abstract'; import {SCSearchFilter} from '../../../types/filters/Abstract';
import {SCSearchSort} from '../../../types/sorts/Abstract'; import {SCSearchSort} from '../../../types/sorts/Abstract';
import { import {
@@ -37,7 +38,12 @@ export interface SCSearchRequest extends SCSearchQuery {
*/ */
export interface SCSearchQuery { export interface SCSearchQuery {
/** /**
* A filter structure that combines any number of filters with boolean methods ('AND', 'OR', 'NOT') * The context name from where the search query was initiated
*/
context?: SCSearchContext;
/**
* A filter structure that combines any number of filters with boolean methods ('AND', 'OR', 'NOT')
*/ */
filter?: SCSearchFilter; filter?: SCSearchFilter;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018 StApps * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -13,9 +13,12 @@
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {SCAcademicDegree, SCAcademicDegreeMeta} from '../base/AcademicDegree'; import {SCAcademicDegree, SCAcademicDegreeMeta} from '../base/AcademicDegree';
import {SCAcademicPriceGroup, import {
SCThingThatCanBeOffered, SCAcademicPriceGroup,
SCThingThatCanBeOfferedTranslatableProperties} from '../base/ThingThatCanBeOffered'; SCThingThatCanBeOffered,
SCThingThatCanBeOfferedTranslatableProperties,
SCThingThatCanBeOfferedWithoutReferences,
} from '../base/ThingThatCanBeOffered';
import {SCThingMeta, SCThingType} from '../Thing'; import {SCThingMeta, SCThingType} from '../Thing';
import {SCLanguage, SCMetaTranslations, SCTranslations} from '../types/i18n'; import {SCLanguage, SCMetaTranslations, SCTranslations} from '../types/i18n';
import {SCDateSeriesWithoutReferences} from './DateSeries'; import {SCDateSeriesWithoutReferences} from './DateSeries';
@@ -24,9 +27,8 @@ import {SCOrganization} from './Organization';
/** /**
* A course of studies without references * A course of studies without references
*/ */
export interface SCCourseOfStudiesWithoutReferences extends export interface SCCourseOfStudiesWithoutReferences extends SCAcademicDegree,
SCAcademicDegree, SCThingThatCanBeOfferedWithoutReferences {
SCThingThatCanBeOffered<SCAcademicPriceGroup> {
/** /**
* The main language in which the course of studies * The main language in which the course of studies
* is beeing offered * is beeing offered
@@ -64,14 +66,15 @@ export interface SCCourseOfStudiesWithoutReferences extends
* *
* @validatable * @validatable
*/ */
export interface SCCourseOfStudies extends SCCourseOfStudiesWithoutReferences { export interface SCCourseOfStudies extends SCCourseOfStudiesWithoutReferences,
SCThingThatCanBeOffered<SCAcademicPriceGroup> {
/** /**
* The department that manages the course of studies * The department that manages the course of studies
*/ */
department: SCOrganization; department: SCOrganization;
/** /**
* The secretary that administers requests and * The secretary that administers requests and
* questions concerning the course of studies * questions concerning the course of studies
*/ */
secretary: SCOrganization; secretary: SCOrganization;
@@ -81,6 +84,11 @@ export interface SCCourseOfStudies extends SCCourseOfStudiesWithoutReferences {
*/ */
startDates?: SCDateSeriesWithoutReferences[]; startDates?: SCDateSeriesWithoutReferences[];
/**
* Translated fields of a dish
*/
translations?: SCTranslations<SCCourseOfStudiesTranslatableProperties>;
/** /**
* Type of the course of studies * Type of the course of studies
*/ */
@@ -88,7 +96,7 @@ export interface SCCourseOfStudies extends SCCourseOfStudiesWithoutReferences {
} }
export interface SCCourseOfStudiesTranslatableProperties export interface SCCourseOfStudiesTranslatableProperties
extends SCThingThatCanBeOfferedTranslatableProperties { extends SCThingThatCanBeOfferedTranslatableProperties {
} }
/** /**
@@ -100,19 +108,19 @@ export class SCCourseOfStudiesMeta extends SCThingMeta implements SCMetaTranslat
*/ */
fieldTranslations = { fieldTranslations = {
de: { de: {
... SCAcademicDegreeMeta.getInstance().fieldTranslations.de, ...SCAcademicDegreeMeta.getInstance().fieldTranslations.de,
}, },
en: { en: {
... SCAcademicDegreeMeta.getInstance().fieldTranslations.en, ...SCAcademicDegreeMeta.getInstance().fieldTranslations.en,
}, },
}; };
/** /**
* Translations of values of fields * Translations of values of fields
*/ */
fieldValueTranslations = { fieldValueTranslations = {
de: { de: {
... SCAcademicDegreeMeta.getInstance().fieldValueTranslations.de, ...SCAcademicDegreeMeta.getInstance().fieldValueTranslations.de,
modes: { modes: {
combination: 'Kombinationsstudiengang', combination: 'Kombinationsstudiengang',
dual: 'Dualer Studiengang', dual: 'Dualer Studiengang',
@@ -122,7 +130,7 @@ export class SCCourseOfStudiesMeta extends SCThingMeta implements SCMetaTranslat
type: 'Studiengang', type: 'Studiengang',
}, },
en: { en: {
... SCAcademicDegreeMeta.getInstance().fieldValueTranslations.en, ...SCAcademicDegreeMeta.getInstance().fieldValueTranslations.en,
academicDegree: 'Hochschulabschluss', academicDegree: 'Hochschulabschluss',
department: 'Fachbereich', department: 'Fachbereich',
major: 'Studienfach', major: 'Studienfach',
@@ -136,13 +144,13 @@ export class SCCourseOfStudiesMeta extends SCThingMeta implements SCMetaTranslat
/** /**
* Types of (german) course of studies modes * Types of (german) course of studies modes
*/ */
export type SCCourseOfStudiesMode = 'combination' | export type SCCourseOfStudiesMode = 'combination'
'dual' | | 'dual'
'double-degree' | | 'double-degree'
'standard' ; | 'standard' ;
/** /**
* Types of (german) course of studies time modes * Types of (german) course of studies time modes
*/ */
export type SCCourseOfStudiesTimeMode = 'fulltime' | export type SCCourseOfStudiesTimeMode = 'fulltime'
'parttime' ; | 'parttime' ;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018 StApps * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -17,6 +17,7 @@ import {
SCAcademicPriceGroup, SCAcademicPriceGroup,
SCThingThatCanBeOffered, SCThingThatCanBeOffered,
SCThingThatCanBeOfferedTranslatableProperties, SCThingThatCanBeOfferedTranslatableProperties,
SCThingThatCanBeOfferedWithoutReferences,
} from '../base/ThingThatCanBeOffered'; } from '../base/ThingThatCanBeOffered';
import {SCThingMeta, SCThingType} from '../Thing'; import {SCThingMeta, SCThingType} from '../Thing';
import {SCMetaTranslations, SCTranslations} from '../types/i18n'; import {SCMetaTranslations, SCTranslations} from '../types/i18n';
@@ -38,8 +39,7 @@ export interface SCSportCoursePriceGroup extends SCAcademicPriceGroup {
/** /**
* A date without references * A date without references
*/ */
export interface SCDateSeriesWithoutReferences export interface SCDateSeriesWithoutReferences extends SCThingThatCanBeOfferedWithoutReferences {
extends SCThingThatCanBeOffered<SCSportCoursePriceGroup> {
/** /**
* Dates of the date series that are initially planned to be held * Dates of the date series that are initially planned to be held
*/ */
@@ -76,7 +76,9 @@ export interface SCDateSeriesWithoutReferences
* *
* @validatable * @validatable
*/ */
export interface SCDateSeries extends SCDateSeriesWithoutReferences, SCThingInPlace { export interface SCDateSeries extends SCDateSeriesWithoutReferences,
SCThingInPlace,
SCThingThatCanBeOffered<SCSportCoursePriceGroup> {
/** /**
* Event to which the date series belongs * Event to which the date series belongs
*/ */
@@ -108,26 +110,26 @@ export class SCDateSeriesMeta extends SCThingMeta implements SCMetaTranslations<
*/ */
fieldTranslations = { fieldTranslations = {
de: { de: {
... SCThingInPlaceMeta.getInstance().fieldTranslations.de, ...SCThingInPlaceMeta.getInstance().fieldTranslations.de,
}, },
en: { en: {
... SCThingInPlaceMeta.getInstance().fieldTranslations.en, ...SCThingInPlaceMeta.getInstance().fieldTranslations.en,
}, },
}; };
/** /**
* Translations of values of fields * Translations of values of fields
*/ */
fieldValueTranslations = { fieldValueTranslations = {
de: { de: {
... SCThingInPlaceMeta.getInstance().fieldValueTranslations.de, ...SCThingInPlaceMeta.getInstance().fieldValueTranslations.de,
dates: 'Einzeltermine', dates: 'Einzeltermine',
duration: 'Dauer', duration: 'Dauer',
frequency: 'Wiederholung', frequency: 'Wiederholung',
type: 'Wiederholungreihe', type: 'Wiederholungreihe',
}, },
en: { en: {
... SCThingInPlaceMeta.getInstance().fieldValueTranslations.en, ...SCThingInPlaceMeta.getInstance().fieldValueTranslations.en,
type: SCThingType.DateSeries, type: SCThingType.DateSeries,
}, },
}; };

View File

@@ -64,11 +64,6 @@ export interface SCDiff extends SCDiffWithoutReferences {
* Meta information about a diff * Meta information about a diff
*/ */
export class SCDiffMeta extends SCThingMeta implements SCMetaTranslations<SCDiff> { export class SCDiffMeta extends SCThingMeta implements SCMetaTranslations<SCDiff> {
/**
* Set type definiton for singleton instance
*/
protected static _instance: SCThingMeta;
/** /**
* Translations of fields * Translations of fields
*/ */

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018-2019 StApps * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -16,6 +16,7 @@ import {
SCAcademicPriceGroup, SCAcademicPriceGroup,
SCThingThatCanBeOffered, SCThingThatCanBeOffered,
SCThingThatCanBeOfferedTranslatableProperties, SCThingThatCanBeOfferedTranslatableProperties,
SCThingThatCanBeOfferedWithoutReferences,
} from '../base/ThingThatCanBeOffered'; } from '../base/ThingThatCanBeOffered';
import { import {
SCThingWithCategoriesSpecificValues, SCThingWithCategoriesSpecificValues,
@@ -29,12 +30,8 @@ import {SCMetaTranslations, SCTranslations} from '../types/i18n';
/** /**
* A dish without references * A dish without references
*/ */
export interface SCDishWithoutReferences export interface SCDishWithoutReferences extends SCThingThatCanBeOfferedWithoutReferences,
extends SCThingWithCategoriesWithoutReferences< SCThingWithCategoriesWithoutReferences<SCDishCategories, SCThingWithCategoriesSpecificValues> {
SCDishCategories,
SCThingWithCategoriesSpecificValues
>,
SCThingThatCanBeOffered<SCAcademicPriceGroup> {
/** /**
* Additives of the dish * Additives of the dish
*/ */
@@ -71,7 +68,8 @@ export interface SCDishWithoutReferences
* *
* @validatable * @validatable
*/ */
export interface SCDish extends SCDishWithoutReferences { export interface SCDish extends SCDishWithoutReferences,
SCThingThatCanBeOffered<SCAcademicPriceGroup> {
/** /**
* Dishes ("Beilagen") that are served with the dish (if only certain supplement dishes can be taken with a dish) * Dishes ("Beilagen") that are served with the dish (if only certain supplement dishes can be taken with a dish)
*/ */
@@ -173,22 +171,22 @@ export class SCDishMeta extends SCThingMeta implements SCMetaTranslations<SCDish
*/ */
fieldTranslations = { fieldTranslations = {
de: { de: {
... SCThingWithCategoriesWithoutReferencesMeta.getInstance<SCDishCategories, ...SCThingWithCategoriesWithoutReferencesMeta.getInstance<SCDishCategories,
SCThingWithCategoriesSpecificValues>().fieldTranslations.de, SCThingWithCategoriesSpecificValues>().fieldTranslations.de,
}, },
en: { en: {
... SCThingWithCategoriesWithoutReferencesMeta.getInstance<SCDishCategories, ...SCThingWithCategoriesWithoutReferencesMeta.getInstance<SCDishCategories,
SCThingWithCategoriesSpecificValues>().fieldTranslations.en, SCThingWithCategoriesSpecificValues>().fieldTranslations.en,
}, },
}; };
/** /**
* Translations of values of fields * Translations of values of fields
*/ */
fieldValueTranslations = { fieldValueTranslations = {
de: { de: {
... SCThingWithCategoriesWithoutReferencesMeta.getInstance<SCDishCategories, ...SCThingWithCategoriesWithoutReferencesMeta.getInstance<SCDishCategories,
SCThingWithCategoriesSpecificValues>().fieldValueTranslations.de, SCThingWithCategoriesSpecificValues>().fieldValueTranslations.de,
categories: { categories: {
appetizer: 'Vorspeise', appetizer: 'Vorspeise',
dessert: 'Nachtisch', dessert: 'Nachtisch',
@@ -200,8 +198,8 @@ export class SCDishMeta extends SCThingMeta implements SCMetaTranslations<SCDish
type: 'Essen', type: 'Essen',
}, },
en: { en: {
... SCThingWithCategoriesWithoutReferencesMeta.getInstance<SCDishCategories, ...SCThingWithCategoriesWithoutReferencesMeta.getInstance<SCDishCategories,
SCThingWithCategoriesSpecificValues>().fieldValueTranslations.en, SCThingWithCategoriesSpecificValues>().fieldValueTranslations.en,
type: SCThingType.Dish, type: SCThingType.Dish,
}, },
}; };

View File

@@ -28,11 +28,14 @@ export type SCSettingCategories = string;
*/ */
export interface SCSettingWithoutReferences export interface SCSettingWithoutReferences
extends SCThingWithCategoriesWithoutReferences<SCSettingCategories, SCThingWithCategoriesSpecificValues> { extends SCThingWithCategoriesWithoutReferences<SCSettingCategories, SCThingWithCategoriesSpecificValues> {
/** /**
* The type of input/value this setting is carrying * The default value of a setting
*/ */
input: SCSettingInputType; defaultValue: SCSettingValue | SCSettingValues;
/**
* The input type of this setting
*/
inputType: SCSettingInputType;
/** /**
* The order number this setting should show up in its category list * The order number this setting should show up in its category list
*/ */
@@ -45,16 +48,26 @@ export interface SCSettingWithoutReferences
* The type of this model * The type of this model
*/ */
type: SCThingType.Setting; type: SCThingType.Setting;
/**
* The key of a value of a setting
*/
value?: SCSettingValue | SCSettingValues;
/**
* The possible values of a setting
*/
values?: SCSettingValues;
} }
/** /**
* The types of input/value a setting object can carry * The types of input/value a setting object can carry
*/ */
export type SCSettingInputType = SCSettingSingleChoice export enum SCSettingInputType {
| SCSettingMultipleChoice SingleChoice = 'single choice',
| SCSettingNumber MultipleChoice = 'multiple choice',
| SCSettingText Number = 'number',
| SCSettingPassword; Text = 'text',
Password = 'password',
}
/** /**
* A setting with references * A setting with references
@@ -69,58 +82,23 @@ export interface SCSetting extends SCSettingWithoutReferences {
} }
/** /**
* Input type with single choice as value * The type a value of a setting can have
*/ */
export interface SCSettingSingleChoice {
defaultValue: SCSettingValue;
inputType: 'singleChoice';
value?: SCSettingValue;
values: SCSettingValue[];
}
/**
* Input type with multiple choice as value
*/
export interface SCSettingMultipleChoice {
defaultValue: SCSettingValue[];
inputType: 'multipleChoice';
value?: SCSettingValue[];
values: SCSettingValue[];
}
export type SCSettingValue = string | number | boolean; export type SCSettingValue = string | number | boolean;
/** /**
* Input type with number as value * The type of multiple values a setting can have
*/ */
export interface SCSettingNumber { export type SCSettingValues = SCSettingValue[];
defaultValue: number;
inputType: 'number';
value?: number;
}
/**
* Input type with text as value
*/
export interface SCSettingText {
defaultValue: string;
inputType: 'text';
value?: string;
}
/**
* Input type with secret text (eq. password) as value
*/
export interface SCSettingPassword {
defaultValue: string;
inputType: 'password';
value?: string;
}
/** /**
* Translatable properties of a setting * Translatable properties of a setting
*/ */
export interface SCSettingValueTranslatableProperties extends SCThingWithCategoriesTranslatableProperties { export interface SCSettingValueTranslatableProperties extends SCThingWithCategoriesTranslatableProperties {
/**
* The translations of the possible values of a setting
*/
values?: string[];
} }
/** /**
@@ -135,11 +113,17 @@ export class SCSettingMeta extends SCThingMeta implements SCMetaTranslations<SCS
// tslint:disable-next-line:max-line-length // tslint:disable-next-line:max-line-length
... SCThingWithCategoriesWithoutReferencesMeta.getInstance<SCSettingCategories, ... SCThingWithCategoriesWithoutReferencesMeta.getInstance<SCSettingCategories,
SCThingWithCategoriesSpecificValues>().fieldTranslations.de, SCThingWithCategoriesSpecificValues>().fieldTranslations.de,
defaultValue: 'Standard Wert',
inputType: 'Eingabetyp',
value: 'Wert',
values: 'Werte',
}, },
en: { en: {
// tslint:disable-next-line:max-line-length // tslint:disable-next-line:max-line-length
... SCThingWithCategoriesWithoutReferencesMeta.getInstance<SCSettingCategories, ... SCThingWithCategoriesWithoutReferencesMeta.getInstance<SCSettingCategories,
SCThingWithCategoriesSpecificValues>().fieldTranslations.en, SCThingWithCategoriesSpecificValues>().fieldTranslations.en,
defaultValue: 'default value',
inputType: 'input type',
}, },
}; };
@@ -151,6 +135,13 @@ export class SCSettingMeta extends SCThingMeta implements SCMetaTranslations<SCS
// tslint:disable-next-line:max-line-length // tslint:disable-next-line:max-line-length
... SCThingWithCategoriesWithoutReferencesMeta.getInstance<SCSettingCategories, ... SCThingWithCategoriesWithoutReferencesMeta.getInstance<SCSettingCategories,
SCThingWithCategoriesSpecificValues>().fieldValueTranslations.de, SCThingWithCategoriesSpecificValues>().fieldValueTranslations.de,
inputType: {
'multiple choice': 'mehrfach Auswahl',
number: 'Zahl',
password: 'Passwort',
'single choice': 'einfache Auswahl',
text: 'Text',
},
type: 'Einstellung', type: 'Einstellung',
}, },
en: { en: {

View File

@@ -0,0 +1,171 @@
/*
* Copyright (C) 2018-2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {SCAcademicPriceGroup,
SCThingThatCanBeOffered,
SCThingThatCanBeOfferedTranslatableProperties} from '../base/ThingThatCanBeOffered';
import {SCThingMeta, SCThingType} from '../Thing';
import {SCLanguage, SCMetaTranslations, SCTranslations} from '../types/i18n';
import {SCMap} from '../types/Map';
import {SCAcademicEventWithoutReferences} from './AcademicEvent';
import {SCOrganizationWithoutReferences} from './Organization';
import {SCPersonWithoutReferences} from './Person';
/**
* A study module without references
*/
export interface SCStudyModuleWithoutReferences extends
SCThingThatCanBeOffered<SCAcademicPriceGroup> {
/**
* ECTS points (European Credit Transfer System)
*/
ects: number;
/**
* The language in which the study module is offered
*/
language: SCLanguage;
/**
* Majors that this study module is meant for
*/
majors: string[];
/**
* Represents the modules necessity for each given major (of the major property)
*/
necessity: SCMap<SCStudyModuleNecessity>;
/**
* Translated fields of a study module
*/
translations?: SCTranslations<SCStudyModuleTranslatableProperties>;
/**
* Type of the study module
*/
type: SCThingType.StudyModule;
}
/**
* A study module
*
* @validatable
*/
export interface SCStudyModule extends SCStudyModuleWithoutReferences {
/**
* Academic events that make up a study module
*/
academicEvents: SCAcademicEventWithoutReferences[];
/**
* The faculty that manages and curates the study module
*/
faculty: SCOrganizationWithoutReferences;
/**
* Study modules needed for each others fulfillment
*/
partnerModules?: SCStudyModuleWithoutReferences[];
/**
* Study modules required beforehand
*/
requiredModules?: SCStudyModuleWithoutReferences[];
/**
* The secretary that administers requests and
* questions concerning the study module by eg. students
*/
secretary: SCOrganizationWithoutReferences | SCPersonWithoutReferences;
}
export interface SCStudyModuleTranslatableProperties
extends SCThingThatCanBeOfferedTranslatableProperties {
/**
* Translations of the majors that this study module is meant for
*/
majors?: string[];
/**
* Translations of the modules necessity for each given major (of the major property)
*/
necessity: SCMap<SCStudyModuleNecessity>;
}
/**
* Represents a modules necessity (in a major) as it may be required, optional or
* is in a pool of n optional modules were m out of them have to be taken/completed.
* Hence the elective option.
*/
export enum SCStudyModuleNecessity {
Required = 'required',
Elective = 'elective',
Optional = 'optional',
}
/**
* Study module meta data
*/
export class SCStudyModuleMeta extends SCThingMeta implements SCMetaTranslations<SCStudyModule> {
/**
* Translations of fields
*/
fieldTranslations = {
de: {
... SCThingMeta.getInstance().fieldTranslations.de,
academicEvents: 'Veranstaltungen',
ects: 'ECTS-Punkte',
faculty: 'Fachbereich',
language: 'Unterrichtssprache',
majors: 'Fachrichtungen',
necessity: 'Erforderlichkeit',
partnerModules: 'Partnermodule',
requiredModules: 'Benötigte Module',
secretary: 'Sekretariat',
},
en: {
... SCThingMeta.getInstance().fieldTranslations.en,
academicEvents: 'academic events',
ects: 'ECTS points',
faculty: 'faculty',
language: 'teaching language',
majors: 'majors',
necessity: 'necessity',
partnerModules: 'partner modules',
requiredModules: 'required modules',
secretary: 'secretary',
},
};
/**
* Translations of values of fields
*/
fieldValueTranslations = {
de: {
... SCThingMeta.getInstance().fieldValueTranslations.de,
necessity: {
'elective' : 'Wahlfach',
'optional' : 'optional',
'required' : 'benötigt',
},
type: 'Studiengangmodul',
},
en: {
... SCThingMeta.getInstance().fieldValueTranslations.en,
type: SCThingType.StudyModule,
},
};
}

View File

@@ -15,10 +15,10 @@
/** /**
* Capsulation for a map with a string as key with values of type `T` * Capsulation for a map with a string as key with values of type `T`
* *
* !!! BEWARE !!! * !!! BEWARE !!!
* Can't be refactored to a `Map<K, V>`, because it can't be serialized via JSON.stringify(map) * Can't be refactored to a `Map<K, V>`, because it can't be serialized via JSON.stringify(map)
* *
* @typeparam T Can be any type. * @typeparam T Can be any type.
*/ */
export interface SCMap<T> { export interface SCMap<T> {
@@ -27,3 +27,20 @@ export interface SCMap<T> {
*/ */
[key: string]: T; [key: string]: T;
} }
/**
* Restricted map with keys, limited to values of `U`, and corresponding values of type `T`
*
* !!! BEWARE !!!
* Can't be refactored to a `Map<K, V>`, because it can't be serialized via JSON.stringify(map)
* Also note, that this is a type not an interface
*
* @typeparam U Must be a type the `in` operator can be applied to and contains only strings or numbers
* @typeparam T Can be any type
*/
export type SCRestrictedMap<U extends string | number, T> = {
/**
* One value for each key
*/
[key in U]: T
};

View File

@@ -15,7 +15,7 @@
import {SCThingType} from '../../Thing'; import {SCThingType} from '../../Thing';
import {SCSearchSortType} from '../sorts/Abstract'; import {SCSearchSortType} from '../sorts/Abstract';
import {SCUuid} from '../UUID'; import {SCUuid} from '../UUID';
import {SCMap} from './../Map'; import {SCMap, SCRestrictedMap} from './../Map';
import {SCMonitoringConfiguration} from './Monitoring'; import {SCMonitoringConfiguration} from './Monitoring';
/** /**
@@ -72,7 +72,7 @@ export interface SCBackendConfigurationSortableField {
/** /**
* A list of SC types on which this field exists * A list of SC types on which this field exists
* *
* If no type is given it is assumed it exists on every type * If no type is given it is assumed it exists on every type
*/ */
onlyOnTypes?: SCThingType[]; onlyOnTypes?: SCThingType[];
@@ -84,10 +84,27 @@ export interface SCBackendConfigurationSortableField {
} }
/**
* Possible context names to be used by the search request
*/
export type SCSearchContext =
| 'default'
| 'dining'
| 'place';
/**
* A boosting configuration for one context
*/
export type SCBackendConfigurationSearchBoostingContext =
SCRestrictedMap<
SCSearchContext,
SCBackendConfigurationSearchBoostingType[]
>;
/** /**
* A boosting configuration for one SCType * A boosting configuration for one SCType
*/ */
export interface SCBackendConfigurationSearchBoosting { export interface SCBackendConfigurationSearchBoostingType {
/** /**
* The factor of which the scores matching this type should be multiplied by * The factor of which the scores matching this type should be multiplied by
@@ -98,7 +115,7 @@ export interface SCBackendConfigurationSearchBoosting {
* Outer-Map: * Outer-Map:
* Fields of this type that should be boosted if they match a given value * Fields of this type that should be boosted if they match a given value
* For nest fields you can use the `.` as a separator. For example `academicTerms.acronym` * For nest fields you can use the `.` as a separator. For example `academicTerms.acronym`
* *
* Inner-map: * Inner-map:
* Value of the field that should be boosted by the given number * Value of the field that should be boosted by the given number
* For example `"SS 2019": 2` * For example `"SS 2019": 2`
@@ -143,7 +160,7 @@ export interface SCBackendInternalConfiguration {
* The resulting scores of matching objects can be boosted (multiplied by a number) to change the order in the * The resulting scores of matching objects can be boosted (multiplied by a number) to change the order in the
* set of results * set of results
*/ */
boostings: SCBackendConfigurationSearchBoosting[]; boostings: SCBackendConfigurationSearchBoostingContext;
/** /**
* Configuration of the database * Configuration of the database

View File

@@ -13,11 +13,12 @@
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {expect} from 'chai'; import {expect} from 'chai';
import clone = require('fast-clone');
import {slow, suite, test, timeout} from 'mocha-typescript'; import {slow, suite, test, timeout} from 'mocha-typescript';
import {SCThingOriginType, SCThingType} from '../src/core/Thing'; import {SCThingOriginType, SCThingType} from '../src/core/Thing';
import {SCBuildingWithoutReferences} from '../src/core/things/Building'; import {SCBuildingWithoutReferences} from '../src/core/things/Building';
import {SCDish, SCDishMeta} from '../src/core/things/Dish'; import {SCDish, SCDishMeta} from '../src/core/things/Dish';
import {SCPerson} from '../src/core/things/Person';
import {SCThingTranslator} from '../src/core/Translator'; import {SCThingTranslator} from '../src/core/Translator';
const building: SCBuildingWithoutReferences = { const building: SCBuildingWithoutReferences = {
@@ -96,29 +97,15 @@ const dish: SCDish = {
uid: '540862f3-ea30-5b8f-8678-56b4dc217140', uid: '540862f3-ea30-5b8f-8678-56b4dc217140',
}; };
const person: SCPerson = { const translator = new SCThingTranslator('de');
familyName: 'base-familyName-name',
givenName: 'base-givenName-name',
homeLocations: [building, building, building],
name : 'base-person-name',
origin: {
indexed: '1970',
name: 'ding',
type: SCThingOriginType.Remote,
},
type: SCThingType.Person,
uid: '1234',
};
const translator = new SCThingTranslator('de', 'en');
// tslint:disable-next-line:no-eval // tslint:disable-next-line:no-eval
const languageNonExistant = eval("'jp'"); const languageNonExistant = eval("'jp'");
// this will simulate a translator always utilizing the base language translations // this will simulate a translator always utilizing the base language translations
const translatorWithFallback = new SCThingTranslator(languageNonExistant); const translatorWithFallback = new SCThingTranslator(languageNonExistant);
// tslint:disable:member-ordering TranslationSpec // tslint:disable:member-ordering TranslationSpecInplace
@suite(timeout(10000), slow(5000)) @suite(timeout(10000), slow(5000))
export class TranslationSpec { export class TranslationSpecInplace {
@test @test
public directStringLiteralType() { public directStringLiteralType() {
@@ -231,134 +218,24 @@ export class TranslationSpec {
expect(translator.translate(dish).offers[0].inPlace.categories[1234]('printer')).to.equal('printer'); expect(translator.translate(dish).offers[0].inPlace.categories[1234]('printer')).to.equal('printer');
expect(translator.translate(dish).offers[0].inPlace.categories[1]('printer')).to.not.equal('printer'); expect(translator.translate(dish).offers[0].inPlace.categories[1]('printer')).to.not.equal('printer');
} }
}
// tslint:disable:member-ordering TranslationSpecByString
@suite(timeout(10000), slow(5000))
export class TranslationSpecByString {
@test @test
public directStringLiteralType() { public changingTranslatorLanguageFlushesItsLRUCache() {
expect(translator.getFieldValueTranslation(dish, 'type')).to.equal('Essen'); const translatorDE = new SCThingTranslator('de');
expect(translatorDE.translate(dish).name()).to.equal('de-dish-name');
translatorDE.language = 'en';
expect(translatorDE.translate(dish).name()).to.equal('base-dish-name');
} }
@test @test
public directStringProperty() { public forceTranslatorLRUCacheToOverflow() {
expect(translator.getFieldValueTranslation(dish, 'name')).to.equal('de-dish-name'); const translatorDE = new SCThingTranslator('de');
} // Make sure to add more elements to the translator cache than the maximum cache capacity. See Translator.ts
for (let i = 0; i < 201; i++) {
@test const anotherDish = Object.assign({}, dish);
public directArrayOfString() { anotherDish.uid = String(i);
expect(translator.getFieldValueTranslation(dish, 'characteristics')).to.deep expect(translatorDE.translate(anotherDish).name()).to.equal('de-dish-name');
.equal([{name: 'de-characteristic0'}, {name: 'de-characteristic1'}]); }
}
@test
public directArrayOfStringSubscript() {
expect(translator.getFieldValueTranslation(dish, 'characteristics[1]'))
.to.deep.equal({name: 'de-characteristic1'});
}
@test
public directMetaArrayOfString() {
expect(translator.getFieldValueTranslation(dish, 'categories')).to.deep.equal(['Hauptgericht', 'Nachtisch']);
}
@test
public directMetaArrayOfStringSubscript() {
expect(translator.getFieldValueTranslation(dish, 'categories[1]')).to.equal('Nachtisch');
}
@test
public nestedStringLiteralType() {
expect(translator.getFieldValueTranslation(dish, 'offers[0].inPlace.type')).to.equal('Gebäude');
}
@test
public nestedStringProperty() {
expect(translator.getFieldValueTranslation(dish, 'offers[0].inPlace.name')).to.equal('de-space-name');
}
@test
public nestedMetaArrayOfString() {
expect(translator.getFieldValueTranslation(dish, 'offers[0].inPlace.categories'))
.to.deep.equal(['Büro', 'Bildung']);
}
@test
public nestedMetaArrayOfStringSubscript() {
expect(translator.getFieldValueTranslation(dish, 'offers[0].inPlace.categories[1]')).to.equal('Bildung');
}
@test
public nestedArrayOfStringSubscript() {
expect(translator.getFieldValueTranslation(dish, 'offers[0].inPlace.floors[1]')).to.equal('de-floor1');
}
@test
public directStringLiteralTypeFallback() {
expect(translatorWithFallback.getFieldValueTranslation(dish, 'type')).to.equal('dish');
}
@test
public directStringPropertyFallback() {
expect(translatorWithFallback.getFieldValueTranslation(dish, 'name')).to.equal('base-dish-name');
}
@test
public directArrayOfStringSubscriptFallback() {
expect(translatorWithFallback.getFieldValueTranslation(dish, 'characteristics[1]'))
.to.deep.equal({name: 'base-characteristic1'});
}
@test
public directMetaArrayOfStringFallback() {
expect(translatorWithFallback.getFieldValueTranslation(dish, 'categories'))
.to.deep.equal(['main dish', 'dessert']);
}
@test
public directMetaArrayOfStringSubscriptFallback() {
expect(translatorWithFallback.getFieldValueTranslation(dish, 'categories[1]')).to.equal('dessert');
}
@test
public nestedStringLiteralTypeFallback() {
expect(translatorWithFallback.getFieldValueTranslation(dish, 'offers[0].inPlace.type')).to.equal('building');
}
@test
public nestedStringPropertyFallback() {
expect(translatorWithFallback.getFieldValueTranslation(dish, 'offers[0].inPlace.name')).to.equal('base-space-name');
}
@test
public nestedMetaArrayOfStringFallback() {
expect(translatorWithFallback.getFieldValueTranslation(dish, 'offers[0].inPlace.categories'))
.to.deep.equal(['office', 'education']);
}
@test
public nestedMetaArrayOfStringSubscriptFallback() {
expect(translatorWithFallback.getFieldValueTranslation(dish, 'offers[0].inPlace.categories[1]'))
.to.equal('education');
}
@test
public nestedArrayOfStringSubscriptFallback() {
expect(translatorWithFallback.getFieldValueTranslation(dish, 'offers[0].inPlace.floors[1]'))
.to.equal('base-floor1');
}
@test
public nestedArrayOfStringSubscriptUncommonFallback() {
expect(translatorWithFallback.getFieldValueTranslation(dish, 'offers[0].inPlace.floors.1')).to.equal('base-floor1');
}
@test
public nestedNestedMetaArrayOfStringSubscriptUncommonFallback() {
expect(translatorWithFallback.getFieldValueTranslation(person, 'homeLocations.1.categories.1'))
.to.equal('education');
} }
} }
@@ -376,7 +253,7 @@ export class MetaTranslationSpec {
@test @test
public thingWithoutMetaClass() { public thingWithoutMetaClass() {
const dishCopy = Object.assign({}, dish); const dishCopy = clone(dish);
const typeNonExistant = eval("(x) => x + 'typeNonExistant';"); const typeNonExistant = eval("(x) => x + 'typeNonExistant';");
// this will assign a non existant SCThingType to dishCopy // this will assign a non existant SCThingType to dishCopy
dishCopy.type = typeNonExistant(); dishCopy.type = typeNonExistant();

View File

@@ -2,6 +2,7 @@
"errorNames": [], "errorNames": [],
"instance": { "instance": {
"query": "*", "query": "*",
"context": "default",
"filter": { "filter": {
"arguments": { "arguments": {
"filters": [ "filters": [

View File

@@ -5,15 +5,13 @@
"privacy" "privacy"
], ],
"description": "This is a Description", "description": "This is a Description",
"input": { "defaultValue": "student",
"defaultValue": "student", "inputType": "single choice",
"inputType": "singleChoice", "values": [
"values": [ "student",
"student", "employee",
"employee", "guest"
"guest" ],
]
},
"name": "group", "name": "group",
"order": 0, "order": 0,
"origin": { "origin": {
@@ -21,11 +19,6 @@
"name": "Dummy", "name": "Dummy",
"type": "remote" "type": "remote"
}, },
"translations": {
"de": {
"description": "Dies ist eine Beschreibung"
}
},
"type": "setting", "type": "setting",
"uid": "c4ff2b08-be18-528e-9b09-cb8c1d18487b" "uid": "c4ff2b08-be18-528e-9b09-cb8c1d18487b"
}, },

View File

@@ -5,20 +5,18 @@
"privacy" "privacy"
], ],
"description": "This is a Description", "description": "This is a Description",
"input": { "defaultValue": [],
"defaultValue": [], "inputType": "multiple choice",
"inputType": "multipleChoice", "values": [
"values": [ 1,
1, 2,
2, 3,
3, 4,
4, 5,
5, 6,
6, 7,
7, 8
8 ],
]
},
"name": "numbers", "name": "numbers",
"order": 1, "order": 1,
"origin": { "origin": {
@@ -26,16 +24,6 @@
"name": "Dummy", "name": "Dummy",
"type": "remote" "type": "remote"
}, },
"translations": {
"de": {
"description": "Dies ist eine Beschreibung",
"name": "Nummern"
},
"en": {
"description": "This is a Description",
"name": "Numbers"
}
},
"type": "setting", "type": "setting",
"uid": "9f0c362e-0b41-532f-9e8b-a0ac373fbede" "uid": "9f0c362e-0b41-532f-9e8b-a0ac373fbede"
}, },

View File

@@ -0,0 +1,47 @@
{
"errorNames": [],
"instance": {
"categories": [
"profile"
],
"defaultValue": "en",
"description": "The language this app is going to use.",
"inputType": "single choice",
"name": "language",
"order": 0,
"origin": {
"indexed": "2018-09-11T12:30:00Z",
"name": "Dummy",
"type": "remote"
},
"translations": {
"de": {
"categories": ["Benutzer"],
"description": "Die Sprache in der die App angezeigt wird.",
"name": "Sprache",
"values": [
"English",
"German"
]
},
"en": {
"categories": [
"User"
],
"description": "The language this app is going to use.",
"name": "Language",
"values": [
"english",
"german"
]
}
},
"type": "setting",
"values": [
"en",
"de"
],
"uid": "184b717a-d020-46f5-995c-03023670cc62"
},
"schema": "SCSetting"
}