assessment question domain fix

This commit is contained in:
Yared Yemane 2026-01-21 13:05:18 -08:00
parent 68472b09b1
commit d691edae8b
5 changed files with 1289 additions and 1120 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,42 +1,42 @@
definitions:
authentication.LoginRequest:
properties:
email:
domain.AgeGroup:
enum:
- UNDER_13
- "13_17"
- "18_24"
- "25_34"
- "35_44"
- "45_54"
- 55_PLUS
type: string
otp_code:
type: string
password:
type: string
phone_number:
type: string
type: object
domain.AssessmentOption:
properties:
id:
format: int64
type: integer
isCorrect:
type: boolean
optionText:
type: string
type: object
x-enum-varnames:
- AgeUnder13
- Age13To17
- Age18To24
- Age25To34
- Age35To44
- Age45To54
- Age55Plus
domain.AssessmentQuestion:
properties:
created_at:
type: string
description:
type: string
difficultyLevel:
difficulty_level:
type: string
id:
format: int64
type: integer
options:
items:
$ref: '#/definitions/domain.AssessmentOption'
type: array
questionType:
is_active:
type: boolean
points:
type: integer
question_type:
type: string
title:
type: string
updated_at:
type: string
type: object
domain.Course:
properties:
@ -66,6 +66,40 @@ definitions:
description: '"Learning English", "Other Courses"'
type: string
type: object
domain.CreateAssessmentQuestionInput:
properties:
correctAnswer:
description: Short Answer only
type: string
description:
type: string
difficultyLevel:
type: string
isActive:
type: boolean
options:
description: Multiple Choice only
items:
$ref: '#/definitions/domain.CreateQuestionOptionInput'
type: array
points:
format: int32
type: integer
questionType:
$ref: '#/definitions/domain.QuestionType'
title:
type: string
type: object
domain.CreateQuestionOptionInput:
properties:
isCorrect:
type: boolean
order:
format: int32
type: integer
text:
type: string
type: object
domain.ErrorResponse:
properties:
error:
@ -129,6 +163,17 @@ definitions:
pagination:
$ref: '#/definitions/domain.Pagination'
type: object
domain.LoginRequest:
properties:
email:
type: string
otp_code:
type: string
password:
type: string
phone_number:
type: string
type: object
domain.Module:
properties:
content:
@ -268,46 +313,44 @@ definitions:
description: '"public", "private", "unlisted"'
type: string
type: object
domain.QuestionOption:
properties:
option_text:
type: string
question_id:
type: integer
type: object
domain.QuestionType:
enum:
- MULTIPLE_CHOICE
- TRUE_FALSE
- SHORT_ANSWER
type: string
x-enum-varnames:
- MultipleChoice
- TrueFalse
- ShortAnswer
domain.QuestionWithDetails:
properties:
options:
items:
$ref: '#/definitions/domain.QuestionOption'
type: array
question:
$ref: '#/definitions/domain.AssessmentQuestion'
type: object
domain.RegisterUserReq:
properties:
age:
type: integer
country:
type: string
education_level:
type: string
email:
type: string
favoutite_topic:
type: string
first_name:
type: string
language_challange:
type: string
language_goal:
type: string
last_name:
type: string
learning_goal:
type: string
nick_name:
type: string
occupation:
type: string
otp_medium:
$ref: '#/definitions/domain.OtpMedium'
password:
type: string
phone_number:
type: string
preferred_language:
type: string
region:
type: string
role:
type: string
user_name:
type: string
type: object
domain.ResendOtpReq:
properties:
@ -351,52 +394,52 @@ definitions:
type: object
domain.UpdateUserReq:
properties:
age:
$ref: '#/definitions/domain.ValidInt'
age_group:
$ref: '#/definitions/domain.AgeGroup'
birth_day:
description: YYYY-MM-DD
type: string
country:
$ref: '#/definitions/domain.ValidString'
educationLevel:
$ref: '#/definitions/domain.ValidString'
favoutiteTopic:
$ref: '#/definitions/domain.ValidString'
firstName:
$ref: '#/definitions/domain.ValidString'
knowledgeLevel:
allOf:
- $ref: '#/definitions/domain.ValidString'
description: Profile fields
languageChallange:
$ref: '#/definitions/domain.ValidString'
languageGoal:
$ref: '#/definitions/domain.ValidString'
lastName:
$ref: '#/definitions/domain.ValidString'
learningGoal:
$ref: '#/definitions/domain.ValidString'
nickName:
$ref: '#/definitions/domain.ValidString'
type: string
education_level:
type: string
favourite_topic:
type: string
first_name:
type: string
gender:
type: string
initial_assessment_completed:
type: boolean
knowledge_level:
type: string
language_challange:
type: string
language_goal:
type: string
last_name:
type: string
learning_goal:
type: string
nick_name:
type: string
occupation:
$ref: '#/definitions/domain.ValidString'
preferredLanguage:
$ref: '#/definitions/domain.ValidString'
profileCompleted:
$ref: '#/definitions/domain.ValidBool'
profilePictureURL:
$ref: '#/definitions/domain.ValidString'
type: string
preferred_language:
type: string
profile_completed:
type: boolean
profile_picture_url:
type: string
region:
$ref: '#/definitions/domain.ValidString'
status:
$ref: '#/definitions/domain.ValidString'
userID:
format: int64
type: integer
userName:
$ref: '#/definitions/domain.ValidString'
type: string
type: object
domain.UserProfileResponse:
properties:
age:
type: integer
age_group:
type: string
birth_day:
type: string
country:
type: string
created_at:
@ -404,6 +447,7 @@ definitions:
education_level:
type: string
email:
description: UserName string `json:"user_name,omitempty"`
type: string
email_verified:
type: boolean
@ -411,6 +455,8 @@ definitions:
type: string
first_name:
type: string
gender:
type: string
id:
type: integer
initial_assessment_completed:
@ -448,8 +494,6 @@ definitions:
$ref: '#/definitions/domain.UserStatus'
updated_at:
type: string
user_name:
type: string
type: object
domain.UserStatus:
enum:
@ -463,27 +507,6 @@ definitions:
- UserStatusActive
- UserStatusSuspended
- UserStatusDeactivated
domain.ValidBool:
properties:
valid:
type: boolean
value:
type: boolean
type: object
domain.ValidInt:
properties:
valid:
type: boolean
value:
type: integer
type: object
domain.ValidString:
properties:
valid:
type: boolean
value:
type: string
type: object
domain.VerifyOtpReq:
properties:
email:
@ -615,21 +638,6 @@ definitions:
type: string
type: object
handlers.ResetPasswordReq:
properties:
otp:
example: "123456"
type: string
password:
example: newpassword123
minLength: 8
type: string
user_name:
example: johndoe
type: string
required:
- otp
- password
- user_name
type: object
handlers.SearchUserByNameOrPhoneReq:
properties:
@ -740,7 +748,7 @@ paths:
name: login
required: true
schema:
$ref: '#/definitions/authentication.LoginRequest'
$ref: '#/definitions/domain.LoginRequest'
produces:
- application/json
responses:
@ -793,45 +801,6 @@ paths:
summary: Resend OTP
tags:
- otp
/api/v1/{tenant_slug}/user:
put:
consumes:
- application/json
description: Updates user profile information (partial updates supported)
parameters:
- description: User ID
in: path
name: user_id
required: true
type: integer
- description: Update user payload
in: body
name: body
required: true
schema:
$ref: '#/definitions/domain.UpdateUserReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/domain.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/domain.ErrorResponse'
"404":
description: Not Found
schema:
$ref: '#/definitions/domain.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/domain.ErrorResponse'
summary: Update user profile
tags:
- user
/api/v1/{tenant_slug}/user-login:
post:
consumes:
@ -843,7 +812,7 @@ paths:
name: login
required: true
schema:
$ref: '#/definitions/authentication.LoginRequest'
$ref: '#/definitions/domain.LoginRequest'
produces:
- application/json
responses:
@ -866,39 +835,6 @@ paths:
summary: Login user
tags:
- auth
/api/v1/{tenant_slug}/user/{user_name}/is-pending:
get:
consumes:
- application/json
description: Returns whether the specified user has a status of "pending"
parameters:
- description: User Name
in: path
name: user_name
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/domain.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/domain.ErrorResponse'
"404":
description: Not Found
schema:
$ref: '#/definitions/domain.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/domain.ErrorResponse'
summary: Check if user status is pending
tags:
- user
/api/v1/{tenant_slug}/user/admin-profile:
get:
consumes:
@ -1139,54 +1075,6 @@ paths:
summary: Get user profile
tags:
- user
/api/v1/{tenant_slug}/users:
get:
consumes:
- application/json
description: Get users with optional filters
parameters:
- description: Role filter
in: query
name: role
type: string
- description: Search query
in: query
name: query
type: string
- description: Page number
in: query
name: page
type: integer
- description: Page size
in: query
name: page_size
type: integer
- description: Created before (RFC3339)
in: query
name: created_before
type: string
- description: Created after (RFC3339)
in: query
name: created_after
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.APIResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Get all users
tags:
- user
/api/v1/admin:
get:
consumes:
@ -1324,53 +1212,42 @@ paths:
- admin
/api/v1/assessment/questions:
get:
consumes:
- application/json
description: Returns all active questions used for initial knowledge assessment
description: Returns all active assessment questions with their options or answers
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/domain.Response'
- properties:
data:
items:
$ref: '#/definitions/domain.AssessmentQuestion'
$ref: '#/definitions/domain.QuestionWithDetails'
type: array
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/domain.ErrorResponse'
summary: Get active initial assessment questions
summary: List assessment questions
tags:
- assessment
- assessment-question
post:
consumes:
- application/json
description: Creates a new question for the initial knowledge assessment
description: Creates a new assessment question with options or short answer
depending on question type
parameters:
- description: Assessment question payload
- description: Create question payload
in: body
name: question
name: body
required: true
schema:
$ref: '#/definitions/domain.AssessmentQuestion'
$ref: '#/definitions/domain.CreateAssessmentQuestionInput'
produces:
- application/json
responses:
"201":
description: Created
schema:
allOf:
- $ref: '#/definitions/domain.Response'
- properties:
data:
$ref: '#/definitions/domain.AssessmentQuestion'
type: object
$ref: '#/definitions/domain.Response'
"400":
description: Bad Request
schema:
@ -1381,7 +1258,50 @@ paths:
$ref: '#/definitions/domain.ErrorResponse'
summary: Create assessment question
tags:
- assessment
- assessment-question
/api/v1/assessment/questions/{id}:
get:
description: Returns a single assessment question with its options or answer
parameters:
- description: Question ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/domain.QuestionWithDetails'
"400":
description: Bad Request
schema:
$ref: '#/definitions/domain.ErrorResponse'
"404":
description: Not Found
schema:
$ref: '#/definitions/domain.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/domain.ErrorResponse'
summary: Get assessment question by ID
tags:
- assessment-question
/api/v1/auth/google/callback:
get:
responses: {}
summary: Google login callback
tags:
- auth
/api/v1/auth/google/login:
get:
responses: {}
summary: Google login redirect
tags:
- auth
/api/v1/auth/logout:
post:
consumes:
@ -2102,7 +2022,7 @@ paths:
name: login
required: true
schema:
$ref: '#/definitions/authentication.LoginRequest'
$ref: '#/definitions/domain.LoginRequest'
produces:
- application/json
responses:
@ -2221,15 +2141,54 @@ paths:
summary: Check if phone number or email exist
tags:
- user
/api/v1/user/{user_name}/is-unique:
/api/v1/user:
put:
consumes:
- application/json
description: Updates user profile information (partial updates supported)
parameters:
- description: User ID
in: path
name: user_id
required: true
type: integer
- description: Update user payload
in: body
name: body
required: true
schema:
$ref: '#/definitions/domain.UpdateUserReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/domain.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/domain.ErrorResponse'
"404":
description: Not Found
schema:
$ref: '#/definitions/domain.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/domain.ErrorResponse'
summary: Update user profile
tags:
- user
/api/v1/user/{user_id}/is-pending:
get:
consumes:
- application/json
description: Returns whether the specified user_name is available (unique)
description: Returns whether the specified user has a status of "pending"
parameters:
- description: User Name
- description: User ID
in: path
name: user_name
name: user_id
required: true
type: string
produces:
@ -2243,11 +2202,48 @@ paths:
description: Bad Request
schema:
$ref: '#/definitions/domain.ErrorResponse'
"404":
description: Not Found
schema:
$ref: '#/definitions/domain.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/domain.ErrorResponse'
summary: Check if user_name is unique
summary: Check if user status is pending
tags:
- user
/api/v1/user/{user_id}/is-profile-completed:
get:
consumes:
- application/json
description: Returns whether the specified user's profile is completed
parameters:
- description: User ID
in: path
name: user_id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/domain.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/domain.ErrorResponse'
"404":
description: Not Found
schema:
$ref: '#/definitions/domain.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/domain.ErrorResponse'
summary: Check if user profile is completed
tags:
- user
/api/v1/user/delete/{id}:
@ -2432,6 +2428,54 @@ paths:
summary: Verify OTP
tags:
- user
/api/v1/users:
get:
consumes:
- application/json
description: Get users with optional filters
parameters:
- description: Role filter
in: query
name: role
type: string
- description: Search query
in: query
name: query
type: string
- description: Page number
in: query
name: page
type: integer
- description: Page size
in: query
name: page_size
type: integer
- description: Created before (RFC3339)
in: query
name: created_before
type: string
- description: Created after (RFC3339)
in: query
name: created_after
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.APIResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Get all users
tags:
- user
securityDefinitions:
Bearer:
in: header

View File

@ -1,6 +1,8 @@
package domain
import dbgen "Yimaru-Backend/gen/db"
import (
"time"
)
type QuestionType string
@ -10,8 +12,20 @@ const (
ShortAnswer QuestionType = "SHORT_ANSWER"
)
type AssessmentQuestion struct {
ID int64 `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
QuestionType string `json:"question_type"`
DifficultyLevel string `json:"difficulty_level"`
Points int32 `json:"points"`
IsActive bool `json:"is_active"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type QuestionWithDetails struct {
Question dbgen.AssessmentQuestion
Question AssessmentQuestion
Options []QuestionOption
}

View File

@ -5,6 +5,7 @@ import (
"Yimaru-Backend/internal/domain"
"context"
"errors"
"encoding/json"
"github.com/jackc/pgx/v5/pgtype"
)
@ -117,7 +118,15 @@ func (s *Service) ListQuestions(ctx context.Context) ([]domain.QuestionWithDetai
out := make([]domain.QuestionWithDetails, 0, len(questions))
for _, q := range questions {
item := domain.QuestionWithDetails{Question: q}
var dq domain.AssessmentQuestion
b, err := json.Marshal(q)
if err != nil {
return nil, err
}
if err := json.Unmarshal(b, &dq); err != nil {
return nil, err
}
item := domain.QuestionWithDetails{Question: dq}
switch domain.QuestionType(q.QuestionType) {
case domain.MultipleChoice, domain.TrueFalse:
@ -156,7 +165,15 @@ func (s *Service) GetQuestionByID(ctx context.Context, id int64) (domain.Questio
return domain.QuestionWithDetails{}, err
}
item := domain.QuestionWithDetails{Question: q}
var dq domain.AssessmentQuestion
b, err := json.Marshal(q)
if err != nil {
return domain.QuestionWithDetails{}, err
}
if err := json.Unmarshal(b, &dq); err != nil {
return domain.QuestionWithDetails{}, err
}
item := domain.QuestionWithDetails{Question: dq}
switch domain.QuestionType(q.QuestionType) {
case domain.MultipleChoice, domain.TrueFalse:
opts, err := repo.GetQuestionOptions(ctx, q.ID)