From b7c5d24428b21b1b5639414d4dd2ea596269608f Mon Sep 17 00:00:00 2001 From: Vibhu Dujari Date: Sat, 4 May 2024 22:54:33 +0530 Subject: [PATCH] * use popup instead of footer in monitorer * add multipleface monitorer * add noise monitorer --- app/models/contest-attempt.js | 18 ++- app/models/contest.js | 3 + .../full-screen-contest-view/component.js | 23 ++++ .../full-screen-contest-view/template.hbs | 107 ++++++++++++++--- .../full-screen-problem-view/template.hbs | 2 +- .../intermediate-contest-view/template.hbs | 10 +- app/services/monitorer.js | 110 ++++++++++++++++-- 7 files changed, 240 insertions(+), 33 deletions(-) diff --git a/app/models/contest-attempt.js b/app/models/contest-attempt.js index 6f99a76..761f522 100644 --- a/app/models/contest-attempt.js +++ b/app/models/contest-attempt.js @@ -17,21 +17,27 @@ export default Model.extend({ }), monitorerData: DS.attr(), tabSwitchCount: Ember.computed('monitorerData', function() { - return this.monitorerData && this.monitorerData['tab-switch-count'] + return this.monitorerData && this.monitorerData['tab-switch-count'] || 0 }), tabSwitchTimePenaltyMinutes: Ember.computed('monitorerData', function() { - return this.monitorerData && this.monitorerData['tab-switch-count'] * 10 + return this.monitorerData && this.monitorerData['tab-switch-count'] > 3 ? (this.monitorerData['tab-switch-count'] - 3) * 10 : 0 }), windowResizeCount: Ember.computed('monitorerData', function() { - return this.monitorerData && this.monitorerData['window-resize-count'] + return this.monitorerData && this.monitorerData['window-resize-count'] || 0 }), windowResizeTimePenaltyMinutes: Ember.computed('monitorerData', function() { - return this.monitorerData && this.monitorerData['window-resize-count'] * 10 + return this.monitorerData && this.monitorerData['window-resize-count'] || 0 }), noFaceCount: Ember.computed('monitorerData', function() { - return this.monitorerData && this.monitorerData['no-face-count'] + return this.monitorerData && this.monitorerData['no-face-count'] || 0 }), noFaceTimePenaltyMinutes: Ember.computed('monitorerData', function() { - return this.monitorerData && this.monitorerData['no-face-count'] * 10 + return this.monitorerData && this.monitorerData['no-face-count'] > 3 ? (this.monitorerData['no-face-count'] - 3) * 10 : 0 + }), + multipleFacesCount: Ember.computed('monitorerData', function() { + return this.monitorerData && this.monitorerData['multiple-faces-count'] || 0 + }), + multipleFacesTimePenaltyMinutes: Ember.computed('monitorerData', function() { + return this.monitorerData && this.monitorerData['multiple-faces-count'] > 3 ? (this.monitorerData['multiple-faces-count'] - 3) * 10 : 0 }), }); diff --git a/app/models/contest.js b/app/models/contest.js index 1500296..7c6a0c2 100644 --- a/app/models/contest.js +++ b/app/models/contest.js @@ -64,4 +64,7 @@ export default Model.extend({ disallowTabSwitch: DS.attr(), disallowWindowResize: DS.attr(), disallowNoFace: DS.attr(), + disallowMultipleFaces: DS.attr(), + disallowNoise: DS.attr(), + enforceFullscreen: DS.attr(), }); diff --git a/app/pods/components/full-screen-contest-view/component.js b/app/pods/components/full-screen-contest-view/component.js index 22fcf6e..5d06c65 100644 --- a/app/pods/components/full-screen-contest-view/component.js +++ b/app/pods/components/full-screen-contest-view/component.js @@ -1,5 +1,7 @@ import Component from '@ember/component'; +import ENV from 'hackerblocks/config/environment'; import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; export default class FullScreenContestView extends Component { @service monitorer @@ -9,4 +11,25 @@ export default class FullScreenContestView extends Component { this._super(...arguments) this.setupMonitorer() } + + async didInsertElement() { + // await this.ajax.request(ENV.apiHost + '/time') + // if(this.contest.enforceFullscreen) { + // const fullScreenContestViewElement = document.getElementById('fullsceen-contest-view') + // if (fullScreenContestViewElement.requestFullscreen) { + // fullScreenContestViewElement.requestFullscreen.call(fullScreenContestViewElement) + // } else if (fullScreenContestViewElement.webkitRequestFullscreen) { /* Safari */ + // fullScreenContestViewElement.webkitRequestFullscreen(); + // } else if (fullScreenContestViewElement.msRequestFullscreen) { /* IE11 */ + // fullScreenContestViewElement.msRequestFullscreen(); + // } + // } + } + + re + + @action + resetFaultMessages() { + this.monitorer.resetFaultMessages() + } } diff --git a/app/pods/components/full-screen-contest-view/template.hbs b/app/pods/components/full-screen-contest-view/template.hbs index 6ce2b57..0826e9e 100644 --- a/app/pods/components/full-screen-contest-view/template.hbs +++ b/app/pods/components/full-screen-contest-view/template.hbs @@ -1,4 +1,4 @@ -
+
@@ -20,7 +20,7 @@ {{#if contest.disallowTabSwitch}}
  • - Tab Switching Prohibited You will get a penalty of 10 mins in case you switch tab. + Tab Switching Prohibited You will get a penalty of 10 mins in case you switch tab after 3 faults.
  • {{/if}} {{#if contest.disallowWindowResize}} @@ -30,7 +30,12 @@ {{/if}} {{#if contest.disallowNoFace}}
  • - Face Detection Enabled You will get a penalty of 10 mins every 5 secs if your face is not visible in camera. + Face Detection Enabled You will get a penalty of 10 mins every 5 secs if your face is not visible in camera after 3 faults. +
  • + {{/if}} + {{#if contest.disallowMultipleFaces}} +
  • + Multiple Face Detection Enabled You will get a penalty of 10 mins every 5 secs if multiple faces are visible in camera after 3 faults.
  • {{/if}} @@ -45,7 +50,7 @@
    {{#if contest.disallowNoFace}}
    - +
    {{/if}}
    @@ -69,27 +74,24 @@ - {{#if (or attempt.tabSwitchCount attempt.windowResizeCount)}} + {{#if (or attempt.tabSwitchCount attempt.windowResizeCount attempt.noFaceCount attempt.multipleFacesCount)}}
    Penalties
    {{#if contest.disallowTabSwitch}} - Total Tab Switches: {{or attempt.tabSwitchCount 0}} -
    - Total Time Penalty: {{or attempt.tabSwitchTimePenaltyMinutes 0}} mins + Total Tab Switches: {{or attempt.tabSwitchCount 0}} | {{or attempt.tabSwitchTimePenaltyMinutes 0}} mins deducted {{/if}}
    {{#if contest.disallowWindowResize}} - Total Window Resizes: {{or attempt.windowResizeCount 0}} -
    - Total Time Penalty: {{or attempt.windowResizeTimePenaltyMinutes 0}} mins + Total Window Resizes: {{or attempt.windowResizeCount 0}} | {{or attempt.windowResizeTimePenaltyMinutes 0}} mins deducted {{/if}}
    {{#if contest.disallowNoFace}} - Total Face Undetected: {{or attempt.noFaceCount 0}} -
    - Total Time Penalty: {{or attempt.noFaceTimePenaltyMinutes 0}} mins + Total Face Undetected: {{or attempt.noFaceCount 0}} | {{or attempt.noFaceTimePenaltyMinutes 0}} mins deducted + {{/if}} + {{#if contest.disallowMultipleFaces}} + Total Multiple Faces Detected: {{attempt.multipleFacesCount}} | {{attempt.multipleFacesTimePenaltyMinutes}} mins deducted {{/if}}
    @@ -107,7 +109,6 @@ @route={{route}} @contest={{contest}} /> {{yield}}
    -
    {{#if showSubmitDialog}} @@ -135,6 +136,77 @@ {{/if}} + {{#if monitorer.faultTrigger}} + +
    +
    +

    Faults

    +
      +
    • + {{#if monitorer.faultMessages.tabSwitch}} + You switched tab {{attempt.tabSwitchCount}} {{if (eq attempt.tabSwitchCount 1) 'time' 'times'}}. + {{#if (gt attempt.tabSwitchCount 3)}} + 10 minutes deducted. + {{/if }} + {{/if}} +
    • +
    • + {{#if monitorer.faultMessages.noFace}} + No Face detected {{attempt.noFaceCount}} {{if (eq attempt.noFaceCount 1) 'time' 'times'}}. + {{#if (gt attempt.noFaceCount 3)}} + 10 minutes deducted. + {{/if }} + {{/if}} +
    • +
    • + {{#if monitorer.faultMessages.multipleFaces}} + Multiple faces detected {{attempt.multipleFacesCount}} {{if (eq attempt.multipleFacesCount 1) 'time' 'times'}}. + {{#if (gt attempt.multipleFacesCount 3)}} + 10 minutes deducted. + {{/if }} + {{/if}} +
    • +
    • + {{#if monitorer.faultMessages.windowResize}} + You resized window {{attempt.windowResizeCount}} {{if (eq attempt.windowResizeCount 1) 'time' 'times'}}. + {{#if (gt attempt.multipleFacesCount 3)}} + 10 minutes deducted. + {{/if }} + {{/if}} +
    • +
    • + {{#if monitorer.faultMessages.noise}} + Noise detected. + {{/if}} +
    • +
    +
    +
    + {{#if contest.disallowTabSwitch}} + Total Tab Switches: {{attempt.tabSwitchCount}} | {{attempt.tabSwitchTimePenaltyMinutes}} mins deducted + {{/if}} +
    + {{#if contest.disallowWindowResize}} + Total Window Resizes: {{attempt.windowResizeCount}} | {{attempt.windowResizeTimePenaltyMinutes}} mins deducted + {{/if}} +
    + {{#if contest.disallowNoFace}} + Total Face Undetected: {{attempt.noFaceCount}} | {{attempt.noFaceTimePenaltyMinutes}} mins deducted + {{/if}} +
    + {{#if contest.disallowMultipleFaces}} + Total Multiple Faces detected: {{attempt.multipleFacesCount}} | {{attempt.multipleFacesTimePenaltyMinutes}} mins deducted + {{/if}} +
    +
    +
    + {{/if}} +
    + +
    @@ -151,4 +223,9 @@ No face detected in camera. 10 minutes deducted.
    + +
    + Multiple faces detected in camera. 10 minutes deducted. +
    +
    \ No newline at end of file diff --git a/app/pods/components/full-screen-problem-view/template.hbs b/app/pods/components/full-screen-problem-view/template.hbs index a661eb2..c9587e6 100644 --- a/app/pods/components/full-screen-problem-view/template.hbs +++ b/app/pods/components/full-screen-problem-view/template.hbs @@ -1,4 +1,4 @@ -
    +
    Problem
    diff --git a/app/pods/components/intermediate-contest-view/template.hbs b/app/pods/components/intermediate-contest-view/template.hbs index 2a7484a..90aac82 100644 --- a/app/pods/components/intermediate-contest-view/template.hbs +++ b/app/pods/components/intermediate-contest-view/template.hbs @@ -89,7 +89,7 @@ {{#if (or contest.disallowTabSwitch contest.disallowWindowResize contest.disallowNoFace)}}
    {{#if contest.disallowTabSwitch}} - Tab Switching is prohibited on this contest. You will face a penalty of 10 mins in case you :- + Tab Switching is prohibited on this contest. You will face a penalty of 10 mins after 3 faults in case you :-
    • • Switch the current tab
    • • Minimize the browser or switch to any other application
    • @@ -104,11 +104,17 @@
    {{/if}} {{#if contest.disallowNoFace}} - Face detection is enabled on this contest. You will face a penalty of 10 mins every 5 secs in case :- + Face detection is enabled on this contest. You will face a penalty of 10 mins every 5 secs after 3 faults in case :-
    • • Your face is not visible in camera.
    {{/if}} + {{#if contest.disallowMultipleFaces}} + Multiple face detection is enabled on this contest. You will face a penalty of 10 mins every 5 secs after 3 faults in case :- +
      +
    • • More than 1 face is visible in camera.
    • +
    + {{/if}}
    {{/if}}
    diff --git a/app/services/monitorer.js b/app/services/monitorer.js index ff4a777..6f41667 100644 --- a/app/services/monitorer.js +++ b/app/services/monitorer.js @@ -8,11 +8,23 @@ export default Service.extend({ store: service(), tabSwitchTrigger: false, noFaceTrigger: false, + multipleFacesTrigger: false, windowResizeTrigger: false, + faultTrigger: false, noFaceThrottled: false, - noFaceDetected: false, + multipleFacesThrottled: false, + oneFaceDetected: false, + multipleFacesDetected: false, isMonitorerFaultEventHandlerAdded: false, failureRedirect: null, + isLifeFeedEnabled: false, + faultMessages: { + tabSwitch: false, + noFace: false, + multipleFaces: false, + windowResize: false, + noise: false + }, init() { this._super(...arguments) this.monitorerFaultEventHandler = this.monitorerFaultEventHandler.bind(this) @@ -34,7 +46,7 @@ export default Service.extend({ window.addEventListener('monitorersuccess', this.monitorerSuccessEventHandler) } - if(this.contest.disallowNoFace || contest.disallowTabSwitch || contest.disallowWindowResize) { + if(this.contest.disallowNoFace || contest.disallowTabSwitch || contest.disallowWindowResize || contest.disallowMultipleFaces) { await this.enableRightClickMonitorer() await this.enableKeyboardMonitorer({ console: true }) } @@ -48,7 +60,15 @@ export default Service.extend({ } if(this.contest.disallowNoFace) { - await this.enableNoFaceMonitorer({ noFace: true }) + await this.enableNoFaceMonitorer() + } + + if(this.contest.disallowMultipleFaces) { + await this.enableMultipleFacesMonitorer() + } + + if(this.contest.disallowNoise) { + await this.enableNoiseMonitorer({ volume: 3 }) } }, @@ -73,7 +93,18 @@ export default Service.extend({ async enableNoFaceMonitorer() { await this.monitorer.enable({ noFace: true }) - await this.monitorer.enable({ liveFeed: true }) + if(!this.isLiveFeedEnabled) { + await this.monitorer.enable({ liveFeed: true }) + this.set('isLifeFeedEnabled', true) + } + }, + + async enableMultipleFacesMonitorer() { + await this.monitorer.enable({ multipleFaces: true }) + if(!this.isLiveFeedEnabled) { + await this.monitorer.enable({ liveFeed: true }) + this.set('isLifeFeedEnabled', true) + } }, async enableRightClickMonitorer() { @@ -84,6 +115,10 @@ export default Service.extend({ await this.monitorer.enable({ keyboard: options }) }, + async enableNoiseMonitorer(options) { + await this.monitorer.enable({ noise: options }) + }, + async disableTabSwitchMonitorer() { await this.monitorer.disable({ tabSwitch: true }) }, @@ -97,6 +132,11 @@ export default Service.extend({ await this.monitorer.disable({ liveFeed: true }) }, + async disableMultipleFacesMonitorer() { + await this.monitorer.disable({ multipleFaces: true }) + await this.monitorer.disable({ liveFeed: true }) + }, + async disableRightClickMonitorer() { await this.monitorer.disable({ rightClick: true }) }, @@ -104,6 +144,16 @@ export default Service.extend({ async disableKeyboardMonitorer(options) { await this.monitorer.disable({ keyboard: true }) }, + + resetFaultMessages() { + this.set('faultMessages', { + tabSwitch: false, + windowResize: false, + noFace: false, + multipleFace: false + }) + this.set('faultTrigger', false) + }, async monitorerFaultEventHandler(e) { const currentAttempt = await this.contest.currentAttempt @@ -113,7 +163,10 @@ export default Service.extend({ case "TAB_SWITCHED": await this.handleTabSwitchFault(); break; case "WINDOW_RESIZED": await this.handleWindowResizeFault(e.detail); break; case "NO_FACE_DETECTED": await this.handleNoFaceFault(e.detail); - this.set('noFaceDetected', true); break; + this.set('oneFaceDetected', false); break; + case "MULTIPLE_FACES_DETECTED": await this.handleMultipleFacesFault(e.detail); + this.set('oneFaceDetected', false); break; + case "NOISE_DETECTED": await this.handleNoiseFault(); break; } }, @@ -125,12 +178,17 @@ export default Service.extend({ async monitorerSuccessEventHandler(e) { switch(e.detail.code) { - case "ONEFACEDETECTED": this.set('noFaceDetected', false); break; + case "ONEFACEDETECTED": this.set('oneFaceDetected', true); break; } }, async handleTabSwitchFault() { - if(!document.hidden) return this.set('tabSwitchTrigger', true) + // if(!document.hidden) return this.set('tabSwitchTrigger', true) + if(!document.hidden) { + this.set('faultMessages.tabSwitch', true) + return this.set('faultTrigger', true) + } + const currentAttempt = await this.contest.currentAttempt await this.api.request(`/contest-attempts/${currentAttempt.id}/report-monitorer-fault`, { @@ -144,7 +202,9 @@ export default Service.extend({ async handleWindowResizeFault(details) { if(details.message === 'browser_unfullscreened') { - this.set('windowResizeTrigger', true) + // this.set('windowResizeTrigger', true) + this.set('faultMessages.windowResize', true) + this.set('faultTrigger', true) const currentAttempt = await this.contest.currentAttempt await this.api.request(`/contest-attempts/${currentAttempt.id}/report-monitorer-fault`, { @@ -162,7 +222,10 @@ export default Service.extend({ this.set('noFaceThrottled', true) setTimeout(() => this.set('noFaceThrottled', false), 5000) - this.set('noFaceTrigger', true) + // this.set('noFaceTrigger', true) + this.set('faultMessages.noFace', true) + this.set('faultTrigger', true) + const currentAttempt = await this.contest.currentAttempt const imgFileArray = new Uint8Array(await details.imageBlob.arrayBuffer()) @@ -176,5 +239,34 @@ export default Service.extend({ }) await this.store.findRecord('contest-attempt', currentAttempt.id) } + }, + + async handleMultipleFacesFault(details) { + if(!this.multipleFacesThrottled) { + this.set('multipleFacesThrottled', true) + setTimeout(() => this.set('multipleFacesThrottled', false), 5000) + + // this.set('noFaceTrigger', true) + this.set('faultMessages.multipleFaces', true) + this.set('faultTrigger', true) + + + const currentAttempt = await this.contest.currentAttempt + const imgFileArray = new Uint8Array(await details.imageBlob.arrayBuffer()) + + await this.api.request(`/contest-attempts/${currentAttempt.id}/report-monitorer-fault`, { + method: 'POST', + data: { + fault_type: 'multiple_faces', + image_file_array: imgFileArray + } + }) + await this.store.findRecord('contest-attempt', currentAttempt.id) + } + }, + + async handleNoiseFault() { + this.set('faultMessages.noise', true) + this.set('faultTrigger', true) } });