From 544d13727bedc0d5fb4c0acf73f1a1c69be8964d Mon Sep 17 00:00:00 2001 From: Liam Keegan Date: Thu, 24 Oct 2024 10:19:46 +0200 Subject: [PATCH 1/2] Use 2-char lang code as Language primary key - enforces uniqueness of languages - get languages now returns a list of these lang codes - remove langStore from frontend, instead we can now directly use the i18n-svelte library: - `$locale` for current lang_id - `$locales` for list of available lang_ids - update frontend and admin frontend accordingly - note: involves many changes to the database, probably easiest to just delete milestones.db - resolves #128 --- frontend/src/lib/client/schemas.gen.ts | 37 +- frontend/src/lib/client/types.gen.ts | 23 +- .../Admin/EditMilestoneGroupModal.svelte | 7 +- .../Admin/EditMilestoneModal.svelte | 7 +- .../Admin/EditUserQuestionModal.svelte | 15 +- .../src/lib/components/Admin/Languages.svelte | 23 +- .../components/Admin/MilestoneGroups.svelte | 7 +- .../lib/components/Admin/Translations.svelte | 24 +- .../lib/components/Admin/UserQuestions.svelte | 7 +- .../src/lib/components/LocaleChooser.svelte | 2 - frontend/src/lib/components/Milestone.svelte | 15 +- frontend/src/lib/i18n.ts | 14 +- frontend/src/lib/stores/langStore.ts | 6 - mondey_backend/openapi.json | 2 +- .../mondey_backend/databases/milestones.py | 4 +- .../src/mondey_backend/models/milestones.py | 29 +- .../src/mondey_backend/models/questions.py | 21 +- .../src/mondey_backend/routers/admin.py | 18 +- .../src/mondey_backend/routers/milestones.py | 7 +- .../src/mondey_backend/routers/utils.py | 2 +- mondey_backend/tests/conftest.py | 431 ++++++++++-------- mondey_backend/tests/routers/test_admin.py | 73 +-- .../tests/routers/test_milestones.py | 2 +- 23 files changed, 375 insertions(+), 401 deletions(-) delete mode 100644 frontend/src/lib/stores/langStore.ts diff --git a/frontend/src/lib/client/schemas.gen.ts b/frontend/src/lib/client/schemas.gen.ts index f2a5a8ec..91e69ebc 100644 --- a/frontend/src/lib/client/schemas.gen.ts +++ b/frontend/src/lib/client/schemas.gen.ts @@ -238,40 +238,16 @@ export const HTTPValidationErrorSchema = { export const LanguageSchema = { properties: { id: { - anyOf: [ - { - type: 'integer' - }, - { - type: 'null' - } - ], - title: 'Id' - }, - lang: { type: 'string', maxLength: 2, - title: 'Lang' + title: 'Id' } }, type: 'object', - required: ['lang'], + required: ['id'], title: 'Language' } as const; -export const LanguageCreateSchema = { - properties: { - lang: { - type: 'string', - maxLength: 2, - title: 'Lang' - } - }, - type: 'object', - required: ['lang'], - title: 'LanguageCreate' -} as const; - export const MilestoneAdminSchema = { properties: { id: { @@ -482,7 +458,8 @@ export const MilestoneGroupTextSchema = { lang_id: { anyOf: [ { - type: 'integer' + type: 'string', + maxLength: 2 }, { type: 'null' @@ -631,7 +608,8 @@ export const MilestoneTextSchema = { lang_id: { anyOf: [ { - type: 'integer' + type: 'string', + maxLength: 2 }, { type: 'null' @@ -843,7 +821,8 @@ export const UserQuestionTextSchema = { lang_id: { anyOf: [ { - type: 'integer' + type: 'string', + maxLength: 2 }, { type: 'null' diff --git a/frontend/src/lib/client/types.gen.ts b/frontend/src/lib/client/types.gen.ts index 2ed61cb8..31304831 100644 --- a/frontend/src/lib/client/types.gen.ts +++ b/frontend/src/lib/client/types.gen.ts @@ -66,12 +66,7 @@ export type HTTPValidationError = { }; export type Language = { - id?: number | null; - lang: string; -}; - -export type LanguageCreate = { - lang: string; + id: string; }; export type MilestoneAdmin = { @@ -132,7 +127,7 @@ export type MilestoneGroupText = { title?: string; desc?: string; group_id?: number | null; - lang_id?: number | null; + lang_id?: string | null; }; export type MilestoneGroupTextPublic = { @@ -166,7 +161,7 @@ export type MilestoneText = { obs?: string; help?: string; milestone_id?: number | null; - lang_id?: number | null; + lang_id?: string | null; }; export type MilestoneTextPublic = { @@ -220,7 +215,7 @@ export type UserQuestionText = { question?: string; options_json?: string; user_question_id?: number | null; - lang_id?: number | null; + lang_id?: string | null; options?: string; }; @@ -253,9 +248,7 @@ export type ValidationError = { type: string; }; -export type GetLanguagesResponse = { - [key: string]: number; -}; +export type GetLanguagesResponse = Array; export type GetLanguagesError = unknown; @@ -302,7 +295,7 @@ export type GetUserQuestionsResponse = Array; export type GetUserQuestionsError = unknown; export type CreateLanguageData = { - body: LanguageCreate; + body: Language; }; export type CreateLanguageResponse = Language; @@ -311,7 +304,7 @@ export type CreateLanguageError = HTTPValidationError; export type DeleteLanguageData = { path: { - language_id: number; + language_id: string; }; }; @@ -326,7 +319,7 @@ export type UpdateI18NData = { }; }; path: { - language_id: number; + language_id: string; }; }; diff --git a/frontend/src/lib/components/Admin/EditMilestoneGroupModal.svelte b/frontend/src/lib/components/Admin/EditMilestoneGroupModal.svelte index 2373f747..a9a0b82a 100644 --- a/frontend/src/lib/components/Admin/EditMilestoneGroupModal.svelte +++ b/frontend/src/lib/components/Admin/EditMilestoneGroupModal.svelte @@ -2,9 +2,8 @@ @@ -22,7 +21,6 @@ class="flex items-center" on:click={() => { locale.set(loc); - lang_id.set($languages[loc]); dropdownOpen = false; }} > diff --git a/frontend/src/lib/components/Milestone.svelte b/frontend/src/lib/components/Milestone.svelte index 4dd32ab5..c8b17bb1 100644 --- a/frontend/src/lib/components/Milestone.svelte +++ b/frontend/src/lib/components/Milestone.svelte @@ -24,8 +24,7 @@ } from '$lib/client/types.gen'; import { updateMilestoneAnswer } from '$lib/client/services.gen'; import MilestoneButton from '$lib/components/MilestoneButton.svelte'; - import { lang_id } from '$lib/stores/langStore'; - import { _ } from 'svelte-i18n'; + import { _, locale } from 'svelte-i18n'; let { milestoneGroup = undefined, @@ -119,7 +118,7 @@
- {#if milestoneGroup && milestoneGroup.text && milestoneGroup.milestones && currentMilestone && currentMilestone.text && currentMilestone.images} + {#if $locale && milestoneGroup && milestoneGroup.text && milestoneGroup.milestones && currentMilestone && currentMilestone.text && currentMilestone.images}
{$_('milestone.milestones')} {milestoneGroup.text[$lang_id].title}{milestoneGroup.text[$locale].title} {currentMilestoneIndex + 1} / {milestoneGroup.milestones.length}

- {currentMilestone.text[$lang_id].title} + {currentMilestone.text[$locale].title}

-

{currentMilestone.text[$lang_id].desc}

+

{currentMilestone.text[$locale].desc}

@@ -161,7 +160,7 @@ {$_('milestone.observation')}

- {currentMilestone.text[$lang_id].obs} + {currentMilestone.text[$locale].obs}

@@ -170,7 +169,7 @@ {$_('milestone.help')}

- {currentMilestone.text[$lang_id].help} + {currentMilestone.text[$locale].help}

diff --git a/frontend/src/lib/i18n.ts b/frontend/src/lib/i18n.ts index af537016..a50b228c 100644 --- a/frontend/src/lib/i18n.ts +++ b/frontend/src/lib/i18n.ts @@ -1,9 +1,8 @@ import { init, addMessages } from 'svelte-i18n'; -import { languages } from '$lib/stores/langStore'; import { getLanguages } from '$lib/client'; import de from '../locales/de.json'; -export async function getI18nJson(lang_id: number) { +export async function getI18nJson(lang_id: string) { try { const res = await fetch(`${import.meta.env.VITE_MONDEY_API_URL}/static/i18n/${lang_id}.json`); if (!res.ok) { @@ -19,18 +18,17 @@ export async function getI18nJson(lang_id: number) { } } -async function getTranslation(lang: string, lang_id: number) { +async function getTranslation(lang_id: string) { const json = await getI18nJson(lang_id); - addMessages(lang, json); + addMessages(lang_id, json); } export async function getTranslations() { const { data, error } = await getLanguages(); if (!error && data) { - languages.set(data); - Object.entries(data).forEach(([lang, lang_id]) => { - if (lang_id !== 1) { - getTranslation(lang, lang_id); + data.forEach((lang_id) => { + if (lang_id !== 'de') { + getTranslation(lang_id); } }); } diff --git a/frontend/src/lib/stores/langStore.ts b/frontend/src/lib/stores/langStore.ts deleted file mode 100644 index 5d86ac1a..00000000 --- a/frontend/src/lib/stores/langStore.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { type Writable, writable } from 'svelte/store'; -import type { GetLanguagesResponse } from '$lib/client/types.gen'; - -export const lang_id = writable('1'); - -export const languages: Writable = writable({}); diff --git a/mondey_backend/openapi.json b/mondey_backend/openapi.json index 192458e9..a101935f 100644 --- a/mondey_backend/openapi.json +++ b/mondey_backend/openapi.json @@ -1 +1 @@ -{"openapi": "3.1.0", "info": {"title": "MONDEY API", "version": "0.1.0"}, "paths": {"/languages/": {"get": {"tags": ["milestones"], "summary": "Get Languages", "operationId": "get_languages", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": {"type": "integer"}, "type": "object", "title": "Response Get Languages Languages Get"}}}}}}}, "/milestones/": {"get": {"tags": ["milestones"], "summary": "Get Milestones", "operationId": "get_milestones", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/MilestonePublic"}, "type": "array", "title": "Response Get Milestones Milestones Get"}}}}}}}, "/milestones/{milestone_id}": {"get": {"tags": ["milestones"], "summary": "Get Milestone", "operationId": "get_milestone", "parameters": [{"name": "milestone_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestonePublic"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/milestone-groups/": {"get": {"tags": ["milestones"], "summary": "Get Milestone Groups", "operationId": "get_milestone_groups", "parameters": [{"name": "milestone_age_group_id", "in": "query", "required": true, "schema": {"type": "integer", "title": "Milestone Age Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/MilestoneGroupPublic"}, "title": "Response Get Milestone Groups Milestone Groups Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/milestone-groups/{milestone_group_id}": {"get": {"tags": ["milestones"], "summary": "Get Milestone Group", "operationId": "get_milestone_group", "parameters": [{"name": "milestone_group_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneGroupPublic"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/milestone-age-groups/": {"get": {"tags": ["milestones"], "summary": "Get Milestone Age Groups", "operationId": "get_milestone_age_groups", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/MilestoneAgeGroupPublic"}, "type": "array", "title": "Response Get Milestone Age Groups Milestone Age Groups Get"}}}}}}}, "/user-questions/": {"get": {"tags": ["questions"], "summary": "Get User Questions", "operationId": "get_user_questions", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/UserQuestionPublic"}, "type": "array", "title": "Response Get User Questions User Questions Get"}}}}}}}, "/admin/languages/": {"post": {"tags": ["admin"], "summary": "Create Language", "operationId": "create_language", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/LanguageCreate"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Language"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}}, "/admin/languages/{language_id}": {"delete": {"tags": ["admin"], "summary": "Delete Language", "operationId": "delete_language", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "language_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Language Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/i18n/{language_id}": {"put": {"tags": ["admin"], "summary": "Update I18N", "operationId": "update_i18n", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "language_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Language Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "additionalProperties": {"type": "object", "additionalProperties": {"type": "string"}}, "title": "I18Dict"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/milestone-age-groups/": {"put": {"tags": ["admin"], "summary": "Update Milestone Age Group", "operationId": "update_milestone_age_group", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAgeGroupPublic"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAgeGroupPublic"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}, "post": {"tags": ["admin"], "summary": "Create Milestone Age Group", "operationId": "create_milestone_age_group", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAgeGroupCreate"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAgeGroupPublic"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}}, "/admin/milestone-age-groups/{milestone_age_group_id}": {"delete": {"tags": ["admin"], "summary": "Delete Milestone Age Group", "operationId": "delete_milestone_age_group", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "milestone_age_group_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Age Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/milestone-groups/": {"get": {"tags": ["admin"], "summary": "Get Milestone Groups Admin", "operationId": "get_milestone_groups_admin", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "milestone_age_group_id", "in": "query", "required": true, "schema": {"type": "integer", "title": "Milestone Age Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/MilestoneGroupAdmin"}, "title": "Response Get Milestone Groups Admin Admin Milestone Groups Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/milestone-groups/{milestone_age_group_id}": {"post": {"tags": ["admin"], "summary": "Create Milestone Group Admin", "operationId": "create_milestone_group_admin", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "milestone_age_group_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Age Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneGroupAdmin"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/milestone-groups": {"put": {"tags": ["admin"], "summary": "Update Milestone Group Admin", "operationId": "update_milestone_group_admin", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneGroupAdmin"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneGroupAdmin"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}}, "/admin/milestone-groups/{milestone_group_id}": {"delete": {"tags": ["admin"], "summary": "Delete Milestone Group Admin", "operationId": "delete_milestone_group_admin", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "milestone_group_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/milestone-group-images/{milestone_group_id}": {"put": {"tags": ["admin"], "summary": "Upload Milestone Group Image", "operationId": "upload_milestone_group_image", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "milestone_group_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Group Id"}}], "requestBody": {"required": true, "content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_upload_milestone_group_image_admin_milestone_group_images__milestone_group_id__put"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/milestones/{milestone_group_id}": {"post": {"tags": ["admin"], "summary": "Create Milestone", "operationId": "create_milestone", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "milestone_group_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAdmin"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/milestones/": {"put": {"tags": ["admin"], "summary": "Update Milestone", "operationId": "update_milestone", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAdmin"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAdmin"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}}, "/admin/milestones/{milestone_id}": {"delete": {"tags": ["admin"], "summary": "Delete Milestone", "operationId": "delete_milestone", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "milestone_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/milestone-images/{milestone_id}": {"post": {"tags": ["admin"], "summary": "Upload Milestone Image", "operationId": "upload_milestone_image", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "milestone_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Id"}}], "requestBody": {"required": true, "content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_upload_milestone_image_admin_milestone_images__milestone_id__post"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneImage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/user-questions/": {"get": {"tags": ["admin"], "summary": "Get User Questions Admin", "operationId": "get_user_questions_admin", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/UserQuestionAdmin"}, "type": "array", "title": "Response Get User Questions Admin Admin User Questions Get"}}}}}, "security": [{"APIKeyCookie": []}]}, "put": {"tags": ["admin"], "summary": "Update User Question", "operationId": "update_user_question", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserQuestionAdmin"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserQuestionAdmin"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}, "post": {"tags": ["admin"], "summary": "Create User Question", "operationId": "create_user_question", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserQuestionAdmin"}}}}}, "security": [{"APIKeyCookie": []}]}}, "/admin/user-questions/{user_question_id}": {"delete": {"tags": ["admin"], "summary": "Delete User Question", "operationId": "delete_user_question", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "user_question_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "User Question Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/users/me": {"get": {"tags": ["users"], "summary": "Users:Current User", "operationId": "users:current_user", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserRead"}}}}, "401": {"description": "Missing token or inactive user."}}, "security": [{"APIKeyCookie": []}]}, "patch": {"tags": ["users"], "summary": "Users:Patch Current User", "operationId": "users:patch_current_user", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserUpdate"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserRead"}}}}, "401": {"description": "Missing token or inactive user."}, "400": {"description": "Bad Request", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorModel"}, "examples": {"UPDATE_USER_EMAIL_ALREADY_EXISTS": {"summary": "A user with this email already exists.", "value": {"detail": "UPDATE_USER_EMAIL_ALREADY_EXISTS"}}, "UPDATE_USER_INVALID_PASSWORD": {"summary": "Password validation failed.", "value": {"detail": {"code": "UPDATE_USER_INVALID_PASSWORD", "reason": "Password should beat least 3 characters"}}}}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}}, "/users/{id}": {"get": {"tags": ["users"], "summary": "Users:User", "operationId": "users:user", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "title": "Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserRead"}}}}, "401": {"description": "Missing token or inactive user."}, "403": {"description": "Not a superuser."}, "404": {"description": "The user does not exist."}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "patch": {"tags": ["users"], "summary": "Users:Patch User", "operationId": "users:patch_user", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "title": "Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserRead"}}}}, "401": {"description": "Missing token or inactive user."}, "403": {"description": "Not a superuser."}, "404": {"description": "The user does not exist."}, "400": {"content": {"application/json": {"examples": {"UPDATE_USER_EMAIL_ALREADY_EXISTS": {"summary": "A user with this email already exists.", "value": {"detail": "UPDATE_USER_EMAIL_ALREADY_EXISTS"}}, "UPDATE_USER_INVALID_PASSWORD": {"summary": "Password validation failed.", "value": {"detail": {"code": "UPDATE_USER_INVALID_PASSWORD", "reason": "Password should beat least 3 characters"}}}}, "schema": {"$ref": "#/components/schemas/ErrorModel"}}}, "description": "Bad Request"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["users"], "summary": "Users:Delete User", "operationId": "users:delete_user", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "title": "Id"}}], "responses": {"204": {"description": "Successful Response"}, "401": {"description": "Missing token or inactive user."}, "403": {"description": "Not a superuser."}, "404": {"description": "The user does not exist."}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/users/children/": {"get": {"tags": ["users"], "summary": "Get Children", "operationId": "get_children", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/ChildPublic"}, "type": "array", "title": "Response Get Children Users Children Get"}}}}}, "security": [{"APIKeyCookie": []}]}, "put": {"tags": ["users"], "summary": "Update Child", "operationId": "update_child", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChildPublic"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChildPublic"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}, "post": {"tags": ["users"], "summary": "Create Child", "operationId": "create_child", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChildCreate"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChildPublic"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}}, "/users/children/{child_id}": {"delete": {"tags": ["users"], "summary": "Delete Child", "operationId": "delete_child", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "child_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Child Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/users/children-images/{child_id}": {"get": {"tags": ["users"], "summary": "Get Child Image", "operationId": "get_child_image", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "child_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Child Id"}}], "responses": {"200": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["users"], "summary": "Upload Child Image", "operationId": "upload_child_image", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "child_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Child Id"}}], "requestBody": {"required": true, "content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_upload_child_image_users_children_images__child_id__put"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/users/milestone-answers/{child_id}": {"get": {"tags": ["users"], "summary": "Get Current Milestone Answer Session", "operationId": "get_current_milestone_answer_session", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "child_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Child Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAnswerSessionPublic"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/users/milestone-answers/{milestone_answer_session_id}": {"put": {"tags": ["users"], "summary": "Update Milestone Answer", "operationId": "update_milestone_answer", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "milestone_answer_session_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Answer Session Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAnswerPublic"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAnswerPublic"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/users/user-answers/": {"get": {"tags": ["users"], "summary": "Get Current User Answers", "operationId": "get_current_user_answers", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/UserAnswerPublic"}, "type": "array", "title": "Response Get Current User Answers Users User Answers Get"}}}}}, "security": [{"APIKeyCookie": []}]}, "put": {"tags": ["users"], "summary": "Update Current User Answers", "operationId": "update_current_user_answers", "requestBody": {"content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/UserAnswerPublic"}, "type": "array", "title": "New Answers"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/UserAnswerPublic"}, "type": "array", "title": "Response Update Current User Answers Users User Answers Put"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}}, "/auth/login": {"post": {"tags": ["auth"], "summary": "Auth:Cookie.Login", "operationId": "auth:cookie.login", "requestBody": {"content": {"application/x-www-form-urlencoded": {"schema": {"$ref": "#/components/schemas/Body_auth_cookie_login_auth_login_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "400": {"description": "Bad Request", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorModel"}, "examples": {"LOGIN_BAD_CREDENTIALS": {"summary": "Bad credentials or the user is inactive.", "value": {"detail": "LOGIN_BAD_CREDENTIALS"}}, "LOGIN_USER_NOT_VERIFIED": {"summary": "The user is not verified.", "value": {"detail": "LOGIN_USER_NOT_VERIFIED"}}}}}}, "204": {"description": "No Content"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/auth/logout": {"post": {"tags": ["auth"], "summary": "Auth:Cookie.Logout", "operationId": "auth:cookie.logout", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "401": {"description": "Missing token or inactive user."}, "204": {"description": "No Content"}}, "security": [{"APIKeyCookie": []}]}}, "/auth/register": {"post": {"tags": ["auth"], "summary": "Register:Register", "operationId": "register:register", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserCreate"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserRead"}}}}, "400": {"description": "Bad Request", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorModel"}, "examples": {"REGISTER_USER_ALREADY_EXISTS": {"summary": "A user with this email already exists.", "value": {"detail": "REGISTER_USER_ALREADY_EXISTS"}}, "REGISTER_INVALID_PASSWORD": {"summary": "Password validation failed.", "value": {"detail": {"code": "REGISTER_INVALID_PASSWORD", "reason": "Password should beat least 3 characters"}}}}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/auth/forgot-password": {"post": {"tags": ["auth"], "summary": "Reset:Forgot Password", "operationId": "reset:forgot_password", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Body_reset_forgot_password_auth_forgot_password_post"}}}, "required": true}, "responses": {"202": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/auth/reset-password": {"post": {"tags": ["auth"], "summary": "Reset:Reset Password", "operationId": "reset:reset_password", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Body_reset_reset_password_auth_reset_password_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "400": {"description": "Bad Request", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorModel"}, "examples": {"RESET_PASSWORD_BAD_TOKEN": {"summary": "Bad or expired token.", "value": {"detail": "RESET_PASSWORD_BAD_TOKEN"}}, "RESET_PASSWORD_INVALID_PASSWORD": {"summary": "Password validation failed.", "value": {"detail": {"code": "RESET_PASSWORD_INVALID_PASSWORD", "reason": "Password should be at least 3 characters"}}}}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/auth/request-verify-token": {"post": {"tags": ["auth"], "summary": "Verify:Request-Token", "operationId": "verify:request-token", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Body_verify_request_token_auth_request_verify_token_post"}}}, "required": true}, "responses": {"202": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/auth/verify": {"post": {"tags": ["auth"], "summary": "Verify:Verify", "operationId": "verify:verify", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Body_verify_verify_auth_verify_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserRead"}}}}, "400": {"description": "Bad Request", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorModel"}, "examples": {"VERIFY_USER_BAD_TOKEN": {"summary": "Bad token, not existing user ornot the e-mail currently set for the user.", "value": {"detail": "VERIFY_USER_BAD_TOKEN"}}, "VERIFY_USER_ALREADY_VERIFIED": {"summary": "The user is already verified.", "value": {"detail": "VERIFY_USER_ALREADY_VERIFIED"}}}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/research/auth/": {"get": {"tags": ["research"], "summary": "Auth", "operationId": "auth", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"APIKeyCookie": []}]}}}, "components": {"schemas": {"Body_auth_cookie_login_auth_login_post": {"properties": {"grant_type": {"anyOf": [{"type": "string", "pattern": "password"}, {"type": "null"}], "title": "Grant Type"}, "username": {"type": "string", "title": "Username"}, "password": {"type": "string", "title": "Password"}, "scope": {"type": "string", "title": "Scope", "default": ""}, "client_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Client Id"}, "client_secret": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Client Secret"}}, "type": "object", "required": ["username", "password"], "title": "Body_auth_cookie_login_auth_login_post"}, "Body_reset_forgot_password_auth_forgot_password_post": {"properties": {"email": {"type": "string", "format": "email", "title": "Email"}}, "type": "object", "required": ["email"], "title": "Body_reset_forgot_password_auth_forgot_password_post"}, "Body_reset_reset_password_auth_reset_password_post": {"properties": {"token": {"type": "string", "title": "Token"}, "password": {"type": "string", "title": "Password"}}, "type": "object", "required": ["token", "password"], "title": "Body_reset_reset_password_auth_reset_password_post"}, "Body_upload_child_image_users_children_images__child_id__put": {"properties": {"file": {"type": "string", "format": "binary", "title": "File"}}, "type": "object", "required": ["file"], "title": "Body_upload_child_image_users_children_images__child_id__put"}, "Body_upload_milestone_group_image_admin_milestone_group_images__milestone_group_id__put": {"properties": {"file": {"type": "string", "format": "binary", "title": "File"}}, "type": "object", "required": ["file"], "title": "Body_upload_milestone_group_image_admin_milestone_group_images__milestone_group_id__put"}, "Body_upload_milestone_image_admin_milestone_images__milestone_id__post": {"properties": {"file": {"type": "string", "format": "binary", "title": "File"}}, "type": "object", "required": ["file"], "title": "Body_upload_milestone_image_admin_milestone_images__milestone_id__post"}, "Body_verify_request_token_auth_request_verify_token_post": {"properties": {"email": {"type": "string", "format": "email", "title": "Email"}}, "type": "object", "required": ["email"], "title": "Body_verify_request_token_auth_request_verify_token_post"}, "Body_verify_verify_auth_verify_post": {"properties": {"token": {"type": "string", "title": "Token"}}, "type": "object", "required": ["token"], "title": "Body_verify_verify_auth_verify_post"}, "ChildCreate": {"properties": {"name": {"type": "string", "title": "Name", "default": ""}, "birth_year": {"type": "integer", "title": "Birth Year"}, "birth_month": {"type": "integer", "title": "Birth Month"}}, "type": "object", "required": ["birth_year", "birth_month"], "title": "ChildCreate"}, "ChildPublic": {"properties": {"name": {"type": "string", "title": "Name", "default": ""}, "birth_year": {"type": "integer", "title": "Birth Year"}, "birth_month": {"type": "integer", "title": "Birth Month"}, "id": {"type": "integer", "title": "Id"}, "has_image": {"type": "boolean", "title": "Has Image"}}, "type": "object", "required": ["birth_year", "birth_month", "id", "has_image"], "title": "ChildPublic"}, "ErrorModel": {"properties": {"detail": {"anyOf": [{"type": "string"}, {"additionalProperties": {"type": "string"}, "type": "object"}], "title": "Detail"}}, "type": "object", "required": ["detail"], "title": "ErrorModel"}, "HTTPValidationError": {"properties": {"detail": {"items": {"$ref": "#/components/schemas/ValidationError"}, "type": "array", "title": "Detail"}}, "type": "object", "title": "HTTPValidationError"}, "Language": {"properties": {"id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Id"}, "lang": {"type": "string", "maxLength": 2, "title": "Lang"}}, "type": "object", "required": ["lang"], "title": "Language"}, "LanguageCreate": {"properties": {"lang": {"type": "string", "maxLength": 2, "title": "Lang"}}, "type": "object", "required": ["lang"], "title": "LanguageCreate"}, "MilestoneAdmin": {"properties": {"id": {"type": "integer", "title": "Id"}, "group_id": {"type": "integer", "title": "Group Id"}, "order": {"type": "integer", "title": "Order"}, "text": {"additionalProperties": {"$ref": "#/components/schemas/MilestoneText"}, "type": "object", "title": "Text", "default": {}}, "images": {"items": {"$ref": "#/components/schemas/MilestoneImage"}, "type": "array", "title": "Images", "default": []}}, "type": "object", "required": ["id", "group_id", "order"], "title": "MilestoneAdmin"}, "MilestoneAgeGroupCreate": {"properties": {"months_min": {"type": "integer", "title": "Months Min"}, "months_max": {"type": "integer", "title": "Months Max"}}, "type": "object", "required": ["months_min", "months_max"], "title": "MilestoneAgeGroupCreate"}, "MilestoneAgeGroupPublic": {"properties": {"months_min": {"type": "integer", "title": "Months Min"}, "months_max": {"type": "integer", "title": "Months Max"}, "id": {"type": "integer", "title": "Id"}}, "type": "object", "required": ["months_min", "months_max", "id"], "title": "MilestoneAgeGroupPublic"}, "MilestoneAnswerPublic": {"properties": {"milestone_id": {"type": "integer", "title": "Milestone Id"}, "answer": {"type": "integer", "title": "Answer"}}, "type": "object", "required": ["milestone_id", "answer"], "title": "MilestoneAnswerPublic"}, "MilestoneAnswerSessionPublic": {"properties": {"id": {"type": "integer", "title": "Id"}, "child_id": {"type": "integer", "title": "Child Id"}, "age_group_id": {"type": "integer", "title": "Age Group Id"}, "created_at": {"type": "string", "format": "date-time", "title": "Created At"}, "answers": {"additionalProperties": {"$ref": "#/components/schemas/MilestoneAnswerPublic"}, "type": "object", "title": "Answers"}}, "type": "object", "required": ["id", "child_id", "age_group_id", "created_at", "answers"], "title": "MilestoneAnswerSessionPublic"}, "MilestoneGroupAdmin": {"properties": {"id": {"type": "integer", "title": "Id"}, "age_group_id": {"type": "integer", "title": "Age Group Id"}, "order": {"type": "integer", "title": "Order"}, "text": {"additionalProperties": {"$ref": "#/components/schemas/MilestoneGroupText"}, "type": "object", "title": "Text", "default": {}}, "milestones": {"items": {"$ref": "#/components/schemas/MilestoneAdmin"}, "type": "array", "title": "Milestones", "default": []}}, "type": "object", "required": ["id", "age_group_id", "order"], "title": "MilestoneGroupAdmin"}, "MilestoneGroupPublic": {"properties": {"id": {"type": "integer", "title": "Id"}, "text": {"additionalProperties": {"$ref": "#/components/schemas/MilestoneGroupTextPublic"}, "type": "object", "title": "Text", "default": {}}, "milestones": {"items": {"$ref": "#/components/schemas/MilestonePublic"}, "type": "array", "title": "Milestones", "default": []}}, "type": "object", "required": ["id"], "title": "MilestoneGroupPublic"}, "MilestoneGroupText": {"properties": {"title": {"type": "string", "title": "Title", "default": ""}, "desc": {"type": "string", "title": "Desc", "default": ""}, "group_id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Group Id"}, "lang_id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Lang Id"}}, "type": "object", "title": "MilestoneGroupText"}, "MilestoneGroupTextPublic": {"properties": {"title": {"type": "string", "title": "Title", "default": ""}, "desc": {"type": "string", "title": "Desc", "default": ""}}, "type": "object", "title": "MilestoneGroupTextPublic"}, "MilestoneImage": {"properties": {"id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Id"}, "milestone_id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Milestone Id"}, "filename": {"type": "string", "title": "Filename", "default": ""}, "approved": {"type": "boolean", "title": "Approved", "default": false}}, "type": "object", "title": "MilestoneImage"}, "MilestoneImagePublic": {"properties": {"filename": {"type": "string", "title": "Filename"}, "approved": {"type": "boolean", "title": "Approved"}}, "type": "object", "required": ["filename", "approved"], "title": "MilestoneImagePublic"}, "MilestonePublic": {"properties": {"id": {"type": "integer", "title": "Id"}, "text": {"additionalProperties": {"$ref": "#/components/schemas/MilestoneTextPublic"}, "type": "object", "title": "Text", "default": {}}, "images": {"items": {"$ref": "#/components/schemas/MilestoneImagePublic"}, "type": "array", "title": "Images", "default": []}}, "type": "object", "required": ["id"], "title": "MilestonePublic"}, "MilestoneText": {"properties": {"title": {"type": "string", "title": "Title", "default": ""}, "desc": {"type": "string", "title": "Desc", "default": ""}, "obs": {"type": "string", "title": "Obs", "default": ""}, "help": {"type": "string", "title": "Help", "default": ""}, "milestone_id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Milestone Id"}, "lang_id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Lang Id"}}, "type": "object", "title": "MilestoneText"}, "MilestoneTextPublic": {"properties": {"title": {"type": "string", "title": "Title", "default": ""}, "desc": {"type": "string", "title": "Desc", "default": ""}, "obs": {"type": "string", "title": "Obs", "default": ""}, "help": {"type": "string", "title": "Help", "default": ""}}, "type": "object", "title": "MilestoneTextPublic"}, "UserAnswerPublic": {"properties": {"answer": {"type": "string", "title": "Answer"}, "question_id": {"type": "integer", "title": "Question Id"}, "non_standard": {"type": "boolean", "title": "Non Standard"}}, "type": "object", "required": ["answer", "question_id", "non_standard"], "title": "UserAnswerPublic", "description": "External data model for UserAnswers\n\nParameters\n----------\nSQLModel : Pydantic model basic sqlmodel pydantic type"}, "UserCreate": {"properties": {"email": {"type": "string", "format": "email", "title": "Email"}, "password": {"type": "string", "title": "Password"}, "is_active": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Is Active", "default": true}, "is_superuser": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Is Superuser", "default": false}, "is_verified": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Is Verified", "default": false}, "is_researcher": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Is Researcher", "default": false}}, "type": "object", "required": ["email", "password"], "title": "UserCreate"}, "UserQuestionAdmin": {"properties": {"id": {"type": "integer", "title": "Id"}, "order": {"type": "integer", "title": "Order"}, "input": {"type": "string", "title": "Input"}, "options": {"type": "string", "title": "Options"}, "text": {"additionalProperties": {"$ref": "#/components/schemas/UserQuestionText"}, "type": "object", "title": "Text", "default": {}}}, "type": "object", "required": ["id", "order", "input", "options"], "title": "UserQuestionAdmin"}, "UserQuestionPublic": {"properties": {"id": {"type": "integer", "title": "Id"}, "input": {"type": "string", "title": "Input"}, "text": {"additionalProperties": {"$ref": "#/components/schemas/UserQuestionTextPublic"}, "type": "object", "title": "Text", "default": {}}}, "type": "object", "required": ["id", "input"], "title": "UserQuestionPublic"}, "UserQuestionText": {"properties": {"question": {"type": "string", "title": "Question", "default": ""}, "options_json": {"type": "string", "title": "Options Json", "default": ""}, "user_question_id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "User Question Id"}, "lang_id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Lang Id"}, "options": {"type": "string", "title": "Options", "default": ""}}, "type": "object", "title": "UserQuestionText"}, "UserQuestionTextPublic": {"properties": {"question": {"type": "string", "title": "Question", "default": ""}, "options_json": {"type": "string", "title": "Options Json", "default": ""}}, "type": "object", "title": "UserQuestionTextPublic"}, "UserRead": {"properties": {"id": {"type": "integer", "title": "Id"}, "email": {"type": "string", "format": "email", "title": "Email"}, "is_active": {"type": "boolean", "title": "Is Active", "default": true}, "is_superuser": {"type": "boolean", "title": "Is Superuser", "default": false}, "is_verified": {"type": "boolean", "title": "Is Verified", "default": false}, "is_researcher": {"type": "boolean", "title": "Is Researcher"}}, "type": "object", "required": ["id", "email", "is_researcher"], "title": "UserRead"}, "UserUpdate": {"properties": {"password": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Password"}, "email": {"anyOf": [{"type": "string", "format": "email"}, {"type": "null"}], "title": "Email"}, "is_active": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Is Active"}, "is_superuser": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Is Superuser"}, "is_verified": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Is Verified"}, "is_researcher": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Is Researcher"}}, "type": "object", "title": "UserUpdate"}, "ValidationError": {"properties": {"loc": {"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, "type": "array", "title": "Location"}, "msg": {"type": "string", "title": "Message"}, "type": {"type": "string", "title": "Error Type"}}, "type": "object", "required": ["loc", "msg", "type"], "title": "ValidationError"}}, "securitySchemes": {"APIKeyCookie": {"type": "apiKey", "in": "cookie", "name": "fastapiusersauth"}}}} \ No newline at end of file +{"openapi": "3.1.0", "info": {"title": "MONDEY API", "version": "0.1.0"}, "paths": {"/languages/": {"get": {"tags": ["milestones"], "summary": "Get Languages", "operationId": "get_languages", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"type": "string"}, "type": "array", "title": "Response Get Languages Languages Get"}}}}}}}, "/milestones/": {"get": {"tags": ["milestones"], "summary": "Get Milestones", "operationId": "get_milestones", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/MilestonePublic"}, "type": "array", "title": "Response Get Milestones Milestones Get"}}}}}}}, "/milestones/{milestone_id}": {"get": {"tags": ["milestones"], "summary": "Get Milestone", "operationId": "get_milestone", "parameters": [{"name": "milestone_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestonePublic"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/milestone-groups/": {"get": {"tags": ["milestones"], "summary": "Get Milestone Groups", "operationId": "get_milestone_groups", "parameters": [{"name": "milestone_age_group_id", "in": "query", "required": true, "schema": {"type": "integer", "title": "Milestone Age Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/MilestoneGroupPublic"}, "title": "Response Get Milestone Groups Milestone Groups Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/milestone-groups/{milestone_group_id}": {"get": {"tags": ["milestones"], "summary": "Get Milestone Group", "operationId": "get_milestone_group", "parameters": [{"name": "milestone_group_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneGroupPublic"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/milestone-age-groups/": {"get": {"tags": ["milestones"], "summary": "Get Milestone Age Groups", "operationId": "get_milestone_age_groups", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/MilestoneAgeGroupPublic"}, "type": "array", "title": "Response Get Milestone Age Groups Milestone Age Groups Get"}}}}}}}, "/user-questions/": {"get": {"tags": ["questions"], "summary": "Get User Questions", "operationId": "get_user_questions", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/UserQuestionPublic"}, "type": "array", "title": "Response Get User Questions User Questions Get"}}}}}}}, "/admin/languages/": {"post": {"tags": ["admin"], "summary": "Create Language", "operationId": "create_language", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Language"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Language"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}}, "/admin/languages/{language_id}": {"delete": {"tags": ["admin"], "summary": "Delete Language", "operationId": "delete_language", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "language_id", "in": "path", "required": true, "schema": {"type": "string", "title": "Language Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/i18n/{language_id}": {"put": {"tags": ["admin"], "summary": "Update I18N", "operationId": "update_i18n", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "language_id", "in": "path", "required": true, "schema": {"type": "string", "title": "Language Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "additionalProperties": {"type": "object", "additionalProperties": {"type": "string"}}, "title": "I18Dict"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/milestone-age-groups/": {"put": {"tags": ["admin"], "summary": "Update Milestone Age Group", "operationId": "update_milestone_age_group", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAgeGroupPublic"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAgeGroupPublic"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}, "post": {"tags": ["admin"], "summary": "Create Milestone Age Group", "operationId": "create_milestone_age_group", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAgeGroupCreate"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAgeGroupPublic"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}}, "/admin/milestone-age-groups/{milestone_age_group_id}": {"delete": {"tags": ["admin"], "summary": "Delete Milestone Age Group", "operationId": "delete_milestone_age_group", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "milestone_age_group_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Age Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/milestone-groups/": {"get": {"tags": ["admin"], "summary": "Get Milestone Groups Admin", "operationId": "get_milestone_groups_admin", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "milestone_age_group_id", "in": "query", "required": true, "schema": {"type": "integer", "title": "Milestone Age Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/MilestoneGroupAdmin"}, "title": "Response Get Milestone Groups Admin Admin Milestone Groups Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/milestone-groups/{milestone_age_group_id}": {"post": {"tags": ["admin"], "summary": "Create Milestone Group Admin", "operationId": "create_milestone_group_admin", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "milestone_age_group_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Age Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneGroupAdmin"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/milestone-groups": {"put": {"tags": ["admin"], "summary": "Update Milestone Group Admin", "operationId": "update_milestone_group_admin", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneGroupAdmin"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneGroupAdmin"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}}, "/admin/milestone-groups/{milestone_group_id}": {"delete": {"tags": ["admin"], "summary": "Delete Milestone Group Admin", "operationId": "delete_milestone_group_admin", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "milestone_group_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/milestone-group-images/{milestone_group_id}": {"put": {"tags": ["admin"], "summary": "Upload Milestone Group Image", "operationId": "upload_milestone_group_image", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "milestone_group_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Group Id"}}], "requestBody": {"required": true, "content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_upload_milestone_group_image_admin_milestone_group_images__milestone_group_id__put"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/milestones/{milestone_group_id}": {"post": {"tags": ["admin"], "summary": "Create Milestone", "operationId": "create_milestone", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "milestone_group_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAdmin"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/milestones/": {"put": {"tags": ["admin"], "summary": "Update Milestone", "operationId": "update_milestone", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAdmin"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAdmin"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}}, "/admin/milestones/{milestone_id}": {"delete": {"tags": ["admin"], "summary": "Delete Milestone", "operationId": "delete_milestone", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "milestone_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/milestone-images/{milestone_id}": {"post": {"tags": ["admin"], "summary": "Upload Milestone Image", "operationId": "upload_milestone_image", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "milestone_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Id"}}], "requestBody": {"required": true, "content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_upload_milestone_image_admin_milestone_images__milestone_id__post"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneImage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/user-questions/": {"get": {"tags": ["admin"], "summary": "Get User Questions Admin", "operationId": "get_user_questions_admin", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/UserQuestionAdmin"}, "type": "array", "title": "Response Get User Questions Admin Admin User Questions Get"}}}}}, "security": [{"APIKeyCookie": []}]}, "put": {"tags": ["admin"], "summary": "Update User Question", "operationId": "update_user_question", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserQuestionAdmin"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserQuestionAdmin"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}, "post": {"tags": ["admin"], "summary": "Create User Question", "operationId": "create_user_question", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserQuestionAdmin"}}}}}, "security": [{"APIKeyCookie": []}]}}, "/admin/user-questions/{user_question_id}": {"delete": {"tags": ["admin"], "summary": "Delete User Question", "operationId": "delete_user_question", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "user_question_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "User Question Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/users/me": {"get": {"tags": ["users"], "summary": "Users:Current User", "operationId": "users:current_user", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserRead"}}}}, "401": {"description": "Missing token or inactive user."}}, "security": [{"APIKeyCookie": []}]}, "patch": {"tags": ["users"], "summary": "Users:Patch Current User", "operationId": "users:patch_current_user", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserUpdate"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserRead"}}}}, "401": {"description": "Missing token or inactive user."}, "400": {"description": "Bad Request", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorModel"}, "examples": {"UPDATE_USER_EMAIL_ALREADY_EXISTS": {"summary": "A user with this email already exists.", "value": {"detail": "UPDATE_USER_EMAIL_ALREADY_EXISTS"}}, "UPDATE_USER_INVALID_PASSWORD": {"summary": "Password validation failed.", "value": {"detail": {"code": "UPDATE_USER_INVALID_PASSWORD", "reason": "Password should beat least 3 characters"}}}}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}}, "/users/{id}": {"get": {"tags": ["users"], "summary": "Users:User", "operationId": "users:user", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "title": "Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserRead"}}}}, "401": {"description": "Missing token or inactive user."}, "403": {"description": "Not a superuser."}, "404": {"description": "The user does not exist."}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "patch": {"tags": ["users"], "summary": "Users:Patch User", "operationId": "users:patch_user", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "title": "Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserRead"}}}}, "401": {"description": "Missing token or inactive user."}, "403": {"description": "Not a superuser."}, "404": {"description": "The user does not exist."}, "400": {"content": {"application/json": {"examples": {"UPDATE_USER_EMAIL_ALREADY_EXISTS": {"summary": "A user with this email already exists.", "value": {"detail": "UPDATE_USER_EMAIL_ALREADY_EXISTS"}}, "UPDATE_USER_INVALID_PASSWORD": {"summary": "Password validation failed.", "value": {"detail": {"code": "UPDATE_USER_INVALID_PASSWORD", "reason": "Password should beat least 3 characters"}}}}, "schema": {"$ref": "#/components/schemas/ErrorModel"}}}, "description": "Bad Request"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["users"], "summary": "Users:Delete User", "operationId": "users:delete_user", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "title": "Id"}}], "responses": {"204": {"description": "Successful Response"}, "401": {"description": "Missing token or inactive user."}, "403": {"description": "Not a superuser."}, "404": {"description": "The user does not exist."}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/users/children/": {"get": {"tags": ["users"], "summary": "Get Children", "operationId": "get_children", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/ChildPublic"}, "type": "array", "title": "Response Get Children Users Children Get"}}}}}, "security": [{"APIKeyCookie": []}]}, "put": {"tags": ["users"], "summary": "Update Child", "operationId": "update_child", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChildPublic"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChildPublic"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}, "post": {"tags": ["users"], "summary": "Create Child", "operationId": "create_child", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChildCreate"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChildPublic"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}}, "/users/children/{child_id}": {"delete": {"tags": ["users"], "summary": "Delete Child", "operationId": "delete_child", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "child_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Child Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/users/children-images/{child_id}": {"get": {"tags": ["users"], "summary": "Get Child Image", "operationId": "get_child_image", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "child_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Child Id"}}], "responses": {"200": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["users"], "summary": "Upload Child Image", "operationId": "upload_child_image", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "child_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Child Id"}}], "requestBody": {"required": true, "content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_upload_child_image_users_children_images__child_id__put"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/users/milestone-answers/{child_id}": {"get": {"tags": ["users"], "summary": "Get Current Milestone Answer Session", "operationId": "get_current_milestone_answer_session", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "child_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Child Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAnswerSessionPublic"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/users/milestone-answers/{milestone_answer_session_id}": {"put": {"tags": ["users"], "summary": "Update Milestone Answer", "operationId": "update_milestone_answer", "security": [{"APIKeyCookie": []}], "parameters": [{"name": "milestone_answer_session_id", "in": "path", "required": true, "schema": {"type": "integer", "title": "Milestone Answer Session Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAnswerPublic"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MilestoneAnswerPublic"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/users/user-answers/": {"get": {"tags": ["users"], "summary": "Get Current User Answers", "operationId": "get_current_user_answers", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/UserAnswerPublic"}, "type": "array", "title": "Response Get Current User Answers Users User Answers Get"}}}}}, "security": [{"APIKeyCookie": []}]}, "put": {"tags": ["users"], "summary": "Update Current User Answers", "operationId": "update_current_user_answers", "requestBody": {"content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/UserAnswerPublic"}, "type": "array", "title": "New Answers"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/UserAnswerPublic"}, "type": "array", "title": "Response Update Current User Answers Users User Answers Put"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"APIKeyCookie": []}]}}, "/auth/login": {"post": {"tags": ["auth"], "summary": "Auth:Cookie.Login", "operationId": "auth:cookie.login", "requestBody": {"content": {"application/x-www-form-urlencoded": {"schema": {"$ref": "#/components/schemas/Body_auth_cookie_login_auth_login_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "400": {"description": "Bad Request", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorModel"}, "examples": {"LOGIN_BAD_CREDENTIALS": {"summary": "Bad credentials or the user is inactive.", "value": {"detail": "LOGIN_BAD_CREDENTIALS"}}, "LOGIN_USER_NOT_VERIFIED": {"summary": "The user is not verified.", "value": {"detail": "LOGIN_USER_NOT_VERIFIED"}}}}}}, "204": {"description": "No Content"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/auth/logout": {"post": {"tags": ["auth"], "summary": "Auth:Cookie.Logout", "operationId": "auth:cookie.logout", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "401": {"description": "Missing token or inactive user."}, "204": {"description": "No Content"}}, "security": [{"APIKeyCookie": []}]}}, "/auth/register": {"post": {"tags": ["auth"], "summary": "Register:Register", "operationId": "register:register", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserCreate"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserRead"}}}}, "400": {"description": "Bad Request", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorModel"}, "examples": {"REGISTER_USER_ALREADY_EXISTS": {"summary": "A user with this email already exists.", "value": {"detail": "REGISTER_USER_ALREADY_EXISTS"}}, "REGISTER_INVALID_PASSWORD": {"summary": "Password validation failed.", "value": {"detail": {"code": "REGISTER_INVALID_PASSWORD", "reason": "Password should beat least 3 characters"}}}}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/auth/forgot-password": {"post": {"tags": ["auth"], "summary": "Reset:Forgot Password", "operationId": "reset:forgot_password", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Body_reset_forgot_password_auth_forgot_password_post"}}}, "required": true}, "responses": {"202": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/auth/reset-password": {"post": {"tags": ["auth"], "summary": "Reset:Reset Password", "operationId": "reset:reset_password", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Body_reset_reset_password_auth_reset_password_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "400": {"description": "Bad Request", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorModel"}, "examples": {"RESET_PASSWORD_BAD_TOKEN": {"summary": "Bad or expired token.", "value": {"detail": "RESET_PASSWORD_BAD_TOKEN"}}, "RESET_PASSWORD_INVALID_PASSWORD": {"summary": "Password validation failed.", "value": {"detail": {"code": "RESET_PASSWORD_INVALID_PASSWORD", "reason": "Password should be at least 3 characters"}}}}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/auth/request-verify-token": {"post": {"tags": ["auth"], "summary": "Verify:Request-Token", "operationId": "verify:request-token", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Body_verify_request_token_auth_request_verify_token_post"}}}, "required": true}, "responses": {"202": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/auth/verify": {"post": {"tags": ["auth"], "summary": "Verify:Verify", "operationId": "verify:verify", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Body_verify_verify_auth_verify_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserRead"}}}}, "400": {"description": "Bad Request", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorModel"}, "examples": {"VERIFY_USER_BAD_TOKEN": {"summary": "Bad token, not existing user ornot the e-mail currently set for the user.", "value": {"detail": "VERIFY_USER_BAD_TOKEN"}}, "VERIFY_USER_ALREADY_VERIFIED": {"summary": "The user is already verified.", "value": {"detail": "VERIFY_USER_ALREADY_VERIFIED"}}}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/research/auth/": {"get": {"tags": ["research"], "summary": "Auth", "operationId": "auth", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"APIKeyCookie": []}]}}}, "components": {"schemas": {"Body_auth_cookie_login_auth_login_post": {"properties": {"grant_type": {"anyOf": [{"type": "string", "pattern": "password"}, {"type": "null"}], "title": "Grant Type"}, "username": {"type": "string", "title": "Username"}, "password": {"type": "string", "title": "Password"}, "scope": {"type": "string", "title": "Scope", "default": ""}, "client_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Client Id"}, "client_secret": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Client Secret"}}, "type": "object", "required": ["username", "password"], "title": "Body_auth_cookie_login_auth_login_post"}, "Body_reset_forgot_password_auth_forgot_password_post": {"properties": {"email": {"type": "string", "format": "email", "title": "Email"}}, "type": "object", "required": ["email"], "title": "Body_reset_forgot_password_auth_forgot_password_post"}, "Body_reset_reset_password_auth_reset_password_post": {"properties": {"token": {"type": "string", "title": "Token"}, "password": {"type": "string", "title": "Password"}}, "type": "object", "required": ["token", "password"], "title": "Body_reset_reset_password_auth_reset_password_post"}, "Body_upload_child_image_users_children_images__child_id__put": {"properties": {"file": {"type": "string", "format": "binary", "title": "File"}}, "type": "object", "required": ["file"], "title": "Body_upload_child_image_users_children_images__child_id__put"}, "Body_upload_milestone_group_image_admin_milestone_group_images__milestone_group_id__put": {"properties": {"file": {"type": "string", "format": "binary", "title": "File"}}, "type": "object", "required": ["file"], "title": "Body_upload_milestone_group_image_admin_milestone_group_images__milestone_group_id__put"}, "Body_upload_milestone_image_admin_milestone_images__milestone_id__post": {"properties": {"file": {"type": "string", "format": "binary", "title": "File"}}, "type": "object", "required": ["file"], "title": "Body_upload_milestone_image_admin_milestone_images__milestone_id__post"}, "Body_verify_request_token_auth_request_verify_token_post": {"properties": {"email": {"type": "string", "format": "email", "title": "Email"}}, "type": "object", "required": ["email"], "title": "Body_verify_request_token_auth_request_verify_token_post"}, "Body_verify_verify_auth_verify_post": {"properties": {"token": {"type": "string", "title": "Token"}}, "type": "object", "required": ["token"], "title": "Body_verify_verify_auth_verify_post"}, "ChildCreate": {"properties": {"name": {"type": "string", "title": "Name", "default": ""}, "birth_year": {"type": "integer", "title": "Birth Year"}, "birth_month": {"type": "integer", "title": "Birth Month"}}, "type": "object", "required": ["birth_year", "birth_month"], "title": "ChildCreate"}, "ChildPublic": {"properties": {"name": {"type": "string", "title": "Name", "default": ""}, "birth_year": {"type": "integer", "title": "Birth Year"}, "birth_month": {"type": "integer", "title": "Birth Month"}, "id": {"type": "integer", "title": "Id"}, "has_image": {"type": "boolean", "title": "Has Image"}}, "type": "object", "required": ["birth_year", "birth_month", "id", "has_image"], "title": "ChildPublic"}, "ErrorModel": {"properties": {"detail": {"anyOf": [{"type": "string"}, {"additionalProperties": {"type": "string"}, "type": "object"}], "title": "Detail"}}, "type": "object", "required": ["detail"], "title": "ErrorModel"}, "HTTPValidationError": {"properties": {"detail": {"items": {"$ref": "#/components/schemas/ValidationError"}, "type": "array", "title": "Detail"}}, "type": "object", "title": "HTTPValidationError"}, "Language": {"properties": {"id": {"type": "string", "maxLength": 2, "title": "Id"}}, "type": "object", "required": ["id"], "title": "Language"}, "MilestoneAdmin": {"properties": {"id": {"type": "integer", "title": "Id"}, "group_id": {"type": "integer", "title": "Group Id"}, "order": {"type": "integer", "title": "Order"}, "text": {"additionalProperties": {"$ref": "#/components/schemas/MilestoneText"}, "type": "object", "title": "Text", "default": {}}, "images": {"items": {"$ref": "#/components/schemas/MilestoneImage"}, "type": "array", "title": "Images", "default": []}}, "type": "object", "required": ["id", "group_id", "order"], "title": "MilestoneAdmin"}, "MilestoneAgeGroupCreate": {"properties": {"months_min": {"type": "integer", "title": "Months Min"}, "months_max": {"type": "integer", "title": "Months Max"}}, "type": "object", "required": ["months_min", "months_max"], "title": "MilestoneAgeGroupCreate"}, "MilestoneAgeGroupPublic": {"properties": {"months_min": {"type": "integer", "title": "Months Min"}, "months_max": {"type": "integer", "title": "Months Max"}, "id": {"type": "integer", "title": "Id"}}, "type": "object", "required": ["months_min", "months_max", "id"], "title": "MilestoneAgeGroupPublic"}, "MilestoneAnswerPublic": {"properties": {"milestone_id": {"type": "integer", "title": "Milestone Id"}, "answer": {"type": "integer", "title": "Answer"}}, "type": "object", "required": ["milestone_id", "answer"], "title": "MilestoneAnswerPublic"}, "MilestoneAnswerSessionPublic": {"properties": {"id": {"type": "integer", "title": "Id"}, "child_id": {"type": "integer", "title": "Child Id"}, "age_group_id": {"type": "integer", "title": "Age Group Id"}, "created_at": {"type": "string", "format": "date-time", "title": "Created At"}, "answers": {"additionalProperties": {"$ref": "#/components/schemas/MilestoneAnswerPublic"}, "type": "object", "title": "Answers"}}, "type": "object", "required": ["id", "child_id", "age_group_id", "created_at", "answers"], "title": "MilestoneAnswerSessionPublic"}, "MilestoneGroupAdmin": {"properties": {"id": {"type": "integer", "title": "Id"}, "age_group_id": {"type": "integer", "title": "Age Group Id"}, "order": {"type": "integer", "title": "Order"}, "text": {"additionalProperties": {"$ref": "#/components/schemas/MilestoneGroupText"}, "type": "object", "title": "Text", "default": {}}, "milestones": {"items": {"$ref": "#/components/schemas/MilestoneAdmin"}, "type": "array", "title": "Milestones", "default": []}}, "type": "object", "required": ["id", "age_group_id", "order"], "title": "MilestoneGroupAdmin"}, "MilestoneGroupPublic": {"properties": {"id": {"type": "integer", "title": "Id"}, "text": {"additionalProperties": {"$ref": "#/components/schemas/MilestoneGroupTextPublic"}, "type": "object", "title": "Text", "default": {}}, "milestones": {"items": {"$ref": "#/components/schemas/MilestonePublic"}, "type": "array", "title": "Milestones", "default": []}}, "type": "object", "required": ["id"], "title": "MilestoneGroupPublic"}, "MilestoneGroupText": {"properties": {"title": {"type": "string", "title": "Title", "default": ""}, "desc": {"type": "string", "title": "Desc", "default": ""}, "group_id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Group Id"}, "lang_id": {"anyOf": [{"type": "string", "maxLength": 2}, {"type": "null"}], "title": "Lang Id"}}, "type": "object", "title": "MilestoneGroupText"}, "MilestoneGroupTextPublic": {"properties": {"title": {"type": "string", "title": "Title", "default": ""}, "desc": {"type": "string", "title": "Desc", "default": ""}}, "type": "object", "title": "MilestoneGroupTextPublic"}, "MilestoneImage": {"properties": {"id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Id"}, "milestone_id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Milestone Id"}, "filename": {"type": "string", "title": "Filename", "default": ""}, "approved": {"type": "boolean", "title": "Approved", "default": false}}, "type": "object", "title": "MilestoneImage"}, "MilestoneImagePublic": {"properties": {"filename": {"type": "string", "title": "Filename"}, "approved": {"type": "boolean", "title": "Approved"}}, "type": "object", "required": ["filename", "approved"], "title": "MilestoneImagePublic"}, "MilestonePublic": {"properties": {"id": {"type": "integer", "title": "Id"}, "text": {"additionalProperties": {"$ref": "#/components/schemas/MilestoneTextPublic"}, "type": "object", "title": "Text", "default": {}}, "images": {"items": {"$ref": "#/components/schemas/MilestoneImagePublic"}, "type": "array", "title": "Images", "default": []}}, "type": "object", "required": ["id"], "title": "MilestonePublic"}, "MilestoneText": {"properties": {"title": {"type": "string", "title": "Title", "default": ""}, "desc": {"type": "string", "title": "Desc", "default": ""}, "obs": {"type": "string", "title": "Obs", "default": ""}, "help": {"type": "string", "title": "Help", "default": ""}, "milestone_id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Milestone Id"}, "lang_id": {"anyOf": [{"type": "string", "maxLength": 2}, {"type": "null"}], "title": "Lang Id"}}, "type": "object", "title": "MilestoneText"}, "MilestoneTextPublic": {"properties": {"title": {"type": "string", "title": "Title", "default": ""}, "desc": {"type": "string", "title": "Desc", "default": ""}, "obs": {"type": "string", "title": "Obs", "default": ""}, "help": {"type": "string", "title": "Help", "default": ""}}, "type": "object", "title": "MilestoneTextPublic"}, "UserAnswerPublic": {"properties": {"answer": {"type": "string", "title": "Answer"}, "question_id": {"type": "integer", "title": "Question Id"}, "non_standard": {"type": "boolean", "title": "Non Standard"}}, "type": "object", "required": ["answer", "question_id", "non_standard"], "title": "UserAnswerPublic", "description": "External data model for UserAnswers\n\nParameters\n----------\nSQLModel : Pydantic model basic sqlmodel pydantic type"}, "UserCreate": {"properties": {"email": {"type": "string", "format": "email", "title": "Email"}, "password": {"type": "string", "title": "Password"}, "is_active": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Is Active", "default": true}, "is_superuser": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Is Superuser", "default": false}, "is_verified": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Is Verified", "default": false}, "is_researcher": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Is Researcher", "default": false}}, "type": "object", "required": ["email", "password"], "title": "UserCreate"}, "UserQuestionAdmin": {"properties": {"id": {"type": "integer", "title": "Id"}, "order": {"type": "integer", "title": "Order"}, "input": {"type": "string", "title": "Input"}, "options": {"type": "string", "title": "Options"}, "text": {"additionalProperties": {"$ref": "#/components/schemas/UserQuestionText"}, "type": "object", "title": "Text", "default": {}}}, "type": "object", "required": ["id", "order", "input", "options"], "title": "UserQuestionAdmin"}, "UserQuestionPublic": {"properties": {"id": {"type": "integer", "title": "Id"}, "input": {"type": "string", "title": "Input"}, "text": {"additionalProperties": {"$ref": "#/components/schemas/UserQuestionTextPublic"}, "type": "object", "title": "Text", "default": {}}}, "type": "object", "required": ["id", "input"], "title": "UserQuestionPublic"}, "UserQuestionText": {"properties": {"question": {"type": "string", "title": "Question", "default": ""}, "options_json": {"type": "string", "title": "Options Json", "default": ""}, "user_question_id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "User Question Id"}, "lang_id": {"anyOf": [{"type": "string", "maxLength": 2}, {"type": "null"}], "title": "Lang Id"}, "options": {"type": "string", "title": "Options", "default": ""}}, "type": "object", "title": "UserQuestionText"}, "UserQuestionTextPublic": {"properties": {"question": {"type": "string", "title": "Question", "default": ""}, "options_json": {"type": "string", "title": "Options Json", "default": ""}}, "type": "object", "title": "UserQuestionTextPublic"}, "UserRead": {"properties": {"id": {"type": "integer", "title": "Id"}, "email": {"type": "string", "format": "email", "title": "Email"}, "is_active": {"type": "boolean", "title": "Is Active", "default": true}, "is_superuser": {"type": "boolean", "title": "Is Superuser", "default": false}, "is_verified": {"type": "boolean", "title": "Is Verified", "default": false}, "is_researcher": {"type": "boolean", "title": "Is Researcher"}}, "type": "object", "required": ["id", "email", "is_researcher"], "title": "UserRead"}, "UserUpdate": {"properties": {"password": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Password"}, "email": {"anyOf": [{"type": "string", "format": "email"}, {"type": "null"}], "title": "Email"}, "is_active": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Is Active"}, "is_superuser": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Is Superuser"}, "is_verified": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Is Verified"}, "is_researcher": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Is Researcher"}}, "type": "object", "title": "UserUpdate"}, "ValidationError": {"properties": {"loc": {"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, "type": "array", "title": "Location"}, "msg": {"type": "string", "title": "Message"}, "type": {"type": "string", "title": "Error Type"}}, "type": "object", "required": ["loc", "msg", "type"], "title": "ValidationError"}}, "securitySchemes": {"APIKeyCookie": {"type": "apiKey", "in": "cookie", "name": "fastapiusersauth"}}}} \ No newline at end of file diff --git a/mondey_backend/src/mondey_backend/databases/milestones.py b/mondey_backend/src/mondey_backend/databases/milestones.py index d6bb61cc..bc7dcf08 100644 --- a/mondey_backend/src/mondey_backend/databases/milestones.py +++ b/mondey_backend/src/mondey_backend/databases/milestones.py @@ -19,6 +19,6 @@ def create_database(): with Session(engine) as session: # add "de" and "en" Languages if no languages are present in the database: if session.exec(select(Language)).first() is None: - session.add(Language(lang="de")) - session.add(Language(lang="en")) + session.add(Language(id="de")) + session.add(Language(id="en")) session.commit() diff --git a/mondey_backend/src/mondey_backend/models/milestones.py b/mondey_backend/src/mondey_backend/models/milestones.py index ad9ea109..d54a75d2 100644 --- a/mondey_backend/src/mondey_backend/models/milestones.py +++ b/mondey_backend/src/mondey_backend/models/milestones.py @@ -16,12 +16,7 @@ class Language(SQLModel, table=True): - id: int | None = Field(default=None, primary_key=True) - lang: str = fixed_length_string_field(2, index=True) - - -class LanguageCreate(SQLModel): - lang: str = fixed_length_string_field(2, index=True) + id: str = fixed_length_string_field(max_length=2, index=True, primary_key=True) ## MilestoneAgeGroup @@ -56,14 +51,14 @@ class MilestoneGroupText(MilestoneGroupTextBase, table=True): group_id: int | None = Field( default=None, foreign_key="milestonegroup.id", primary_key=True ) - lang_id: int | None = Field( - default=None, foreign_key="language.id", primary_key=True + lang_id: str | None = fixed_length_string_field( + max_length=2, default=None, foreign_key="language.id", primary_key=True ) class MilestoneGroupTextCreate(MilestoneGroupTextBase): group_id: int - lang_id: int + lang_id: str class MilestoneGroupTextPublic(MilestoneGroupTextBase): @@ -77,13 +72,13 @@ class MilestoneGroup(SQLModel, table=True): id: int | None = Field(default=None, primary_key=True) age_group_id: int = Field(foreign_key="milestoneagegroup.id") order: int = 0 - text: Mapped[dict[int, MilestoneGroupText]] = dict_relationship(key="lang_id") + text: Mapped[dict[str, MilestoneGroupText]] = dict_relationship(key="lang_id") milestones: Mapped[list[Milestone]] = back_populates("group") class MilestoneGroupPublic(SQLModel): id: int - text: dict[int, MilestoneGroupTextPublic] = {} + text: dict[str, MilestoneGroupTextPublic] = {} milestones: list[MilestonePublic] = [] @@ -91,7 +86,7 @@ class MilestoneGroupAdmin(SQLModel): id: int age_group_id: int order: int - text: dict[int, MilestoneGroupText] = {} + text: dict[str, MilestoneGroupText] = {} milestones: list[MilestoneAdmin] = [] @@ -109,8 +104,8 @@ class MilestoneText(MilestoneTextBase, table=True): milestone_id: int | None = Field( default=None, foreign_key="milestone.id", primary_key=True ) - lang_id: int | None = Field( - default=None, foreign_key="language.id", primary_key=True + lang_id: str | None = fixed_length_string_field( + max_length=2, default=None, foreign_key="language.id", primary_key=True ) @@ -126,13 +121,13 @@ class Milestone(SQLModel, table=True): group_id: int | None = Field(default=None, foreign_key="milestonegroup.id") order: int = 0 group: MilestoneGroup | None = back_populates("milestones") - text: Mapped[dict[int, MilestoneText]] = dict_relationship(key="lang_id") + text: Mapped[dict[str, MilestoneText]] = dict_relationship(key="lang_id") images: Mapped[list[MilestoneImage]] = back_populates("milestone") class MilestonePublic(SQLModel): id: int - text: dict[int, MilestoneTextPublic] = {} + text: dict[str, MilestoneTextPublic] = {} images: list[MilestoneImagePublic] = [] @@ -140,7 +135,7 @@ class MilestoneAdmin(SQLModel): id: int group_id: int order: int - text: dict[int, MilestoneText] = {} + text: dict[str, MilestoneText] = {} images: list[MilestoneImage] = [] diff --git a/mondey_backend/src/mondey_backend/models/questions.py b/mondey_backend/src/mondey_backend/models/questions.py index 816f52c9..e74c9240 100644 --- a/mondey_backend/src/mondey_backend/models/questions.py +++ b/mondey_backend/src/mondey_backend/models/questions.py @@ -1,12 +1,12 @@ from __future__ import annotations from sqlalchemy.orm import Mapped -from sqlalchemy.orm import attribute_keyed_dict -from sqlalchemy.orm import relationship from sqlmodel import Field -from sqlmodel import Relationship from sqlmodel import SQLModel +from .utils import dict_relationship +from .utils import fixed_length_string_field + class UserQuestionTextBase(SQLModel): question: str = "" @@ -17,8 +17,8 @@ class UserQuestionText(UserQuestionTextBase, table=True): user_question_id: int | None = Field( default=None, foreign_key="userquestion.id", primary_key=True ) - lang_id: int | None = Field( - default=None, foreign_key="language.id", primary_key=True + lang_id: str | None = fixed_length_string_field( + max_length=2, default=None, foreign_key="language.id", primary_key=True ) options: str = "" @@ -32,18 +32,13 @@ class UserQuestion(SQLModel, table=True): order: int = 0 input: str = "text" options: str = "" - text: Mapped[dict[int, UserQuestionText]] = Relationship( - sa_relationship=relationship( - collection_class=attribute_keyed_dict("lang_id"), - cascade="all, delete-orphan", - ) - ) + text: Mapped[dict[str, UserQuestionText]] = dict_relationship(key="lang_id") class UserQuestionPublic(SQLModel): id: int input: str - text: dict[int, UserQuestionTextPublic] = {} + text: dict[str, UserQuestionTextPublic] = {} class UserQuestionAdmin(SQLModel): @@ -51,7 +46,7 @@ class UserQuestionAdmin(SQLModel): order: int input: str options: str - text: dict[int, UserQuestionText] = {} + text: dict[str, UserQuestionText] = {} # Answers to user questions. Internal model and 'public' model exposed to the forntend app diff --git a/mondey_backend/src/mondey_backend/routers/admin.py b/mondey_backend/src/mondey_backend/routers/admin.py index 475301ba..90d03de6 100644 --- a/mondey_backend/src/mondey_backend/routers/admin.py +++ b/mondey_backend/src/mondey_backend/routers/admin.py @@ -12,7 +12,6 @@ from ..dependencies import AdminDep from ..dependencies import SessionDep from ..models.milestones import Language -from ..models.milestones import LanguageCreate from ..models.milestones import Milestone from ..models.milestones import MilestoneAdmin from ..models.milestones import MilestoneAgeGroup @@ -39,16 +38,18 @@ def create_router() -> APIRouter: router = APIRouter(prefix="/admin", tags=["admin"], dependencies=[AdminDep]) @router.post("/languages/", response_model=Language) - def create_language(session: SessionDep, language: LanguageCreate): + def create_language(session: SessionDep, language: Language): db_language = Language.model_validate(language) + if session.get(Language, db_language.id) is not None: + raise HTTPException(400, "Language already exists") add(session, db_language) return db_language @router.delete("/languages/{language_id}") - def delete_language(session: SessionDep, language_id: int): - if language_id <= 2: + def delete_language(session: SessionDep, language_id: str): + if language_id in ["de", "en"]: raise HTTPException( - status_code=400, detail="DE and EN languages cannot be deleted" + status_code=400, detail=f"{language_id} language cannot be deleted" ) language = get(session, Language, language_id) session.delete(language) @@ -56,11 +57,14 @@ def delete_language(session: SessionDep, language_id: int): return {"ok": True} @router.put("/i18n/{language_id}") - async def update_i18n(language_id: int, i18dict: dict[str, dict[str, str]]): + async def update_i18n( + session: SessionDep, language_id: str, i18dict: dict[str, dict[str, str]] + ): + language = get(session, Language, language_id) i18json_path = ( pathlib.Path(app_settings.STATIC_FILES_PATH) / "i18n" - / f"{language_id}.json" + / f"{language.id}.json" ) i18json_path.parent.mkdir(exist_ok=True) with open(i18json_path, "w", encoding="utf-8") as i18json_file: diff --git a/mondey_backend/src/mondey_backend/routers/milestones.py b/mondey_backend/src/mondey_backend/routers/milestones.py index 17aa3b9f..6080aaf9 100644 --- a/mondey_backend/src/mondey_backend/routers/milestones.py +++ b/mondey_backend/src/mondey_backend/routers/milestones.py @@ -18,14 +18,11 @@ def create_router() -> APIRouter: router = APIRouter(tags=["milestones"]) - @router.get("/languages/", response_model=dict[str, int]) + @router.get("/languages/", response_model=list[str]) def get_languages( session: SessionDep, ): - return { - language.lang: language.id - for language in session.exec(select(Language)).all() - } + return [language.id for language in session.exec(select(Language)).all()] @router.get("/milestones/", response_model=list[MilestonePublic]) def get_milestones( diff --git a/mondey_backend/src/mondey_backend/routers/utils.py b/mondey_backend/src/mondey_backend/routers/utils.py index 4824fbd5..af0b9647 100644 --- a/mondey_backend/src/mondey_backend/routers/utils.py +++ b/mondey_backend/src/mondey_backend/routers/utils.py @@ -40,7 +40,7 @@ def write_file(file: UploadFile, filename: str): file.file.close() -def get(session: SessionDep, entity: type, ident: int): +def get(session: SessionDep, entity: type, ident: int | str): instance = session.get(entity, ident) if not instance: raise HTTPException( diff --git a/mondey_backend/tests/conftest.py b/mondey_backend/tests/conftest.py index 7c0c6c24..185109d9 100644 --- a/mondey_backend/tests/conftest.py +++ b/mondey_backend/tests/conftest.py @@ -6,6 +6,11 @@ import pytest from fastapi import FastAPI from fastapi.testclient import TestClient +from sqlmodel import Session +from sqlmodel import SQLModel +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + from mondey_backend import settings from mondey_backend.dependencies import current_active_researcher from mondey_backend.dependencies import current_active_superuser @@ -24,10 +29,6 @@ from mondey_backend.models.milestones import MilestoneText from mondey_backend.models.questions import UserAnswer from mondey_backend.models.users import UserRead -from sqlmodel import Session -from sqlmodel import SQLModel -from sqlmodel import create_engine -from sqlmodel.pool import StaticPool @pytest.fixture(scope="session") @@ -67,16 +68,16 @@ def session(): # add some test data with Session(engine) as session: # add 3 languages - for lang in ["de", "en", "fr"]: - session.add(Language(lang=lang)) - lang_ids = [1, 2, 3] + lang_ids = ["de", "en", "fr"] + for lang_id in lang_ids: + session.add(Language(id=lang_id)) # add two milestone age groups - session.add(MilestoneAgeGroup(months_min=0, months_max=36, years=lang_ids)) - session.add(MilestoneAgeGroup(months_min=36, months_max=72, years=lang_ids)) + session.add(MilestoneAgeGroup(months_min=0, months_max=36)) + session.add(MilestoneAgeGroup(months_min=36, months_max=72)) # add a milestone group with 3 milestones session.add(MilestoneGroup(order=2, age_group_id=1)) for lang_id in lang_ids: - lbl = f"g1_l{lang_id}" + lbl = f"g1_{lang_id}" session.add( MilestoneGroupText( group_id=1, lang_id=lang_id, title=f"{lbl}_t", desc=f"{lbl}_d" @@ -85,7 +86,7 @@ def session(): for milestone_id in [1, 2, 3]: session.add(Milestone(order=0, group_id=1)) for lang_id in lang_ids: - lbl = f"m{milestone_id}_l{lang_id}" + lbl = f"m{milestone_id}_{lang_id}" session.add( MilestoneText( milestone_id=milestone_id, @@ -99,7 +100,7 @@ def session(): # add a second milestone group with 2 milestones session.add(MilestoneGroup(order=1, age_group_id=1)) for lang_id in lang_ids: - lbl = f"g1_l{lang_id}" + lbl = f"g1_{lang_id}" session.add( MilestoneGroupText( group_id=2, lang_id=lang_id, title=f"{lbl}_t", desc=f"{lbl}_d" @@ -108,7 +109,7 @@ def session(): for milestone_id in [4, 5]: session.add(Milestone(order=0, group_id=2)) for lang_id in lang_ids: - lbl = f"m{milestone_id}_l{lang_id}" + lbl = f"m{milestone_id}_{lang_id}" session.add( MilestoneText( milestone_id=milestone_id, @@ -350,31 +351,31 @@ def milestone_group1(): return { "id": 1, "text": { - "1": {"title": "g1_l1_t", "desc": "g1_l1_d"}, - "2": {"title": "g1_l2_t", "desc": "g1_l2_d"}, - "3": {"title": "g1_l3_t", "desc": "g1_l3_d"}, + "de": {"title": "g1_de_t", "desc": "g1_de_d"}, + "en": {"title": "g1_en_t", "desc": "g1_en_d"}, + "fr": {"title": "g1_fr_t", "desc": "g1_fr_d"}, }, "milestones": [ { "id": 1, "text": { - "1": { - "title": "m1_l1_t", - "desc": "m1_l1_d", - "obs": "m1_l1_o", - "help": "m1_l1_h", - }, - "2": { - "title": "m1_l2_t", - "desc": "m1_l2_d", - "obs": "m1_l2_o", - "help": "m1_l2_h", - }, - "3": { - "title": "m1_l3_t", - "desc": "m1_l3_d", - "obs": "m1_l3_o", - "help": "m1_l3_h", + "de": { + "title": "m1_de_t", + "desc": "m1_de_d", + "obs": "m1_de_o", + "help": "m1_de_h", + }, + "en": { + "title": "m1_en_t", + "desc": "m1_en_d", + "obs": "m1_en_o", + "help": "m1_en_h", + }, + "fr": { + "title": "m1_fr_t", + "desc": "m1_fr_d", + "obs": "m1_fr_o", + "help": "m1_fr_h", }, }, "images": [ @@ -385,23 +386,23 @@ def milestone_group1(): { "id": 2, "text": { - "1": { - "title": "m2_l1_t", - "desc": "m2_l1_d", - "obs": "m2_l1_o", - "help": "m2_l1_h", - }, - "2": { - "title": "m2_l2_t", - "desc": "m2_l2_d", - "obs": "m2_l2_o", - "help": "m2_l2_h", - }, - "3": { - "title": "m2_l3_t", - "desc": "m2_l3_d", - "obs": "m2_l3_o", - "help": "m2_l3_h", + "de": { + "title": "m2_de_t", + "desc": "m2_de_d", + "obs": "m2_de_o", + "help": "m2_de_h", + }, + "en": { + "title": "m2_en_t", + "desc": "m2_en_d", + "obs": "m2_en_o", + "help": "m2_en_h", + }, + "fr": { + "title": "m2_fr_t", + "desc": "m2_fr_d", + "obs": "m2_fr_o", + "help": "m2_fr_h", }, }, "images": [{"filename": "m3.jpg", "approved": True}], @@ -409,23 +410,23 @@ def milestone_group1(): { "id": 3, "text": { - "1": { - "title": "m3_l1_t", - "desc": "m3_l1_d", - "obs": "m3_l1_o", - "help": "m3_l1_h", - }, - "2": { - "title": "m3_l2_t", - "desc": "m3_l2_d", - "obs": "m3_l2_o", - "help": "m3_l2_h", - }, - "3": { - "title": "m3_l3_t", - "desc": "m3_l3_d", - "obs": "m3_l3_o", - "help": "m3_l3_h", + "de": { + "title": "m3_de_t", + "desc": "m3_de_d", + "obs": "m3_de_o", + "help": "m3_de_h", + }, + "en": { + "title": "m3_en_t", + "desc": "m3_en_d", + "obs": "m3_en_o", + "help": "m3_en_h", + }, + "fr": { + "title": "m3_fr_t", + "desc": "m3_fr_d", + "obs": "m3_fr_o", + "help": "m3_fr_h", }, }, "images": [], @@ -441,9 +442,24 @@ def milestone_group_admin1(): "age_group_id": 1, "order": 2, "text": { - "1": {"group_id": 1, "desc": "g1_l1_d", "title": "g1_l1_t", "lang_id": 1}, - "2": {"group_id": 1, "desc": "g1_l2_d", "title": "g1_l2_t", "lang_id": 2}, - "3": {"group_id": 1, "desc": "g1_l3_d", "title": "g1_l3_t", "lang_id": 3}, + "de": { + "group_id": 1, + "desc": "g1_de_d", + "title": "g1_de_t", + "lang_id": "de", + }, + "en": { + "group_id": 1, + "desc": "g1_en_d", + "title": "g1_en_t", + "lang_id": "en", + }, + "fr": { + "group_id": 1, + "desc": "g1_fr_d", + "title": "g1_fr_t", + "lang_id": "fr", + }, }, "milestones": [ { @@ -465,29 +481,29 @@ def milestone_group_admin1(): }, ], "text": { - "1": { - "obs": "m1_l1_o", - "help": "m1_l1_h", - "title": "m1_l1_t", - "desc": "m1_l1_d", + "de": { + "obs": "m1_de_o", + "help": "m1_de_h", + "title": "m1_de_t", + "desc": "m1_de_d", "milestone_id": 1, - "lang_id": 1, + "lang_id": "de", }, - "2": { - "obs": "m1_l2_o", - "help": "m1_l2_h", - "title": "m1_l2_t", - "desc": "m1_l2_d", + "en": { + "obs": "m1_en_o", + "help": "m1_en_h", + "title": "m1_en_t", + "desc": "m1_en_d", "milestone_id": 1, - "lang_id": 2, + "lang_id": "en", }, - "3": { - "obs": "m1_l3_o", - "help": "m1_l3_h", - "title": "m1_l3_t", - "desc": "m1_l3_d", + "fr": { + "obs": "m1_fr_o", + "help": "m1_fr_h", + "title": "m1_fr_t", + "desc": "m1_fr_d", "milestone_id": 1, - "lang_id": 3, + "lang_id": "fr", }, }, }, @@ -504,29 +520,29 @@ def milestone_group_admin1(): } ], "text": { - "1": { - "obs": "m2_l1_o", - "help": "m2_l1_h", - "title": "m2_l1_t", - "desc": "m2_l1_d", + "de": { + "obs": "m2_de_o", + "help": "m2_de_h", + "title": "m2_de_t", + "desc": "m2_de_d", "milestone_id": 2, - "lang_id": 1, + "lang_id": "de", }, - "2": { - "obs": "m2_l2_o", - "help": "m2_l2_h", - "title": "m2_l2_t", - "desc": "m2_l2_d", + "en": { + "obs": "m2_en_o", + "help": "m2_en_h", + "title": "m2_en_t", + "desc": "m2_en_d", "milestone_id": 2, - "lang_id": 2, + "lang_id": "en", }, - "3": { - "obs": "m2_l3_o", - "help": "m2_l3_h", - "title": "m2_l3_t", - "desc": "m2_l3_d", + "fr": { + "obs": "m2_fr_o", + "help": "m2_fr_h", + "title": "m2_fr_t", + "desc": "m2_fr_d", "milestone_id": 2, - "lang_id": 3, + "lang_id": "fr", }, }, }, @@ -536,29 +552,29 @@ def milestone_group_admin1(): "id": 3, "images": [], "text": { - "1": { - "obs": "m3_l1_o", - "help": "m3_l1_h", - "title": "m3_l1_t", - "desc": "m3_l1_d", + "de": { + "obs": "m3_de_o", + "help": "m3_de_h", + "title": "m3_de_t", + "desc": "m3_de_d", "milestone_id": 3, - "lang_id": 1, + "lang_id": "de", }, - "2": { - "obs": "m3_l2_o", - "help": "m3_l2_h", - "title": "m3_l2_t", - "desc": "m3_l2_d", + "en": { + "obs": "m3_en_o", + "help": "m3_en_h", + "title": "m3_en_t", + "desc": "m3_en_d", "milestone_id": 3, - "lang_id": 2, + "lang_id": "en", }, - "3": { - "obs": "m3_l3_o", - "help": "m3_l3_h", - "title": "m3_l3_t", - "desc": "m3_l3_d", + "fr": { + "obs": "m3_fr_o", + "help": "m3_fr_h", + "title": "m3_fr_t", + "desc": "m3_fr_d", "milestone_id": 3, - "lang_id": 3, + "lang_id": "fr", }, }, }, @@ -571,32 +587,32 @@ def milestone_group2(): return { "id": 2, "text": { - "1": {"title": "g1_l1_t", "desc": "g1_l1_d"}, - "2": {"title": "g1_l2_t", "desc": "g1_l2_d"}, - "3": {"title": "g1_l3_t", "desc": "g1_l3_d"}, + "de": {"title": "g1_de_t", "desc": "g1_de_d"}, + "en": {"title": "g1_en_t", "desc": "g1_en_d"}, + "fr": {"title": "g1_fr_t", "desc": "g1_fr_d"}, }, "milestones": [ { "id": 4, "images": [], "text": { - "1": { - "desc": "m4_l1_d", - "title": "m4_l1_t", - "obs": "m4_l1_o", - "help": "m4_l1_h", - }, - "2": { - "desc": "m4_l2_d", - "title": "m4_l2_t", - "obs": "m4_l2_o", - "help": "m4_l2_h", - }, - "3": { - "desc": "m4_l3_d", - "title": "m4_l3_t", - "obs": "m4_l3_o", - "help": "m4_l3_h", + "de": { + "desc": "m4_de_d", + "title": "m4_de_t", + "obs": "m4_de_o", + "help": "m4_de_h", + }, + "en": { + "desc": "m4_en_d", + "title": "m4_en_t", + "obs": "m4_en_o", + "help": "m4_en_h", + }, + "fr": { + "desc": "m4_fr_d", + "title": "m4_fr_t", + "obs": "m4_fr_o", + "help": "m4_fr_h", }, }, }, @@ -604,23 +620,23 @@ def milestone_group2(): "id": 5, "images": [], "text": { - "1": { - "desc": "m5_l1_d", - "title": "m5_l1_t", - "obs": "m5_l1_o", - "help": "m5_l1_h", - }, - "2": { - "desc": "m5_l2_d", - "title": "m5_l2_t", - "obs": "m5_l2_o", - "help": "m5_l2_h", - }, - "3": { - "desc": "m5_l3_d", - "title": "m5_l3_t", - "obs": "m5_l3_o", - "help": "m5_l3_h", + "de": { + "desc": "m5_de_d", + "title": "m5_de_t", + "obs": "m5_de_o", + "help": "m5_de_h", + }, + "en": { + "desc": "m5_en_d", + "title": "m5_en_t", + "obs": "m5_en_o", + "help": "m5_en_h", + }, + "fr": { + "desc": "m5_fr_d", + "title": "m5_fr_t", + "obs": "m5_fr_o", + "help": "m5_fr_h", }, }, }, @@ -635,9 +651,24 @@ def milestone_group_admin2(): "age_group_id": 1, "order": 1, "text": { - "1": {"group_id": 2, "desc": "g1_l1_d", "title": "g1_l1_t", "lang_id": 1}, - "2": {"group_id": 2, "desc": "g1_l2_d", "title": "g1_l2_t", "lang_id": 2}, - "3": {"group_id": 2, "desc": "g1_l3_d", "title": "g1_l3_t", "lang_id": 3}, + "de": { + "group_id": 2, + "desc": "g1_de_d", + "title": "g1_de_t", + "lang_id": "de", + }, + "en": { + "group_id": 2, + "desc": "g1_en_d", + "title": "g1_en_t", + "lang_id": "en", + }, + "fr": { + "group_id": 2, + "desc": "g1_fr_d", + "title": "g1_fr_t", + "lang_id": "fr", + }, }, "milestones": [ { @@ -646,29 +677,29 @@ def milestone_group_admin2(): "id": 4, "images": [], "text": { - "1": { - "obs": "m4_l1_o", - "help": "m4_l1_h", - "title": "m4_l1_t", - "desc": "m4_l1_d", + "de": { + "obs": "m4_de_o", + "help": "m4_de_h", + "title": "m4_de_t", + "desc": "m4_de_d", "milestone_id": 4, - "lang_id": 1, + "lang_id": "de", }, - "2": { - "obs": "m4_l2_o", - "help": "m4_l2_h", - "title": "m4_l2_t", - "desc": "m4_l2_d", + "en": { + "obs": "m4_en_o", + "help": "m4_en_h", + "title": "m4_en_t", + "desc": "m4_en_d", "milestone_id": 4, - "lang_id": 2, + "lang_id": "en", }, - "3": { - "obs": "m4_l3_o", - "help": "m4_l3_h", - "title": "m4_l3_t", - "desc": "m4_l3_d", + "fr": { + "obs": "m4_fr_o", + "help": "m4_fr_h", + "title": "m4_fr_t", + "desc": "m4_fr_d", "milestone_id": 4, - "lang_id": 3, + "lang_id": "fr", }, }, }, @@ -678,29 +709,29 @@ def milestone_group_admin2(): "id": 5, "images": [], "text": { - "1": { - "obs": "m5_l1_o", - "help": "m5_l1_h", - "title": "m5_l1_t", - "desc": "m5_l1_d", + "de": { + "obs": "m5_de_o", + "help": "m5_de_h", + "title": "m5_de_t", + "desc": "m5_de_d", "milestone_id": 5, - "lang_id": 1, + "lang_id": "de", }, - "2": { - "obs": "m5_l2_o", - "help": "m5_l2_h", - "title": "m5_l2_t", - "desc": "m5_l2_d", + "en": { + "obs": "m5_en_o", + "help": "m5_en_h", + "title": "m5_en_t", + "desc": "m5_en_d", "milestone_id": 5, - "lang_id": 2, + "lang_id": "en", }, - "3": { - "obs": "m5_l3_o", - "help": "m5_l3_h", - "title": "m5_l3_t", - "desc": "m5_l3_d", + "fr": { + "obs": "m5_fr_o", + "help": "m5_fr_h", + "title": "m5_fr_t", + "desc": "m5_fr_d", "milestone_id": 5, - "lang_id": 3, + "lang_id": "fr", }, }, }, diff --git a/mondey_backend/tests/routers/test_admin.py b/mondey_backend/tests/routers/test_admin.py index ef632d6b..338f1fa8 100644 --- a/mondey_backend/tests/routers/test_admin.py +++ b/mondey_backend/tests/routers/test_admin.py @@ -6,44 +6,49 @@ def test_post_language(admin_client: TestClient): - response = admin_client.post("/admin/languages/", json={"lang": "es"}) + response = admin_client.post("/admin/languages/", json={"id": "es"}) assert response.status_code == 200 - assert response.json() == {"id": 4, "lang": "es"} + assert response.json() == {"id": "es"} + + +def test_post_language_invalid_already_exists(admin_client: TestClient): + response = admin_client.post("/admin/languages/", json={"id": "de"}) + assert response.status_code == 400 def test_delete_language(admin_client: TestClient): - response = admin_client.delete("/admin/languages/3") + response = admin_client.delete("/admin/languages/fr") assert response.status_code == 200 - assert admin_client.get("/languages").json() == {"de": 1, "en": 2} + assert admin_client.get("/languages").json() == ["de", "en"] -@pytest.mark.parametrize("lang_id", [1, 2]) +@pytest.mark.parametrize("lang_id", ["de", "en"]) def test_delete_language_invalid_de_en_cannot_be_deleted( - admin_client: TestClient, lang_id: int + admin_client: TestClient, lang_id: str ): response = admin_client.delete(f"/admin/languages/{lang_id}") assert response.status_code == 400 def test_delete_language_invalid_language_id(admin_client: TestClient): - response = admin_client.delete("/admin/languages/67") + response = admin_client.delete("/admin/languages/zz") assert response.status_code == 404 def test_update_i18n(admin_client: TestClient, static_dir: pathlib.Path): - i18_json_path = static_dir / "i18n" / "1.json" + i18_json_path = static_dir / "i18n" / "nl.json" i18_json = { "s1": {"k1": "v1", "k2": "v2"}, "accents": {"k1": "v1", "äéœ": "óíüúëþ"}, } assert not i18_json_path.is_file() - response = admin_client.put("/admin/i18n/1", json=i18_json) + response = admin_client.put("/admin/i18n/nl", json=i18_json) assert response.status_code == 200 assert i18_json_path.is_file() with open(i18_json_path) as f: assert json.load(f) == i18_json i18_json["s1"]["k1"] = "MODIFIED!" - response = admin_client.put("/admin/i18n/1", json=i18_json) + response = admin_client.put("/admin/i18n/nl", json=i18_json) assert response.status_code == 200 with open(i18_json_path) as f: assert json.load(f) == i18_json @@ -54,7 +59,7 @@ def test_update_i18n_invalid_json(admin_client: TestClient, static_dir: pathlib. "valid-section": {"key1": "value1"}, "invalid-section": "this-value-should-be-a-dict!", } - response = admin_client.put("/admin/i18n/1", json=i18_json) + response = admin_client.put("/admin/i18n/en", json=i18_json) assert response.status_code == 422 @@ -103,21 +108,21 @@ def test_post_milestone_group(admin_client: TestClient): "age_group_id": 1, "order": 0, "text": { - "1": { + "de": { "group_id": 3, - "lang_id": 1, + "lang_id": "de", "title": "", "desc": "", }, - "2": { + "en": { "group_id": 3, - "lang_id": 2, + "lang_id": "en", "title": "", "desc": "", }, - "3": { + "fr": { "group_id": 3, - "lang_id": 3, + "lang_id": "fr", "title": "", "desc": "", }, @@ -129,10 +134,10 @@ def test_post_milestone_group(admin_client: TestClient): def test_put_milestone_group(admin_client: TestClient, milestone_group_admin1: dict): milestone_group = milestone_group_admin1 milestone_group["order"] = 6 - milestone_group["text"]["1"]["title"] = "asdsd" - milestone_group["text"]["1"]["desc"] = "12xzascdasdf" - milestone_group["text"]["2"]["title"] = "asqwdreqweqw" - milestone_group["text"]["2"]["desc"] = "th567" + milestone_group["text"]["de"]["title"] = "asdsd" + milestone_group["text"]["de"]["desc"] = "12xzascdasdf" + milestone_group["text"]["en"]["title"] = "asqwdreqweqw" + milestone_group["text"]["en"]["desc"] = "th567" response = admin_client.put("/admin/milestone-groups", json=milestone_group) assert response.status_code == 200 assert response.json() == milestone_group @@ -154,7 +159,7 @@ def test_delete_milestone_group_invalid_group_id(admin_client: TestClient): @pytest.mark.parametrize( "endpoint", - ["/admin/languages/1", "/admin/milestone-groups/1", "/admin/milestones/1"], + ["/admin/languages/fr", "/admin/milestone-groups/1", "/admin/milestones/1"], ) @pytest.mark.parametrize( "client_type", ["public_client", "user_client", "research_client"] @@ -189,25 +194,25 @@ def test_post_milestone(admin_client: TestClient): "group_id": 2, "order": 0, "text": { - "1": { + "de": { "milestone_id": 6, - "lang_id": 1, + "lang_id": "de", "title": "", "desc": "", "obs": "", "help": "", }, - "2": { + "en": { "milestone_id": 6, - "lang_id": 2, + "lang_id": "en", "title": "", "desc": "", "obs": "", "help": "", }, - "3": { + "fr": { "milestone_id": 6, - "lang_id": 3, + "lang_id": "fr", "title": "", "desc": "", "obs": "", @@ -221,12 +226,12 @@ def test_post_milestone(admin_client: TestClient): def test_put_milestone(admin_client: TestClient, milestone_group_admin1: dict): milestone = milestone_group_admin1["milestones"][0] milestone["order"] = 6 - milestone["text"]["1"]["title"] = "asdsd" - milestone["text"]["1"]["desc"] = "12xzascdasdf" - milestone["text"]["1"]["obs"] = "asdrgf" - milestone["text"]["1"]["help"] = "jgfhj" - milestone["text"]["2"]["title"] = "asqwdreqweqw" - milestone["text"]["2"]["desc"] = "th567" + milestone["text"]["de"]["title"] = "asdsd" + milestone["text"]["de"]["desc"] = "12xzascdasdf" + milestone["text"]["de"]["obs"] = "asdrgf" + milestone["text"]["de"]["help"] = "jgfhj" + milestone["text"]["en"]["title"] = "asqwdreqweqw" + milestone["text"]["en"]["desc"] = "th567" response = admin_client.put("/admin/milestones", json=milestone) assert response.status_code == 200 assert response.json() == milestone diff --git a/mondey_backend/tests/routers/test_milestones.py b/mondey_backend/tests/routers/test_milestones.py index 0ad8904a..74923b0b 100644 --- a/mondey_backend/tests/routers/test_milestones.py +++ b/mondey_backend/tests/routers/test_milestones.py @@ -9,7 +9,7 @@ def test_get_languages(self, client_type: str, request: pytest.FixtureRequest): client = request.getfixturevalue(client_type) response = client.get("/languages/") assert response.status_code == 200 - assert response.json() == {"de": 1, "en": 2, "fr": 3} + assert response.json() == ["de", "en", "fr"] def test_get_milestone_groups_age_group_1( self, From b566083b6659b30537dfe99ebbc2c6906bc243a1 Mon Sep 17 00:00:00 2001 From: Liam Keegan Date: Thu, 24 Oct 2024 12:55:29 +0200 Subject: [PATCH 2/2] fix test --- mondey_backend/tests/routers/test_admin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mondey_backend/tests/routers/test_admin.py b/mondey_backend/tests/routers/test_admin.py index 338f1fa8..fc1d02bd 100644 --- a/mondey_backend/tests/routers/test_admin.py +++ b/mondey_backend/tests/routers/test_admin.py @@ -36,6 +36,7 @@ def test_delete_language_invalid_language_id(admin_client: TestClient): def test_update_i18n(admin_client: TestClient, static_dir: pathlib.Path): + assert admin_client.post("/admin/languages/", json={"id": "nl"}).status_code == 200 i18_json_path = static_dir / "i18n" / "nl.json" i18_json = { "s1": {"k1": "v1", "k2": "v2"},