lifedebugger commited on
Commit
ce8ca59
·
1 Parent(s): 864a875

Deploy files from GitHub repository

Browse files
Quzuu_API_Collection.postman_collection.json CHANGED
@@ -4041,7 +4041,7 @@
4041
  }
4042
  ],
4043
  "url": {
4044
- "raw": "{{base_url}}/api/v1/admin/problemsets",
4045
  "host": [
4046
  "{{base_url}}"
4047
  ],
@@ -4050,6 +4050,33 @@
4050
  "v1",
4051
  "admin",
4052
  "problemsets"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4053
  ]
4054
  }
4055
  },
@@ -4177,7 +4204,7 @@
4177
  }
4178
  ],
4179
  "url": {
4180
- "raw": "{{base_url}}/api/v1/admin/problemsets/:id/questions",
4181
  "host": [
4182
  "{{base_url}}"
4183
  ],
@@ -4194,6 +4221,33 @@
4194
  "key": "id",
4195
  "value": "{{problemset_id}}"
4196
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4197
  ]
4198
  }
4199
  },
 
4041
  }
4042
  ],
4043
  "url": {
4044
+ "raw": "{{base_url}}/api/v1/admin/problemsets?page=1&limit=10&search=&sortBy=title&orderBy=asc",
4045
  "host": [
4046
  "{{base_url}}"
4047
  ],
 
4050
  "v1",
4051
  "admin",
4052
  "problemsets"
4053
+ ],
4054
+ "query": [
4055
+ {
4056
+ "key": "page",
4057
+ "value": "1",
4058
+ "description": "Page number"
4059
+ },
4060
+ {
4061
+ "key": "limit",
4062
+ "value": "10",
4063
+ "description": "Items per page (max 100)"
4064
+ },
4065
+ {
4066
+ "key": "search",
4067
+ "value": "",
4068
+ "description": "Search by title / description"
4069
+ },
4070
+ {
4071
+ "key": "sortBy",
4072
+ "value": "title",
4073
+ "description": "Sort field (title, description, created_at)"
4074
+ },
4075
+ {
4076
+ "key": "orderBy",
4077
+ "value": "asc",
4078
+ "description": "Sort direction (asc / desc)"
4079
+ }
4080
  ]
4081
  }
4082
  },
 
4204
  }
4205
  ],
4206
  "url": {
4207
+ "raw": "{{base_url}}/api/v1/admin/problemsets/:id/questions?page=1&limit=10&search=&sortBy=id&orderBy=asc",
4208
  "host": [
4209
  "{{base_url}}"
4210
  ],
 
4221
  "key": "id",
4222
  "value": "{{problemset_id}}"
4223
  }
4224
+ ],
4225
+ "query": [
4226
+ {
4227
+ "key": "page",
4228
+ "value": "1",
4229
+ "description": "Page number"
4230
+ },
4231
+ {
4232
+ "key": "limit",
4233
+ "value": "10",
4234
+ "description": "Items per page (max 100)"
4235
+ },
4236
+ {
4237
+ "key": "search",
4238
+ "value": "",
4239
+ "description": "Search by question / type"
4240
+ },
4241
+ {
4242
+ "key": "sortBy",
4243
+ "value": "id",
4244
+ "description": "Sort field (id, type, question, corr_mark, incorr_mark, null_mark)"
4245
+ },
4246
+ {
4247
+ "key": "orderBy",
4248
+ "value": "asc",
4249
+ "description": "Sort direction (asc / desc)"
4250
+ }
4251
  ]
4252
  }
4253
  },
controllers/admin_problemset_controller.go CHANGED
@@ -36,8 +36,57 @@ func NewAdminProblemSetController(problemSetService services.ProblemSetService)
36
  }
37
 
38
  func (c *adminProblemSetController) ListProblemSets(ctx *gin.Context) {
39
- list, err := c.problemSetService.ListProblemSets(ctx.Request.Context())
40
- ResponseJSON(ctx, gin.H{}, list, err)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  }
42
 
43
  func (c *adminProblemSetController) CreateProblemSet(ctx *gin.Context) {
@@ -93,8 +142,59 @@ func (c *adminProblemSetController) ListQuestions(ctx *gin.Context) {
93
  ResponseJSON[any](ctx, gin.H{"id": ctx.Param("id")}, nil, http_error.INVALID_TOKEN)
94
  return
95
  }
96
- list, listErr := c.problemSetService.ListQuestions(ctx.Request.Context(), problemSetId)
97
- ResponseJSON(ctx, gin.H{"problemset_id": problemSetId}, list, listErr)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  }
99
 
100
  func (c *adminProblemSetController) AddQuestion(ctx *gin.Context) {
 
36
  }
37
 
38
  func (c *adminProblemSetController) ListProblemSets(ctx *gin.Context) {
39
+ limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10"))
40
+ page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
41
+ search := ctx.DefaultQuery("search", "")
42
+ sortBy := ctx.DefaultQuery("sortBy", "")
43
+ if sortBy == "" {
44
+ sortBy = ctx.DefaultQuery("sortby", "")
45
+ }
46
+ if sortBy == "" {
47
+ sortBy = ctx.DefaultQuery("sorby", "")
48
+ }
49
+ order := ctx.DefaultQuery("orderBy", "")
50
+ if order == "" {
51
+ order = ctx.DefaultQuery("orderby", "")
52
+ }
53
+ if order == "" {
54
+ order = ctx.DefaultQuery("order", "")
55
+ }
56
+
57
+ if limit < 1 {
58
+ limit = 10
59
+ } else if limit > 100 {
60
+ limit = 100
61
+ }
62
+ if page < 1 {
63
+ page = 1
64
+ }
65
+
66
+ offset := (page - 1) * limit
67
+ p := entity.Pagination{Limit: limit, Offset: offset, Search: search, SortBy: sortBy, Order: order}
68
+
69
+ list, total, err := c.problemSetService.ListProblemSetsWithPagination(ctx.Request.Context(), p)
70
+ if err != nil {
71
+ ResponseJSON[any, any](ctx, nil, nil, err)
72
+ return
73
+ }
74
+
75
+ totalPages := int((total + int64(limit) - 1) / int64(limit))
76
+ if total == 0 {
77
+ totalPages = 1
78
+ }
79
+ if page > totalPages {
80
+ page = totalPages
81
+ }
82
+
83
+ meta := gin.H{
84
+ "totalItems": total,
85
+ "totalPages": totalPages,
86
+ "currentPage": page,
87
+ "limit": limit,
88
+ }
89
+ ResponseJSON(ctx, meta, list, nil)
90
  }
91
 
92
  func (c *adminProblemSetController) CreateProblemSet(ctx *gin.Context) {
 
142
  ResponseJSON[any](ctx, gin.H{"id": ctx.Param("id")}, nil, http_error.INVALID_TOKEN)
143
  return
144
  }
145
+
146
+ limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10"))
147
+ page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
148
+ search := ctx.DefaultQuery("search", "")
149
+ sortBy := ctx.DefaultQuery("sortBy", "")
150
+ if sortBy == "" {
151
+ sortBy = ctx.DefaultQuery("sortby", "")
152
+ }
153
+ if sortBy == "" {
154
+ sortBy = ctx.DefaultQuery("sorby", "")
155
+ }
156
+ order := ctx.DefaultQuery("orderBy", "")
157
+ if order == "" {
158
+ order = ctx.DefaultQuery("orderby", "")
159
+ }
160
+ if order == "" {
161
+ order = ctx.DefaultQuery("order", "")
162
+ }
163
+
164
+ if limit < 1 {
165
+ limit = 10
166
+ } else if limit > 100 {
167
+ limit = 100
168
+ }
169
+ if page < 1 {
170
+ page = 1
171
+ }
172
+
173
+ offset := (page - 1) * limit
174
+ p := entity.Pagination{Limit: limit, Offset: offset, Search: search, SortBy: sortBy, Order: order}
175
+
176
+ list, total, listErr := c.problemSetService.ListQuestionsWithPagination(ctx.Request.Context(), problemSetId, p)
177
+ if listErr != nil {
178
+ ResponseJSON[any, any](ctx, nil, nil, listErr)
179
+ return
180
+ }
181
+
182
+ totalPages := int((total + int64(limit) - 1) / int64(limit))
183
+ if total == 0 {
184
+ totalPages = 1
185
+ }
186
+ if page > totalPages {
187
+ page = totalPages
188
+ }
189
+
190
+ meta := gin.H{
191
+ "problemset_id": problemSetId,
192
+ "totalItems": total,
193
+ "totalPages": totalPages,
194
+ "currentPage": page,
195
+ "limit": limit,
196
+ }
197
+ ResponseJSON(ctx, meta, list, nil)
198
  }
199
 
200
  func (c *adminProblemSetController) AddQuestion(ctx *gin.Context) {
repositories/problem_set_repository.go CHANGED
@@ -1,57 +1,102 @@
1
- package repositories
2
-
3
- import (
4
- "context"
5
-
6
- entity "abdanhafidz.com/go-boilerplate/models/entity"
7
- "github.com/google/uuid"
8
- "gorm.io/gorm"
9
- )
10
-
11
- type ProblemSetRepository interface {
12
- Create(ctx context.Context, ps entity.ProblemSet) error
13
- Get(ctx context.Context, id uuid.UUID) (entity.ProblemSet, error)
14
- Update(ctx context.Context, ps entity.ProblemSet) error
15
- Delete(ctx context.Context, id uuid.UUID) error
16
- List(ctx context.Context) ([]entity.ProblemSet, error)
17
- }
18
-
19
- type problemSetRepository struct {
20
- db *gorm.DB
21
- }
22
-
23
- func NewProblemSetRepository(db *gorm.DB) ProblemSetRepository {
24
- return &problemSetRepository{db: db}
25
- }
26
-
27
- func (r *problemSetRepository) Create(ctx context.Context, ps entity.ProblemSet) error {
28
- return r.db.WithContext(ctx).Create(&ps).Error
29
- }
30
-
31
- func (r *problemSetRepository) Get(ctx context.Context, id uuid.UUID) (entity.ProblemSet, error) {
32
- var ps entity.ProblemSet
33
- err := r.db.WithContext(ctx).
34
- First(&ps, "id = ?", id).Error
35
- return ps, err
36
- }
37
-
38
- func (r *problemSetRepository) List(ctx context.Context) ([]entity.ProblemSet, error) {
39
- var list []entity.ProblemSet
40
- err := r.db.WithContext(ctx).
41
- Order("title").
42
- Find(&list).Error
43
- return list, err
44
- }
45
-
46
- func (r *problemSetRepository) Update(ctx context.Context, ps entity.ProblemSet) error {
47
- return r.db.WithContext(ctx).
48
- Model(&entity.ProblemSet{}).
49
- Where("id = ?", ps.Id).
50
- Updates(ps).Error
51
- }
52
-
53
- func (r *problemSetRepository) Delete(ctx context.Context, id uuid.UUID) error {
54
- return r.db.WithContext(ctx).
55
- Where("id = ?", id).
56
- Delete(&entity.ProblemSet{}).Error
57
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package repositories
2
+
3
+ import (
4
+ "context"
5
+ "strings"
6
+
7
+ entity "abdanhafidz.com/go-boilerplate/models/entity"
8
+ "github.com/google/uuid"
9
+ "gorm.io/gorm"
10
+ )
11
+
12
+ type ProblemSetRepository interface {
13
+ Create(ctx context.Context, ps entity.ProblemSet) error
14
+ Get(ctx context.Context, id uuid.UUID) (entity.ProblemSet, error)
15
+ Update(ctx context.Context, ps entity.ProblemSet) error
16
+ Delete(ctx context.Context, id uuid.UUID) error
17
+ List(ctx context.Context) ([]entity.ProblemSet, error)
18
+ ListWithPagination(ctx context.Context, p entity.Pagination) ([]entity.ProblemSet, int64, error)
19
+ }
20
+
21
+ type problemSetRepository struct {
22
+ db *gorm.DB
23
+ }
24
+
25
+ func NewProblemSetRepository(db *gorm.DB) ProblemSetRepository {
26
+ return &problemSetRepository{db: db}
27
+ }
28
+
29
+ func (r *problemSetRepository) Create(ctx context.Context, ps entity.ProblemSet) error {
30
+ return r.db.WithContext(ctx).Create(&ps).Error
31
+ }
32
+
33
+ func (r *problemSetRepository) Get(ctx context.Context, id uuid.UUID) (entity.ProblemSet, error) {
34
+ var ps entity.ProblemSet
35
+ err := r.db.WithContext(ctx).
36
+ First(&ps, "id = ?", id).Error
37
+ return ps, err
38
+ }
39
+
40
+ func (r *problemSetRepository) List(ctx context.Context) ([]entity.ProblemSet, error) {
41
+ var list []entity.ProblemSet
42
+ err := r.db.WithContext(ctx).
43
+ Order("title").
44
+ Find(&list).Error
45
+ return list, err
46
+ }
47
+
48
+ func (r *problemSetRepository) ListWithPagination(ctx context.Context, p entity.Pagination) ([]entity.ProblemSet, int64, error) {
49
+ var list []entity.ProblemSet
50
+
51
+ countQ := r.db.WithContext(ctx).Model(&entity.ProblemSet{})
52
+ if s := strings.TrimSpace(p.Search); s != "" {
53
+ like := "%" + strings.ToLower(s) + "%"
54
+ countQ = countQ.Where("LOWER(title) LIKE ? OR LOWER(description) LIKE ?", like, like)
55
+ }
56
+
57
+ var total int64
58
+ if err := countQ.Count(&total).Error; err != nil {
59
+ return nil, 0, err
60
+ }
61
+
62
+ q := r.db.WithContext(ctx).Model(&entity.ProblemSet{})
63
+ if s := strings.TrimSpace(p.Search); s != "" {
64
+ like := "%" + strings.ToLower(s) + "%"
65
+ q = q.Where("LOWER(title) LIKE ? OR LOWER(description) LIKE ?", like, like)
66
+ }
67
+
68
+ col := strings.ToLower(strings.TrimSpace(p.SortBy))
69
+ ord := strings.ToLower(strings.TrimSpace(p.Order))
70
+ if ord != "desc" {
71
+ ord = "asc"
72
+ }
73
+ switch col {
74
+ case "title", "description", "created_at":
75
+ q = q.Order(col + " " + ord)
76
+ default:
77
+ q = q.Order("title " + ord)
78
+ }
79
+
80
+ if p.Limit > 0 {
81
+ q = q.Limit(p.Limit)
82
+ }
83
+ if p.Offset > 0 {
84
+ q = q.Offset(p.Offset)
85
+ }
86
+
87
+ err := q.Find(&list).Error
88
+ return list, total, err
89
+ }
90
+
91
+ func (r *problemSetRepository) Update(ctx context.Context, ps entity.ProblemSet) error {
92
+ return r.db.WithContext(ctx).
93
+ Model(&entity.ProblemSet{}).
94
+ Where("id = ?", ps.Id).
95
+ Updates(ps).Error
96
+ }
97
+
98
+ func (r *problemSetRepository) Delete(ctx context.Context, id uuid.UUID) error {
99
+ return r.db.WithContext(ctx).
100
+ Where("id = ?", id).
101
+ Delete(&entity.ProblemSet{}).Error
102
+ }
repositories/question_repository.go CHANGED
@@ -2,7 +2,7 @@ package repositories
2
 
3
  import (
4
  "context"
5
-
6
 
7
  entity "abdanhafidz.com/go-boilerplate/models/entity"
8
  "github.com/google/uuid"
@@ -15,6 +15,7 @@ type QuestionsRepository interface {
15
  Update(ctx context.Context, q entity.Questions) error
16
  Delete(ctx context.Context, id uuid.UUID) error
17
  ListByProblemSet(ctx context.Context, problemSetId uuid.UUID) ([]entity.Questions, error)
 
18
  }
19
 
20
  type questionsRepository struct{ db *gorm.DB }
@@ -55,3 +56,51 @@ func (r *questionsRepository) ListByProblemSet(ctx context.Context, problemSetId
55
  return q, err
56
  }
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  import (
4
  "context"
5
+ "strings"
6
 
7
  entity "abdanhafidz.com/go-boilerplate/models/entity"
8
  "github.com/google/uuid"
 
15
  Update(ctx context.Context, q entity.Questions) error
16
  Delete(ctx context.Context, id uuid.UUID) error
17
  ListByProblemSet(ctx context.Context, problemSetId uuid.UUID) ([]entity.Questions, error)
18
+ ListByProblemSetWithPagination(ctx context.Context, problemSetId uuid.UUID, p entity.Pagination) ([]entity.Questions, int64, error)
19
  }
20
 
21
  type questionsRepository struct{ db *gorm.DB }
 
56
  return q, err
57
  }
58
 
59
+ func (r *questionsRepository) ListByProblemSetWithPagination(ctx context.Context, problemSetId uuid.UUID, p entity.Pagination) ([]entity.Questions, int64, error) {
60
+ var list []entity.Questions
61
+
62
+ countQ := r.db.WithContext(ctx).
63
+ Model(&entity.Questions{}).
64
+ Where("problem_set_id = ?", problemSetId)
65
+
66
+ if s := strings.TrimSpace(p.Search); s != "" {
67
+ like := "%" + strings.ToLower(s) + "%"
68
+ countQ = countQ.Where("LOWER(type) LIKE ? OR LOWER(question) LIKE ?", like, like)
69
+ }
70
+
71
+ var total int64
72
+ if err := countQ.Count(&total).Error; err != nil {
73
+ return nil, 0, err
74
+ }
75
+
76
+ q := r.db.WithContext(ctx).
77
+ Model(&entity.Questions{}).
78
+ Where("problem_set_id = ?", problemSetId)
79
+
80
+ if s := strings.TrimSpace(p.Search); s != "" {
81
+ like := "%" + strings.ToLower(s) + "%"
82
+ q = q.Where("LOWER(type) LIKE ? OR LOWER(question) LIKE ?", like, like)
83
+ }
84
+
85
+ col := strings.ToLower(strings.TrimSpace(p.SortBy))
86
+ ord := strings.ToLower(strings.TrimSpace(p.Order))
87
+ if ord != "desc" {
88
+ ord = "asc"
89
+ }
90
+ switch col {
91
+ case "id", "type", "question", "corr_mark", "incorr_mark", "null_mark":
92
+ q = q.Order(col + " " + ord)
93
+ default:
94
+ q = q.Order("id " + ord)
95
+ }
96
+
97
+ if p.Limit > 0 {
98
+ q = q.Limit(p.Limit)
99
+ }
100
+ if p.Offset > 0 {
101
+ q = q.Offset(p.Offset)
102
+ }
103
+
104
+ err := q.Find(&list).Error
105
+ return list, total, err
106
+ }
services/problem_set_service.go CHANGED
@@ -17,6 +17,7 @@ type ProblemSetService interface {
17
  CreateProblemSet(ctx context.Context, ps entity.ProblemSet) error
18
  GetProblemSet(ctx context.Context, id uuid.UUID) (entity.ProblemSet, error)
19
  ListProblemSets(ctx context.Context) ([]entity.ProblemSet, error)
 
20
  UpdateProblemSet(ctx context.Context, ps entity.ProblemSet) error
21
  DeleteProblemSet(ctx context.Context, id uuid.UUID) error
22
 
@@ -24,6 +25,7 @@ type ProblemSetService interface {
24
  UpdateQuestion(ctx context.Context, q entity.Questions) error
25
  DeleteQuestion(ctx context.Context, qID uuid.UUID) error
26
  ListQuestions(ctx context.Context, psID uuid.UUID) ([]entity.Questions, error)
 
27
 
28
  ListProblemSetsByExam(ctx context.Context, examId uuid.UUID, p entity.Pagination) ([]entity.ProblemSetExamAssign, int64, error)
29
  ListCandidateProblemSetsByExam(ctx context.Context, examId uuid.UUID, p entity.Pagination) ([]entity.ProblemSet, int64, error)
@@ -70,6 +72,10 @@ func (s *problemSetService) ListProblemSets(ctx context.Context) ([]entity.Probl
70
  return s.problemSetRepository.List(ctx)
71
  }
72
 
 
 
 
 
73
  func (s *problemSetService) UpdateProblemSet(ctx context.Context, ps entity.ProblemSet) error {
74
  _, err := s.problemSetRepository.Get(ctx, ps.Id)
75
  if err != nil {
@@ -120,6 +126,14 @@ func (s *problemSetService) ListQuestions(ctx context.Context, psID uuid.UUID) (
120
  return s.questionsRepository.ListByProblemSet(ctx, psID)
121
  }
122
 
 
 
 
 
 
 
 
 
123
  // ---------------- Exam ↔ Problem Set (Mapping Table) ----------------
124
 
125
  func (s *problemSetService) AssignProblemSetToExam(ctx context.Context, examId uuid.UUID, problemSetId uuid.UUID) error {
@@ -181,4 +195,3 @@ func (s *problemSetService) ListQuestionsByExam(ctx context.Context, examId uuid
181
  }
182
  return s.questionsRepository.ListByProblemSet(ctx, assign.ProblemSetId)
183
  }
184
-
 
17
  CreateProblemSet(ctx context.Context, ps entity.ProblemSet) error
18
  GetProblemSet(ctx context.Context, id uuid.UUID) (entity.ProblemSet, error)
19
  ListProblemSets(ctx context.Context) ([]entity.ProblemSet, error)
20
+ ListProblemSetsWithPagination(ctx context.Context, p entity.Pagination) ([]entity.ProblemSet, int64, error)
21
  UpdateProblemSet(ctx context.Context, ps entity.ProblemSet) error
22
  DeleteProblemSet(ctx context.Context, id uuid.UUID) error
23
 
 
25
  UpdateQuestion(ctx context.Context, q entity.Questions) error
26
  DeleteQuestion(ctx context.Context, qID uuid.UUID) error
27
  ListQuestions(ctx context.Context, psID uuid.UUID) ([]entity.Questions, error)
28
+ ListQuestionsWithPagination(ctx context.Context, psID uuid.UUID, p entity.Pagination) ([]entity.Questions, int64, error)
29
 
30
  ListProblemSetsByExam(ctx context.Context, examId uuid.UUID, p entity.Pagination) ([]entity.ProblemSetExamAssign, int64, error)
31
  ListCandidateProblemSetsByExam(ctx context.Context, examId uuid.UUID, p entity.Pagination) ([]entity.ProblemSet, int64, error)
 
72
  return s.problemSetRepository.List(ctx)
73
  }
74
 
75
+ func (s *problemSetService) ListProblemSetsWithPagination(ctx context.Context, p entity.Pagination) ([]entity.ProblemSet, int64, error) {
76
+ return s.problemSetRepository.ListWithPagination(ctx, p)
77
+ }
78
+
79
  func (s *problemSetService) UpdateProblemSet(ctx context.Context, ps entity.ProblemSet) error {
80
  _, err := s.problemSetRepository.Get(ctx, ps.Id)
81
  if err != nil {
 
126
  return s.questionsRepository.ListByProblemSet(ctx, psID)
127
  }
128
 
129
+ func (s *problemSetService) ListQuestionsWithPagination(ctx context.Context, psID uuid.UUID, p entity.Pagination) ([]entity.Questions, int64, error) {
130
+ _, err := s.problemSetRepository.Get(ctx, psID)
131
+ if err != nil {
132
+ return nil, 0, http_error.PROBLEM_SET_NOT_FOUND
133
+ }
134
+ return s.questionsRepository.ListByProblemSetWithPagination(ctx, psID, p)
135
+ }
136
+
137
  // ---------------- Exam ↔ Problem Set (Mapping Table) ----------------
138
 
139
  func (s *problemSetService) AssignProblemSetToExam(ctx context.Context, examId uuid.UUID, problemSetId uuid.UUID) error {
 
195
  }
196
  return s.questionsRepository.ListByProblemSet(ctx, assign.ProblemSetId)
197
  }