Skip to content

Commit

Permalink
Merge branch 'dev' into CSCEXAM-224
Browse files Browse the repository at this point in the history
  • Loading branch information
lupari committed Jan 20, 2025
2 parents 6986b2d + 6159be2 commit 810c1a8
Show file tree
Hide file tree
Showing 37 changed files with 266 additions and 220 deletions.
4 changes: 3 additions & 1 deletion app/controllers/user/SessionController.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ public class SessionController extends BaseController {
"x-exam-wrong-room",
"wrongRoomData",
"x-exam-wrong-agent-config",
"wrongAgent"
"wrongAgent",
"x-exam-aquarium-login",
"aquariumLogin"
);

@Inject
Expand Down
5 changes: 5 additions & 0 deletions app/repository/EnrolmentRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,11 @@ private void handleUpcomingEnrolment(
if (
enrolment.getExam() != null && enrolment.getExam().getImplementation() == Exam.Implementation.AQUARIUM
) {
// If user is on a correct aquarium machine then always set a header
headers.put(
"x-exam-aquarium-login",
String.format("%s:::%d", getExamHash(enrolment), enrolment.getId())
);
// Aquarium exam, don't set headers unless it starts in 5 minutes
DateTime threshold = DateTime.now().plusMinutes(5);
DateTime start = dateTimeHandler.normalize(
Expand Down
33 changes: 11 additions & 22 deletions app/system/SystemFilter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,48 +12,43 @@ import play.api.mvc.{Filter, RequestHeader, Result}
import javax.inject.Inject
import scala.concurrent.{ExecutionContext, Future}

object ResultImplicits {
implicit class EnhancedResult(result: Result) {
def discardingHeaders(headers: String*): Result =
result.copy(header = result.header.copy(headers = result.header.headers -- headers))
}
}

class SystemFilter @Inject() (implicit val mat: Materializer, ec: ExecutionContext) extends Filter {
class SystemFilter @Inject() (implicit val mat: Materializer, ec: ExecutionContext) extends Filter:

val Headers: Seq[(String, String)] = Seq(
("x-exam-start-exam", "ongoingExamHash"),
("x-exam-upcoming-exam", "upcomingExamHash"),
("x-exam-wrong-machine", "wrongMachineData"),
("x-exam-unknown-machine", "unknownMachineData"),
("x-exam-wrong-room", "wrongRoomData"),
("x-exam-wrong-agent-config", "wrongAgent")
("x-exam-wrong-agent-config", "wrongAgent"),
("x-exam-aquarium-login", "aquariumLogin")
)

import ResultImplicits._
extension (result: Result)
private def discardingHeaders(headers: String*): Result =
result.copy(header = result.header.copy(headers = result.header.headers -- headers))

private def processResult(src: Result)(implicit request: RequestHeader): Result = {
val session = src.session match {
private def processResult(src: Result)(implicit request: RequestHeader): Result =
val session = src.session match
case s if s.isEmpty =>
request.session match {
case rs if rs.isEmpty => None
case rs => Some(rs)
}
case s => Some(s)
}
val result = src.withHeaders(
("Cache-Control", "no-cache;no-store"),
("Pragma", "no-cache"),
("Expires", "0")
)
session match {
session match
case None => result.withNewSession
case Some(s) =>
val (remaining, discarded) = Headers.partition(h => s.get(h._2).isDefined)
val response = result
.withHeaders(remaining.map(h => (h._1, s.get(h._2).get))*)
.discardingHeaders(discarded.map(_._1)*)
request.path match {
request.path match
case path if path == "/app/session" && request.method == "GET" =>
s.get("upcomingExamHash") match {
case Some(_) => // Don't let session expire when awaiting exam to start
Expand All @@ -63,17 +58,11 @@ class SystemFilter @Inject() (implicit val mat: Materializer, ec: ExecutionConte
case path if path.contains("logout") => response.withSession(s)
case _ =>
response.withSession(s + ("since" -> ISODateTimeFormat.dateTime.print(DateTime.now)))
}
}
}

override def apply(next: RequestHeader => Future[Result])(rh: RequestHeader): Future[Result] =
rh.path match {
rh.path match
case "/app/logout" => next.apply(rh)
// Disable caching for index page so that CSRF cookie can be injected without worries
case p if p.startsWith("/app") | p.startsWith("/integration") =>
next.apply(rh).map(processResult(_)(rh))
case _ => next.apply(rh).map(_.withHeaders(("Cache-Control", "no-cache")))
}

}
25 changes: 17 additions & 8 deletions scripts/internal/remove_overlapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,34 @@
# SPDX-License-Identifier: EUPL-1.2

from datetime import datetime
from psycopg2 import connect
from psycopg import connect # pip install "psycopg[binary]"

conn_string = "host='localhost' dbname='exam' user='exam' password='exam'"
conn = connect(conn_string)
cursor = conn.cursor()

def select(unique=False):

def select():
names = [desc[0] for desc in cursor.description]
result = map(dict, [[(names[i], row[i]) for (i, name) in enumerate(names)] for row in cursor])
return list(result)[0] if unique else list(result)
return list(result)


def delete(ids):
ids = ','.join([str(id) for id in ids])
cursor.execute('UPDATE exam_enrolment SET reservation_id = NULL where reservation_id IN (%s)' % ids)
cursor.execute('UPDATE exam_participation SET reservation_id = NULL where reservation_id IN (%s)' % ids)
cursor.execute('DELETE FROM reservation WHERE id IN (%s)' % ids)


def reservations():
cursor.execute("""
SELECT r.*, ep.id AS ep_id FROM reservation AS r
LEFT JOIN exam_participation AS ep ON ep.reservation_id = r.id WHERE r.machine_id IS NOT NULL
""")
return select()


def find_overlapping(rs):
overlapping_pairs = []
rs.sort(key=lambda x: (x["machine_id"], x["start_at"]))
Expand All @@ -43,13 +47,16 @@ def find_overlapping(rs):
overlapping_pairs.append((res1, res2))
return overlapping_pairs


def main():
print("Following overlapping reservation pairs found:")
rs, removed, unresolved = reservations(), set(), set()
print("Following overlapping reservation pairs found:")
for r1, r2 in find_overlapping(rs):
print("r1_id: {id1} r2_id:{id2} r1_machine_id: {mid1} r2_machine_id: {mid2} r1_period: {s1} - {e1} r2_period {s2} - {e2}".format(
id1=r1['id'], id2=r2['id'], mid1=r1['machine_id'], mid2=r2['machine_id'], s1=r1['start_at'], e1=r1['end_at'], s2=r2['start_at'], e2=r2['end_at']
))
print(
"r1_id: {id1} r2_id:{id2} r1_machine_id: {mid1} r2_machine_id: {mid2} r1_period: {s1} - {e1} r2_period {s2} - {e2}".format(
id1=r1['id'], id2=r2['id'], mid1=r1['machine_id'], mid2=r2['machine_id'], s1=r1['start_at'],
e1=r1['end_at'], s2=r2['start_at'], e2=r2['end_at']
))
if r1['ep_id'] and not r2['ep_id']:
print('Reservation #%s will be removed' % r2['id'])
removed.add(r2['id'])
Expand All @@ -61,7 +68,8 @@ def main():
print('Reservation #%s will be removed' % r2['id'])
removed.update([r1['id'], r2['id']])
else:
print('Cannot automatically remove reservations #%s and #%s' % (r1['id'], r2['id']))
print('Will not automatically remove reservations #%s and #%s as both are linked to some participation'
% (r1['id'], r2['id']))
unresolved.update([r1['id'], r2['id']])
print('Conclusion')
print('The following reservations will be removed: \n%s' % removed)
Expand All @@ -70,5 +78,6 @@ def main():
delete(removed)
conn.commit()


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// SPDX-License-Identifier: EUPL-1.2

import { Component } from '@angular/core';
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core';
import { format } from 'date-fns';
import { DatePickerComponent } from 'src/app/shared/date/date-picker.component';
Expand Down Expand Up @@ -39,7 +38,7 @@ import { FileService } from 'src/app/shared/file/file.service';
`,
selector: 'xm-answers-report',
standalone: true,
imports: [DatePickerComponent, NgbPopover, TranslateModule],
imports: [DatePickerComponent, TranslateModule],
})
export class AnswersReportComponent {
startDate: Date | null = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// SPDX-License-Identifier: EUPL-1.2

import { Component, Input } from '@angular/core';
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { ToastrService } from 'ngx-toastr';
import { FileService } from 'src/app/shared/file/file.service';
Expand Down Expand Up @@ -36,7 +35,7 @@ import { Option } from 'src/app/shared/select/select.model';
`,
selector: 'xm-enrolments-report',
standalone: true,
imports: [DropdownSelectComponent, NgbPopover, TranslateModule],
imports: [DropdownSelectComponent, TranslateModule],
})
export class EnrolmentsReportComponent {
@Input() examNames: Option<string, number>[] = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// SPDX-License-Identifier: EUPL-1.2

import { Component, Input } from '@angular/core';
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { ToastrService } from 'ngx-toastr';
import { FileService } from 'src/app/shared/file/file.service';
Expand Down Expand Up @@ -39,7 +38,7 @@ import { Option } from 'src/app/shared/select/select.model';
`,
selector: 'xm-exams-report',
standalone: true,
imports: [DropdownSelectComponent, NgbPopover, TranslateModule],
imports: [DropdownSelectComponent, TranslateModule],
})
export class ExamsReportComponent {
@Input() examNames: Option<string, number>[] = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// SPDX-License-Identifier: EUPL-1.2

import { Component } from '@angular/core';
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core';
import { DatePickerComponent } from 'src/app/shared/date/date-picker.component';
import { FileService } from 'src/app/shared/file/file.service';
Expand Down Expand Up @@ -38,7 +37,7 @@ import { FileService } from 'src/app/shared/file/file.service';
`,
selector: 'xm-records-report',
standalone: true,
imports: [DatePickerComponent, NgbPopover, TranslateModule],
imports: [DatePickerComponent, TranslateModule],
})
export class RecordsReportComponent {
startDate: Date | null = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// SPDX-License-Identifier: EUPL-1.2

import { Component } from '@angular/core';
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core';
import { format } from 'date-fns';
import { DatePickerComponent } from 'src/app/shared/date/date-picker.component';
Expand Down Expand Up @@ -38,7 +37,7 @@ import { FileService } from 'src/app/shared/file/file.service';
`,
selector: 'xm-reviews-report',
standalone: true,
imports: [DatePickerComponent, NgbPopover, TranslateModule],
imports: [DatePickerComponent, TranslateModule],
})
export class ReviewsReportComponent {
startDate: Date | null = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// SPDX-License-Identifier: EUPL-1.2

import { Component, Input } from '@angular/core';
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { format } from 'date-fns';
import { ToastrService } from 'ngx-toastr';
Expand Down Expand Up @@ -53,7 +52,7 @@ import { Option } from 'src/app/shared/select/select.model';
`,
selector: 'xm-rooms-report',
standalone: true,
imports: [DropdownSelectComponent, DatePickerComponent, NgbPopover, TranslateModule],
imports: [DropdownSelectComponent, DatePickerComponent, TranslateModule],
})
export class RoomsReportComponent {
@Input() rooms: Option<ExamRoom, number>[] = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// SPDX-License-Identifier: EUPL-1.2

import { Component, Input } from '@angular/core';
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { format } from 'date-fns';
import { ToastrService } from 'ngx-toastr';
Expand Down Expand Up @@ -51,7 +50,7 @@ import { Option } from 'src/app/shared/select/select.model';
`,
selector: 'xm-students-report',
standalone: true,
imports: [DropdownSelectComponent, DatePickerComponent, NgbPopover, TranslateModule],
imports: [DropdownSelectComponent, DatePickerComponent, TranslateModule],
})
export class StudentsReportComponent {
@Input() students: Option<User, number>[] = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// SPDX-License-Identifier: EUPL-1.2

import { Component, Input } from '@angular/core';
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { format } from 'date-fns';
import { ToastrService } from 'ngx-toastr';
Expand Down Expand Up @@ -51,7 +50,7 @@ import { Option } from 'src/app/shared/select/select.model';
`,
selector: 'xm-teachers-report',
standalone: true,
imports: [DropdownSelectComponent, DatePickerComponent, NgbPopover, TranslateModule],
imports: [DropdownSelectComponent, DatePickerComponent, TranslateModule],
})
export class TeachersReportComponent {
@Input() teachers: Option<User, number>[] = [];
Expand Down
16 changes: 9 additions & 7 deletions ui/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,7 @@ import { LogoutComponent } from './session/logout/logout.component';
const buildTitle = (key: string, extraPart = ''): Observable<string> => {
const tx = inject(TranslateService);
const extra = extraPart ? ` ${extraPart}` : '';
return tx.get(key).pipe(
map(
() =>
`${tx.instant(key)}${extra}
- EXAM`,
),
);
return tx.get(key).pipe(map(() => `${tx.instant(key)}${extra}- EXAM`));
};

export const APP_ROUTES: Route[] = [
Expand Down Expand Up @@ -58,6 +52,14 @@ export const APP_ROUTES: Route[] = [
import('./enrolment/waiting-room/waiting-room.component').then((mod) => mod.WaitingRoomComponent),
title: () => buildTitle('i18n_waiting_room_title'),
},
{
path: 'early/:id/:hash',
loadComponent: () =>
import('./enrolment/waiting-room/waiting-room-early.component').then(
(mod) => mod.WaitingRoomEarlyComponent,
),
title: () => buildTitle('i18n_waiting_room_title'),
},
{
path: 'wrongroom/:eid/:mid',
loadComponent: () =>
Expand Down
4 changes: 0 additions & 4 deletions ui/src/app/calendar/calendar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ import { PageContentComponent } from 'src/app/shared/components/page-content.com
import { PageHeaderComponent } from 'src/app/shared/components/page-header.component';
import { DateTimeService } from 'src/app/shared/date/date.service';
import { ConfirmationDialogService } from 'src/app/shared/dialogs/confirmation-dialog.service';
import { HistoryBackComponent } from 'src/app/shared/history/history-back.component';
import { CourseCodeComponent } from 'src/app/shared/miscellaneous/course-code.component';
import { AutoFocusDirective } from 'src/app/shared/select/auto-focus.directive';
import { ExamInfo, Organisation } from './calendar.model';
import { CalendarService } from './calendar.service';
import { CalendarExamInfoComponent } from './helpers/exam-info.component';
Expand All @@ -32,8 +30,6 @@ import { SlotPickerComponent } from './helpers/slot-picker.component';
styleUrls: ['./calendar.component.scss'],
standalone: true,
imports: [
HistoryBackComponent,
AutoFocusDirective,
CalendarExamInfoComponent,
OptionalSectionsComponent,
OrganisationPickerComponent,
Expand Down
8 changes: 5 additions & 3 deletions ui/src/app/calendar/helpers/selected-room.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: EUPL-1.2

import { DatePipe, NgClass, NgIf, UpperCasePipe } from '@angular/common';
import { DatePipe, NgClass, UpperCasePipe } from '@angular/common';
import { Component, Input, OnChanges, OnInit, signal } from '@angular/core';
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
Expand Down Expand Up @@ -71,7 +71,9 @@ import { OrderByPipe } from 'src/app/shared/sorting/order-by.pipe';
{{ period.startsAt | date: 'dd.MM.yyyy HH:mm' }} -
{{ period.endsAt | date: 'dd.MM.yyyy HH:mm' }}
{{ period.description }}
<span *ngIf="period.remote" class="text-danger">(remote)</span>
@if (period.remote) {
<span class="text-danger">(remote)</span>
}
</div>
}
</div>
Expand All @@ -96,7 +98,7 @@ import { OrderByPipe } from 'src/app/shared/sorting/order-by.pipe';
`,
styleUrls: ['../calendar.component.scss'],
standalone: true,
imports: [NgClass, NgIf, NgbPopover, UpperCasePipe, DatePipe, TranslateModule, OrderByPipe],
imports: [NgClass, NgbPopover, UpperCasePipe, DatePipe, TranslateModule, OrderByPipe],
})
export class SelectedRoomComponent implements OnInit, OnChanges {
@Input() room!: ExamRoom;
Expand Down
Loading

0 comments on commit 810c1a8

Please sign in to comment.