-
Notifications
You must be signed in to change notification settings - Fork 349
Development: Attendance checker modern iPad endpoint
#11419
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
base: develop
Are you sure you want to change the base?
Changes from 216 commits
eb54ed9
e3437d6
d60eb17
da3b4c8
6732bf0
0bfaf7e
8f1a3a6
f2efca0
f021a56
5a1cafb
dca8ba1
a86f864
d8a6a90
fa8356d
d6a952c
7076ed7
2ec994b
da24105
b28dc45
cda2bb1
fdf6dc4
f4988ca
b64f67b
5ef3e88
7ec543c
d067a1d
efcecd3
7491679
a05b723
915ae79
124a2ab
76c20f2
3edb5bc
720441f
03de792
0d7eda1
44f0e2e
2c8a0a3
6ec0c1f
656116b
dc00bd4
a363397
889a328
b6c9ab7
8ed8f44
7003ad3
e2f3476
3736c70
cf22303
85e2ed2
44f715e
9fa408b
9392878
a9c207a
16c4886
28cb949
466afc6
a27a5e2
4cb0821
1d80bfc
cab4585
8b8bf1b
25c587e
7b2e245
5a40386
30ac592
ff90af4
c5cd592
d510ccc
0fd9652
9731204
a229956
0ffdb2a
786a311
b28ecd0
1f5299d
5c54d33
b4327fc
5a1e408
70969d1
32ffad1
bb5f9d3
e2cfd5d
54653f8
135c3f8
f20e8e3
d6a6e15
2d30981
08f459b
709b274
11e289a
121fbee
436474c
a819759
3233321
b821ba6
f8c24a9
f790aae
3b9d5fd
ae67da4
3cf548f
ff44bd5
1fb6e40
6a402ca
30d24c2
6355bac
49dbbf1
efed2bc
de448f4
681e033
b9c7729
8fe63e7
c77c5b7
065f8ed
1b3e19f
d09220b
6f98903
85a3465
172d3d3
9c3a39c
b5caf15
142cf31
4cc239a
8d004b1
b0d15d0
571cd49
1ac17d7
67a160a
327a7a3
87aa963
4baac6e
052b774
32c2ef3
771647f
0fa2580
28b0627
fd6b28a
24ee19f
2e0aac8
1459597
2c51429
404b792
aa0f040
be6f573
6d2dbd9
9de7f05
ff5c4b9
b84ba36
8d9ac66
76eb651
8c10817
916933c
0bc9f62
180a86b
3fb7858
6175031
8afac7d
362b56e
11236a7
0e2021d
d884e13
484ad2c
69bf6f2
342b6a0
edabd3e
2dee634
5efcd60
c7ff242
9e36dfc
4704f32
c01bc32
c593a6b
3ce8a0c
e2067cc
c8a4988
250e897
79e252b
203fa00
e5b93bc
8d68124
c481e49
fb451cc
2d7d687
5cf68f2
0a6feb0
271d242
75da874
356d3ba
c0449af
c26df83
0ef7f2c
2c01f5e
1e92dd5
9178a8d
2acbf10
68ab794
733bcfc
c551482
49436e4
2e014b0
8b48f21
14378dc
92a3042
ab7534c
685ad29
5cd45b6
961b064
7d3bd91
125d756
ccd0311
c87f04c
065770c
4c38903
25264fa
4ceb407
926855f
a334adf
665a35b
30d17fb
4b45e49
74b871d
c0d9c53
1cf5750
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,12 +5,16 @@ | |
| import jakarta.persistence.JoinColumn; | ||
| import jakarta.persistence.ManyToOne; | ||
| import jakarta.persistence.Table; | ||
| import jakarta.persistence.Transient; | ||
| import jakarta.validation.constraints.Size; | ||
|
|
||
| import com.fasterxml.jackson.annotation.JsonIgnore; | ||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||
|
|
||
| import de.tum.cit.aet.artemis.core.domain.AbstractAuditingEntity; | ||
| import de.tum.cit.aet.artemis.core.domain.User; | ||
| import de.tum.cit.aet.artemis.exam.domain.room.ExamRoom; | ||
| import de.tum.cit.aet.artemis.exam.dto.room.ExamSeatDTO; | ||
|
|
||
| @Entity | ||
| @Table(name = "exam_user") | ||
|
|
@@ -29,6 +33,22 @@ public class ExamUser extends AbstractAuditingEntity { | |
| @Column(name = "planned_seat") | ||
| private String plannedSeat; | ||
|
|
||
| @JsonIgnore | ||
| @Transient | ||
| private ExamRoom plannedRoomTransient; | ||
|
|
||
| @JsonIgnore | ||
| @Transient | ||
| private ExamSeatDTO plannedSeatTransient; | ||
|
|
||
| @JsonIgnore | ||
| @Transient | ||
| private ExamRoom actualRoomTransient; | ||
|
|
||
| @JsonIgnore | ||
| @Transient | ||
| private ExamSeatDTO actualSeatTransient; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if i'm a fan of mixing DTOs and domain objects
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I already do it in my and since in this case the fields are I did it like this because it (greatly) simplifies the rest of the logic and improves readability. If you insist on it being bad then I can change it, but please do share your opinion considering these things. |
||
|
|
||
| @Column(name = "did_check_image") | ||
| private boolean didCheckImage = false; | ||
|
|
||
|
|
@@ -89,6 +109,32 @@ public void setPlannedSeat(String plannedSeat) { | |
| this.plannedSeat = plannedSeat; | ||
| } | ||
|
|
||
| public ExamRoom getPlannedRoomTransient() { | ||
| return plannedRoomTransient; | ||
| } | ||
|
|
||
| public ExamSeatDTO getPlannedSeatTransient() { | ||
| return plannedSeatTransient; | ||
| } | ||
|
|
||
| public void setTransientPlannedRoomAndSeat(ExamRoom plannedRoom, ExamSeatDTO plannedSeat) { | ||
| this.plannedRoomTransient = plannedRoom; | ||
| this.plannedSeatTransient = plannedSeat; | ||
| } | ||
|
|
||
| public ExamRoom getActualRoomTransient() { | ||
| return actualRoomTransient; | ||
| } | ||
|
|
||
| public ExamSeatDTO getActualSeatTransient() { | ||
| return actualSeatTransient; | ||
| } | ||
|
|
||
| public void setTransientActualRoomAndSeat(ExamRoom actualRoom, ExamSeatDTO actualSeat) { | ||
| this.actualRoomTransient = actualRoom; | ||
| this.actualSeatTransient = actualSeat; | ||
| } | ||
|
|
||
| public boolean getDidCheckRegistrationNumber() { | ||
| return didCheckRegistrationNumber; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| package de.tum.cit.aet.artemis.exam.dto.room; | ||
|
|
||
| import java.time.ZonedDateTime; | ||
| import java.util.List; | ||
| import java.util.Set; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| import jakarta.annotation.Nullable; | ||
| import jakarta.validation.constraints.Email; | ||
| import jakarta.validation.constraints.NotBlank; | ||
| import jakarta.validation.constraints.NotEmpty; | ||
| import jakarta.validation.constraints.NotNull; | ||
| import jakarta.validation.constraints.Size; | ||
|
|
||
| import org.springframework.util.StringUtils; | ||
|
|
||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||
|
|
||
| import de.tum.cit.aet.artemis.exam.domain.Exam; | ||
| import de.tum.cit.aet.artemis.exam.domain.ExamUser; | ||
| import de.tum.cit.aet.artemis.exam.domain.room.ExamRoom; | ||
|
|
||
| // @formatter:off | ||
| @JsonInclude(JsonInclude.Include.NON_EMPTY) | ||
| record ExamRoomForAttendanceCheckerDTO( | ||
| @NotNull long id, | ||
SamuelRoettgermann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| @NotBlank String roomNumber, | ||
| @Nullable String alternativeRoomNumber, | ||
| @NotBlank String name, | ||
| @Nullable String alternativeName, | ||
| @NotBlank String building, | ||
| @NotNull List<ExamSeatDTO> seats | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As of right now it's TECHNICALLY still possible for a room to have zero seats, so I wouldn't want to exchange that just yet, but it's definitely something that can be adjusted in the future when the room parser rejects rooms without seats. |
||
| ) { | ||
| static ExamRoomForAttendanceCheckerDTO from(ExamRoom examRoom) { | ||
| return new ExamRoomForAttendanceCheckerDTO( | ||
| examRoom.getId(), | ||
| examRoom.getRoomNumber(), | ||
| examRoom.getAlternativeRoomNumber(), | ||
| examRoom.getName(), | ||
| examRoom.getAlternativeName(), | ||
| examRoom.getBuilding(), | ||
| examRoom.getSeats() | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| @JsonInclude(JsonInclude.Include.NON_EMPTY) | ||
| record ExamUserLocationDTO( | ||
| @Nullable Long roomId, // null if legacy version | ||
| @NotBlank String roomNumber, // examUser.plannedRoom if legacy version | ||
| @NotBlank String seatName // examUser.plannedSeat if legacy version | ||
| ) { | ||
| static ExamUserLocationDTO plannedFrom(ExamUser examUser) { | ||
| final boolean isLegacy = examUser.getPlannedRoomTransient() == null || examUser.getPlannedSeatTransient() == null; | ||
|
|
||
| return new ExamUserLocationDTO( | ||
| isLegacy ? null : examUser.getPlannedRoomTransient().getId(), | ||
| isLegacy ? examUser.getPlannedRoom() : examUser.getPlannedRoomTransient().getRoomNumber(), | ||
| isLegacy ? examUser.getPlannedSeat() : examUser.getPlannedSeatTransient().name() | ||
| ); | ||
| } | ||
SamuelRoettgermann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| static ExamUserLocationDTO actualFrom(ExamUser examUser) { | ||
| if (examUser.getActualRoom() == null || examUser.getActualSeat() == null) { | ||
| // examUser has not been moved | ||
| return null; | ||
| } | ||
|
|
||
| final boolean useLegacyFields = examUser.getActualRoomTransient() == null || examUser.getActualSeatTransient() == null; | ||
|
|
||
| return new ExamUserLocationDTO( | ||
| useLegacyFields ? null : examUser.getActualRoomTransient().getId(), | ||
| useLegacyFields ? examUser.getActualRoom() : examUser.getActualRoomTransient().getRoomNumber(), | ||
| useLegacyFields ? examUser.getActualSeat() : examUser.getActualSeatTransient().name() | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| @JsonInclude(JsonInclude.Include.NON_EMPTY) | ||
| record ExamUserWithExamRoomAndSeatDTO ( | ||
| @NotBlank @Size(max = 50) String login, | ||
| // Names are nullable because not everyone has a first and/or last name | ||
| @Nullable @Size(max = 50) String firstName, | ||
| @Nullable @Size(max = 50) String lastName, | ||
| @NotBlank @Size(max = 10) String registrationNumber, | ||
| @Nullable @Email @Size(max = 100) String email, | ||
| @Nullable String imageUrl, | ||
| @NotNull boolean didCheckImage, | ||
| @NotNull boolean didCheckName, | ||
| @NotNull boolean didCheckRegistrationNumber, | ||
| @NotNull boolean didCheckLogin, | ||
SamuelRoettgermann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| @Nullable String signingImagePath, | ||
| @NotNull ExamUserLocationDTO plannedLocation, | ||
| @Nullable ExamUserLocationDTO actualLocation | ||
| ) { | ||
| static ExamUserWithExamRoomAndSeatDTO from(ExamUser examUser) { | ||
| return new ExamUserWithExamRoomAndSeatDTO( | ||
| examUser.getUser().getLogin(), | ||
| examUser.getUser().getFirstName(), | ||
| examUser.getUser().getLastName(), | ||
| examUser.getUser().getRegistrationNumber(), | ||
| examUser.getUser().getEmail(), | ||
| examUser.getUser().getImageUrl(), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just as a note so we don't forget, we will need to add the actual exam user image here as this would show the profile picture the user has set
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. very simple fix, just exchanged the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding it as a TODO here, so we don't forget: When we have tested everything and it all works, we need to replace this with the image from TUMonline instead of the profile picture, otherwise students can "fake" their image |
||
| examUser.getDidCheckImage(), | ||
| examUser.getDidCheckName(), | ||
| examUser.getDidCheckRegistrationNumber(), | ||
| examUser.getDidCheckLogin(), | ||
| examUser.getSigningImagePath(), | ||
| ExamUserLocationDTO.plannedFrom(examUser), | ||
| ExamUserLocationDTO.actualFrom(examUser) | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * DTO containing all relevant information for the attendance checker app | ||
| */ | ||
| @JsonInclude(JsonInclude.Include.NON_EMPTY) | ||
| public record AttendanceCheckerAppExamInformationDTO( | ||
| @NotNull long examId, | ||
SamuelRoettgermann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| @NotBlank String examTitle, | ||
| @NotNull ZonedDateTime startDate, | ||
| @NotNull ZonedDateTime endDate, | ||
| @NotNull boolean isTestExam, | ||
| @NotNull long courseId, | ||
SamuelRoettgermann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| @NotBlank String courseTitle, | ||
| @NotNull Set<ExamRoomForAttendanceCheckerDTO> examRoomsUsedInExam, // empty if legacy version | ||
| @NotEmpty Set<ExamUserWithExamRoomAndSeatDTO> examUsersWithExamRoomAndSeat | ||
| ) { | ||
| /** | ||
| * Create an AttendanceCheckerAppExamInformationDTO from the given exam and its rooms | ||
| * | ||
| * @param exam the exam | ||
| * @param examRooms the rooms used in the exam | ||
| * @return information for the attendance checker app | ||
| */ | ||
| public static AttendanceCheckerAppExamInformationDTO from(Exam exam, Set<ExamRoom> examRooms) { | ||
| Set<ExamUser> examUsersWhoHaveBeenDistributed = exam.getExamUsers().stream() | ||
| .filter(examUser -> StringUtils.hasText(examUser.getPlannedRoom()) && StringUtils.hasText(examUser.getPlannedSeat())) | ||
| .collect(Collectors.toSet()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can just use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if you're using a different Java than me, but that should not be possible: https://bugs.openjdk.org/browse/JDK-8306888 |
||
|
|
||
| return new AttendanceCheckerAppExamInformationDTO( | ||
| exam.getId(), | ||
| exam.getTitle(), | ||
| exam.getStartDate(), | ||
| exam.getEndDate(), | ||
| exam.isTestExam(), | ||
| exam.getCourse().getId(), | ||
| exam.getCourse().getTitle(), | ||
| examRooms.stream().map(ExamRoomForAttendanceCheckerDTO::from).collect(Collectors.toSet()), | ||
| examUsersWhoHaveBeenDistributed.stream().map(ExamUserWithExamRoomAndSeatDTO::from).collect(Collectors.toSet()) | ||
|
Comment on lines
+151
to
+152
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here we can also just use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same answer as above: https://bugs.openjdk.org/browse/JDK-8306888 |
||
| ); | ||
| } | ||
| } | ||
| // @formatter:on | ||
Uh oh!
There was an error while loading. Please reload this page.