Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSCEXAM-1212 Modify question search to work using only user parameter #1033

Merged
merged 7 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions app/controllers/CourseController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,11 @@ class CourseController @Inject() (
DB.find(classOf[Course]).where.isNotNull("name").orderBy("code").findList
}.map(_.asScala.toResult(OK))

private def getUserCourses(
user: User,
examIds: Option[List[Long]],
sectionIds: Option[List[Long]],
tagIds: Option[List[Long]]
): Result =
private def getUserCourses(user: User,
examIds: Option[List[Long]],
sectionIds: Option[List[Long]],
tagIds: Option[List[Long]],
ownerIds: Option[List[Long]]): Result =
var query = DB.find(classOf[Course]).where.isNotNull("name")
if !user.hasRole(Role.Name.ADMIN) then
query = query
Expand All @@ -87,6 +86,10 @@ class CourseController @Inject() (
if tagIds.getOrElse(Nil).nonEmpty then
query =
query.in("exams.examSections.sectionQuestions.question.parent.tags.id", tagIds.get.asJava)

if (ownerIds.getOrElse(Nil).nonEmpty)
query =
query.in("exams.examOwners.id", ownerIds.get.asJava)
query.orderBy("name desc").findList.asScala.toResult(OK)

// Actions ->
Expand All @@ -101,12 +104,11 @@ class CourseController @Inject() (
DB.find(classOf[Course], id).toResult(OK)
}

def listUsersCourses(
examIds: Option[List[Long]],
sectionIds: Option[List[Long]],
tagIds: Option[List[Long]]
): Action[AnyContent] =
def listUsersCourses(examIds: Option[List[Long]],
sectionIds: Option[List[Long]],
tagIds: Option[List[Long]],
ownerIds: Option[List[Long]]): Action[AnyContent] =
authenticated.andThen(authorized(Seq(Role.Name.TEACHER, Role.Name.ADMIN))) { request =>
val user = request.attrs(Auth.ATTR_USER)
getUserCourses(user, examIds, sectionIds, tagIds)
getUserCourses(user, examIds, sectionIds, tagIds, ownerIds)
}
5 changes: 5 additions & 0 deletions app/controllers/ExamController.java
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,14 @@ public Result listExams(
Optional<List<Long>> courseIds,
Optional<List<Long>> sectionIds,
Optional<List<Long>> tagIds,
Optional<List<Long>> ownerIds,
Http.Request request
) {
User user = request.attrs().get(Attrs.AUTHENTICATED_USER);
List<Long> courses = courseIds.orElse(Collections.emptyList());
List<Long> sections = sectionIds.orElse(Collections.emptyList());
List<Long> tags = tagIds.orElse(Collections.emptyList());
List<Long> owners = ownerIds.orElse(Collections.emptyList());
PathProperties pp = PathProperties.parse(
"(id, name, examActiveStartDate, examActiveEndDate, course(id, code), examSections(id, name))"
);
Expand All @@ -179,6 +181,9 @@ public Result listExams(
if (!tags.isEmpty()) {
el = el.in("examSections.sectionQuestions.question.parent.tags.id", tags);
}
if (!owners.isEmpty()) {
el = el.in("questionOwners.id", user);
}
return ok(el.findList(), pp);
}

Expand Down
4 changes: 4 additions & 0 deletions app/controllers/ExamSectionController.java
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,7 @@ public Result listSections(
Optional<List<Long>> courseIds,
Optional<List<Long>> examIds,
Optional<List<Long>> tagIds,
Optional<List<Long>> ownerIds,
Http.Request request
) {
User user = request.attrs().get(Attrs.AUTHENTICATED_USER);
Expand All @@ -644,6 +645,9 @@ public Result listSections(
if (tagIds.isPresent() && !tagIds.get().isEmpty()) {
query = query.in("examSectionQuestions.question.tags.id", tagIds.get());
}
if (ownerIds.isPresent() && !ownerIds.get().isEmpty()) {
query = query.in("questionOwners.id", ownerIds.get());
}
Set<ExamSection> sections = query.findSet();
return ok(sections, PathProperties.parse("(*, creator(id))"));
}
Expand Down
31 changes: 17 additions & 14 deletions app/controllers/QuestionController.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,12 @@ public Result getQuestions(
List<Long> courseIds,
List<Long> tagIds,
List<Long> sectionIds,
List<Long> ownerIds,
Http.Request request
) {
User user = request.attrs().get(Attrs.AUTHENTICATED_USER);
if (
user.hasRole(Role.Name.ADMIN) && Stream.of(examIds, courseIds, tagIds, sectionIds).allMatch(List::isEmpty)
user.hasRole(Role.Name.ADMIN) && Stream.of(examIds, courseIds, tagIds, sectionIds, ownerIds).allMatch(List::isEmpty)
) {
return ok(Collections.emptySet());
}
Expand All @@ -104,21 +105,23 @@ public Result getQuestions(
.endJunction()
.ne("state", QuestionState.DELETED.toString());
if (user.hasRole(Role.Name.TEACHER)) {
el = el.disjunction().eq("shared", true).eq("questionOwners", user).endJunction();
}
if (!examIds.isEmpty()) {
el = el.in("examSectionQuestions.examSection.exam.id", examIds);
}
if (!courseIds.isEmpty()) {
el = el.in("examSectionQuestions.examSection.exam.course.id", courseIds);
}
if (!tagIds.isEmpty()) {
el = el.in("tags.id", tagIds);
}
if (!sectionIds.isEmpty()) {
el = el.in("examSectionQuestions.examSection.id", sectionIds);
if (ownerIds.isEmpty()) {
el = el.eq("questionOwners", user);
} else {
el = el.in("questionOwners.id", ownerIds);
}
} else {
el = el.inOrEmpty("questionOwners.id", ownerIds);
}
el = el.inOrEmpty("examSectionQuestions.examSection.exam.id", examIds);
el = el.inOrEmpty("examSectionQuestions.examSection.exam.course.id", courseIds);
el = el.inOrEmpty("tags.id", tagIds);
el = el.inOrEmpty("examSectionQuestions.examSection.id", sectionIds);

Set<Question> questions = el.orderBy("created desc").findSet();
if (user.hasRole(Role.Name.TEACHER) && !ownerIds.isEmpty()) {
questions = questions.stream().filter(question -> question.getQuestionOwners().contains(user)).collect(Collectors.toSet());
}
return ok(questions, pp);
}

Expand Down
4 changes: 4 additions & 0 deletions app/controllers/TagController.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public Result listTags(
Optional<List<Long>> courseIds,
Optional<List<Long>> examIds,
Optional<List<Long>> sectionIds,
Optional<List<Long>> ownerIds,
Http.Request request
) {
User user = request.attrs().get(Attrs.AUTHENTICATED_USER);
Expand All @@ -64,6 +65,9 @@ public Result listTags(
if (sectionIds.isPresent() && !sectionIds.get().isEmpty()) {
query = query.in("questions.examSectionQuestions.examSection.id", sectionIds.get());
}
if (ownerIds.isPresent() && !ownerIds.get().isEmpty()) {
query = query.in("questions.questionOwners.id", ownerIds.get());
}
Set<Tag> tags = query.findSet();
return ok(tags, PathProperties.parse("(*, creator(id), questions(id))"));
}
Expand Down
10 changes: 5 additions & 5 deletions conf/routes
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ PUT /app/exams/:eid/course/:cid controlle
GET /app/exams/:id/preview controllers.ExamController.getExamPreview(id: Long, request: Request)
GET /app/reviewerexams controllers.ExamController.getTeachersExams(request: Request)

GET /app/examsearch controllers.ExamController.listExams(courseIds: java.util.Optional[LongList], sectionIds: java.util.Optional[LongList], tagIds: java.util.Optional[LongList], request: Request)
GET /app/examsearch controllers.ExamController.listExams(courseIds: java.util.Optional[LongList], sectionIds: java.util.Optional[LongList], tagIds: java.util.Optional[LongList], owner: java.util.Optional[LongList], request: Request)

PUT /app/exam/:eid/software/:sid controllers.ExamController.updateExamSoftware(eid: Long, sid: Long, request: Request)

Expand Down Expand Up @@ -122,7 +122,7 @@ DELETE /app/exams/:eid/sections/:sid/questions/:qid controlle
PUT /app/exams/:eid/sections/:sid/reorder controllers.ExamSectionController.reorderSectionQuestions(eid: Long, sid: Long, request: Request)
PUT /app/exams/:eid/reorder controllers.ExamSectionController.reorderSections(eid: Long, request: Request)
GET /app/exams/question/:id/distribution controllers.ExamSectionController.getQuestionDistribution(id: Long)
GET /app/sections controllers.ExamSectionController.listSections(filter: java.util.Optional[String], courseIds: java.util.Optional[LongList], examIds: java.util.Optional[LongList], tagIds: java.util.Optional[LongList], request: Request)
GET /app/sections controllers.ExamSectionController.listSections(filter: java.util.Optional[String], courseIds: java.util.Optional[LongList], examIds: java.util.Optional[LongList], tagIds: java.util.Optional[LongList], ownerIds: java.util.Optional[LongList], request: Request)

############### Section material interface ###############
GET /app/materials controllers.ExamMaterialController.listMaterials(request: Request)
Expand Down Expand Up @@ -182,12 +182,12 @@ DELETE /app/enrolments/configs/:eecid controlle

############### Courses interface ###############
GET /app/courses controllers.CourseController.getCourses(filter: Option[String], q: Option[String])
GET /app/courses/user controllers.CourseController.listUsersCourses(examIds: Option[List[Long]], sectionIds: Option[List[Long]], tagIds: Option[List[Long]])
GET /app/courses/user controllers.CourseController.listUsersCourses(examIds: Option[List[Long]], sectionIds: Option[List[Long]], tagIds: Option[List[Long]], ownerIds: Option[List[Long]])
GET /app/courses/:id controllers.CourseController.getCourse(id: Long)

############### Question interface ###############

GET /app/questions controllers.QuestionController.getQuestions(exam: java.util.List[java.lang.Long], course: java.util.List[java.lang.Long], tag: java.util.List[java.lang.Long], section: java.util.List[java.lang.Long], request: Request)
GET /app/questions controllers.QuestionController.getQuestions(exam: java.util.List[java.lang.Long], course: java.util.List[java.lang.Long], tag: java.util.List[java.lang.Long], section: java.util.List[java.lang.Long], owner: java.util.List[java.lang.Long], request: Request)
GET /app/questions/:id controllers.QuestionController.getQuestion(id: Long, request: Request)
PUT /app/questions/:id controllers.QuestionController.updateQuestion(id: Long, request: Request)

Expand Down Expand Up @@ -410,7 +410,7 @@ GET /app/availability/:roomId/:date controlle
GET /app/languages controllers.LanguageController.getSupportedLanguages

################# Tag interface ##################
GET /app/tags controllers.TagController.listTags(filter: java.util.Optional[String], courseIds: java.util.Optional[LongList], examIds: java.util.Optional[LongList], sectionIds: java.util.Optional[LongList], request: Request)
GET /app/tags controllers.TagController.listTags(filter: java.util.Optional[String], courseIds: java.util.Optional[LongList], examIds: java.util.Optional[LongList], sectionIds: java.util.Optional[LongList], owner: java.util.Optional[LongList], request: Request)
POST /app/tags/questions controllers.TagController.addTagToQuestions(request: Request)

################# General Settings interface ##################
Expand Down
30 changes: 19 additions & 11 deletions ui/src/app/question/library/library.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import type { Course, Exam, ExamSection, ReverseQuestion, Tag } from '../../exam/exam.model';
import { QuestionService } from '../question.service';
import {User} from "../../session/session.service";

export interface LibraryQuestion extends ReverseQuestion {
icon: string;
Expand All @@ -36,20 +37,23 @@ export class LibraryService {
private Question: QuestionService,
) {}

listExams$ = (courseIds: number[], sectionIds: number[], tagIds: number[]): Observable<Exam[]> =>
this.http.get<Exam[]>('/app/examsearch', { params: this.getQueryParams(courseIds, [], sectionIds, tagIds) });
listExams$ = (courseIds: number[], sectionIds: number[], tagIds: number[], ownerIds: number[]): Observable<Exam[]> =>
this.http.get<Exam[]>('/app/examsearch', { params: this.getQueryParams(courseIds, [], sectionIds, tagIds, ownerIds) });

listCourses$ = (examIds: number[], sectionIds: number[], tagIds: number[]): Observable<Course[]> =>
this.http.get<Course[]>('/app/courses/user', { params: this.getQueryParams([], examIds, sectionIds, tagIds) });
listCourses$ = (examIds: number[], sectionIds: number[], tagIds: number[], ownerIds: number[]): Observable<Course[]> =>
this.http.get<Course[]>('/app/courses/user', { params: this.getQueryParams([], examIds, sectionIds, tagIds, ownerIds) });

listSections$ = (courseIds: number[], examIds: number[], tagIds: number[]): Observable<ExamSection[]> =>
this.http.get<ExamSection[]>('/app/sections', { params: this.getQueryParams(courseIds, examIds, [], tagIds) });
listSections$ = (courseIds: number[], examIds: number[], tagIds: number[], ownerIds: number[]): Observable<ExamSection[]> =>
this.http.get<ExamSection[]>('/app/sections', { params: this.getQueryParams(courseIds, examIds, [], tagIds, ownerIds) });

listTags$ = (courseIds: number[], examIds: number[], sectionIds: number[]): Observable<Tag[]> =>
this.http.get<Tag[]>('/app/tags', { params: this.getQueryParams(courseIds, sectionIds, examIds, []) });
listTags$ = (courseIds: number[], examIds: number[], sectionIds: number[], ownerIds: number[]): Observable<Tag[]> =>
this.http.get<Tag[]>('/app/tags', { params: this.getQueryParams(courseIds, sectionIds, examIds, [], ownerIds) });

listAllTags$ = (): Observable<Tag[]> => this.http.get<Tag[]>('/app/tags');

listAllOwners$ = (): Observable<User[]> =>
this.http.get<User[]>('/app/users');

addTagForQuestions$ = (tagId: number, questionIds: number[]) =>
this.http.post<void>('/app/tags/questions', { questionIds: questionIds, tagId: tagId });

Expand Down Expand Up @@ -93,7 +97,7 @@ export class LibraryService {
if (text) {
return questions.filter((question) => {
const re = new RegExp(text, 'i');
const owner = question.creator ? question.creator.firstName + ' ' + question.creator.lastName : '';
const owner = question.questionOwners.map((o)=> o.firstName + ' ' + o.lastName).toString();
return owner.match(re);
});
} else {
Expand All @@ -106,10 +110,11 @@ export class LibraryService {
examIds: number[],
sectionIds: number[],
tagIds: number[],
ownerIds: number[]
): Observable<LibraryQuestion[]> =>
this.http
.get<LibraryQuestion[]>('/app/questions', {
params: this.getQueryParams(courseIds, examIds, sectionIds, tagIds),
params: this.getQueryParams(courseIds, examIds, sectionIds, tagIds, ownerIds),
})
.pipe(
map((questions) => {
Expand Down Expand Up @@ -137,7 +142,7 @@ export class LibraryService {
}),
);

private getQueryParams = (courseIds: number[], examIds: number[], sectionIds: number[], tagIds: number[]) => {
private getQueryParams = (courseIds: number[], examIds: number[], sectionIds: number[], tagIds: number[], ownerIds: number[]) => {
let params = new HttpParams();

const append = (key: string, idArray: number[], paramsObj: HttpParams) => {
Expand All @@ -156,6 +161,9 @@ export class LibraryService {
if (examIds.length > 0) {
params = append('exam', examIds, params);
}
if (ownerIds.length > 0) {
params = append('owner', ownerIds, params);
}

return params;
};
Expand Down
15 changes: 15 additions & 0 deletions ui/src/app/question/library/results/library-results.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@
>
</xm-table-sort>
</th>
<th>
<xm-table-sort
by="creator"
text="i18n_creator"
[predicate]="questionsPredicate"
[reverse]="reverse"
(click)="setPredicate('creator')"
>
</xm-table-sort>
</th>
<th>
<xm-table-sort
by="modified"
Expand Down Expand Up @@ -135,6 +145,11 @@
</div>
</td>
<td>
<span class="">
<small>{{ question.creator?.firstName }} {{ question.creator?.lastName }}</small>
</span>
</td>
<td>
@if (question.modified) {
<span class="timestamp">
{{ question.modified | date: 'dd.MM.yyyy HH:mm' }}
Expand Down
Loading
Loading