/** * Display the session history of the current test center */ public function sessionHistory() { $testCenter = $this->getCurrentTestCenter(); $delivery = $this->getCurrentDelivery(false); $sessions = $this->getRequestParameter('session'); $requestOptions = $this->getRequestOptions(['sortby' => 'timestamp', 'sortorder' => 'desc', 'periodStart' => '', 'periodEnd' => '', 'detailed' => false]); if (!is_array($sessions)) { $sessions = $sessions ? explode(',', $sessions) : []; } $breadcrumbs = [BreadcrumbsHelper::testCenters(), BreadcrumbsHelper::testCenter($testCenter, TestCenterHelper::getTestCenters()), BreadcrumbsHelper::deliveries($testCenter, [BreadcrumbsHelper::diagnostics($testCenter)])]; $viewData = ['testCenter' => $testCenter->getUri(), 'set' => TestCenterHelper::getSessionHistory($sessions, true, $requestOptions), 'sessions' => $sessions, 'sortBy' => $requestOptions['sortBy'], 'sortOrder' => $requestOptions['sortOrder'], 'periodStart' => $requestOptions['periodStart'], 'periodEnd' => $requestOptions['periodEnd']]; if ($delivery) { $breadcrumbs[] = BreadcrumbsHelper::deliveryMonitoring($testCenter, $delivery, DeliveryHelper::getDeliveries($testCenter)); $viewData['delivery'] = $delivery->getUri(); } else { $breadcrumbs[] = BreadcrumbsHelper::deliveryMonitoringAll($testCenter, DeliveryHelper::getDeliveries($testCenter)); } $breadcrumbs[] = BreadcrumbsHelper::sessionHistory($testCenter, $delivery, $sessions); if (count($sessions) > 1) { $title = __('Detailed Session History of a selection'); } else { $deliveryExecution = \taoDelivery_models_classes_execution_ServiceProxy::singleton()->getDeliveryExecution($sessions[0]); $title = __('Detailed Session History of %s', $deliveryExecution->getLabel()); } $this->setData('title', $title); $this->composeView('session-history', $viewData, $breadcrumbs); }
/** * Get the requested delivery resource * Use this to identify which delivery is currently being selected buy the proctor * * @param bool $mandatory Throws an exception if the delivery is not provided * @return core_kernel_classes_Resource * @throws \common_Exception */ protected function getCurrentDelivery($mandatory = true) { if (is_null($this->currentDelivery)) { if ($this->hasRequestParameter('delivery')) { //get test center resource from its uri $deliveryUri = $this->getRequestParameter('delivery'); $this->currentDelivery = DeliveryHelper::getDelivery($deliveryUri); } else { if ($mandatory) { //@todo use a better exception throw new \common_Exception('no current delivery'); } } } return $this->currentDelivery; }
public static function propertyChange(MetadataModified $event) { $resource = $event->getResource(); $service = ServiceManager::getServiceManager()->get(DeliveryMonitoringService::CONFIG_ID); $tracked = array_merge([PROPERTY_USER_FIRSTNAME, PROPERTY_USER_LASTNAME], array_map(function ($field) { return $field['property']->getUri(); }, DeliveryHelper::getExtraFieldsProperties())); if (in_array($event->getMetadataUri(), $tracked) && $resource->hasType(new \core_kernel_classes_Class(TAO_CLASS_SUBJECT))) { $deliveryExecutionsData = $service->find([DeliveryMonitoringService::TEST_TAKER => $resource->getUri()], []); foreach ($deliveryExecutionsData as $data) { $data->updateData([DeliveryMonitoringService::TEST_TAKER, DeliveryMonitoringService::TEST_TAKER_FIRST_NAME, DeliveryMonitoringService::TEST_TAKER_LAST_NAME]); $success = $service->save($data); if (!$success) { \common_Logger::w('monitor cache for delivery ' . $data[DeliveryMonitoringService::DELIVERY_EXECUTION_ID] . ' could not be updated. TestTaker data has not been changed'); } } } }
/** * Extra Time handling: add or remove time on delivery executions * * @throws \common_Exception */ public function extraTime() { $deliveryExecution = $this->getRequestParameter('execution'); $extraTime = floatval($this->getRequestParameter('time')); if (!is_array($deliveryExecution)) { $deliveryExecution = array($deliveryExecution); } try { $reported = DeliveryHelper::setExtraTime($deliveryExecution, $extraTime); $notReported = array_diff($deliveryExecution, $reported); $this->returnJson(array('success' => !count($notReported), 'processed' => $reported, 'unprocessed' => $notReported)); } catch (ServiceNotFoundException $e) { \common_Logger::w('No delivery service defined for proctoring'); $this->returnError('Proctoring interface not available'); } }
/** * @deprecated please use DeliveryHelper */ public function setHasBeenPaused($deliveryExecution) { return DeliveryHelper::setHasBeenPaused($deliveryExecution); }
$i = 0; $proctorNum = 1; $ttNum = 1; $subClass = $testTakerService->createSubClass($testTakerService->getRootClass(), 'jmeter_test_taker_' . $totalTtNum); while ($i < $totalProctorNum) { if ($userService->loginAvailable('Jmeter_proctor_' . $proctorNum)) { $tts = array(); //create sample group $testCenter = $testCenterService->createInstance(new \core_kernel_classes_Class($testCenterService::CLASS_URI), 'jmeter_test_center_' . $proctorNum); $proctor = $userService->addUser('Jmeter_proctor_' . $proctorNum, 'Jmeter_proctor_' . $proctorNum, new \core_kernel_classes_Resource("http://www.tao.lu/Ontologies/TAOProctor.rdf#ProctorRole"), $userClass); $proctor->setPropertyValue(new core_kernel_classes_Property($proctorManagementService::PROPERTY_ASSIGNED_PROCTOR_URI), $testCenter); $proctor->setPropertyValue(new core_kernel_classes_Property($proctorManagementService::PROPERTY_ADMINISTRATOR_URI), $testCenter); $i++; $j = 0; while ($j < $ttByProctor) { if ($userService->loginAvailable('jmeter_TT_' . $ttNum)) { $tt = $testTakerCrudService->createFromArray(array(PROPERTY_USER_LOGIN => 'jmeter_TT_' . $ttNum, PROPERTY_USER_PASSWORD => 'jmeter_TT_' . $ttNum, RDFS_LABEL => 'jmeter_tt' . $ttNum, PROPERTY_USER_FIRSTNAME => 'jmeter_tt_' . $ttNum, PROPERTY_USER_LASTNAME => 'jmeter_tt_' . $ttNum, RDF_TYPE => $subClass)); $tts[] = $tt->getUri(); $j++; } $ttNum++; } $testCenterService->addTestTaker($tt->getUri(), $testCenter); //add delivery to eligible list \oat\taoProctoring\model\EligibilityService::singleton()->createEligibility($testCenter, $delivery); \oat\taoProctoring\model\EligibilityService::singleton()->setEligibleTestTakers($testCenter, $delivery, $tts); //assign tt to delivery \oat\taoProctoring\helpers\DeliveryHelper::assignTestTakers($tts, $delivery->getUri(), $testCenter->getUri()); } $proctorNum++; }
/** * @param string $sortBy * @return string */ public static function getSortByColumn($sortBy) { $map = array_merge(['firstname' => self::COLUMN_TEST_TAKER_FIRST_NAME, 'lastname' => self::TEST_TAKER_LAST_NAME, 'delivery' => self::DELIVERY_NAME, 'status' => self::STATUS, 'connectivity' => self::CONNECTIVITY], array_combine(array_map(function ($property) { return strtolower($property['id']); }, DeliveryHelper::getExtraFields()), array_map(function ($property) { return $property['id']; }, DeliveryHelper::getExtraFields()))); return array_key_exists(strtolower($sortBy), $map) ? $map[strtolower($sortBy)] : self::DEFAULT_SORT_COLUMN; }
/** * @param bool $overwrite */ private function addExtraFieldsValues($overwrite = false) { $user = $this->getUser(); if ($user) { $fields = DeliveryHelper::getExtraFieldsProperties(); foreach ($fields as $field) { $values = $user->getPropertyValues($field['property']); if (!empty($values) && is_array($values)) { $this->addValue($field['id'], (string) $values[0], $overwrite); } } } }
/** * Build the context of the given candidate test $session as an associative array. This array * is especially usefull to transmit the test context to a view as JSON data. * * The returned array contains the following keys: * * * state: The state of test session. * * navigationMode: The current navigation mode. * * submissionMode: The current submission mode. * * remainingAttempts: The number of remaining attempts for the current item. * * isAdaptive: Whether or not the current item is adaptive. * * itemIdentifier: The identifier of the current item. * * itemSessionState: The state of the current assessment item session. * * timeConstraints: The time constraints in force. * * testTitle: The title of the test. * * testPartId: The identifier of the current test part. * * sectionTitle: The title of the current section. * * numberItems: The total number of items eligible to the candidate. * * numberCompleted: The total number items considered to be completed by the candidate. * * moveForwardUrl: The URL to be dereferenced to perform a moveNext on the session. * * moveBackwardUrl: The URL to be dereferenced to perform a moveBack on the session. * * skipUrl: The URL to be dereferenced to perform a skip on the session. * * commentUrl: The URL to be dereferenced to leave a comment about the current item. * * timeoutUrl: The URL to be dereferenced when the time constraints in force reach their maximum. * * canMoveBackward: Whether or not the candidate is allowed/able to move backward. * * jumps: The possible jumpers the candidate is allowed to undertake among eligible items. * * itemServiceApiCall: The JavaScript code to be executed to instantiate the current item. * * rubrics: The XHTML compiled content of the rubric blocks to be displayed for the current item if any. * * allowComment: Whether or not the candidate is allowed to leave a comment about the current item. * * allowSkipping: Whether or not the candidate is allowed to skip the current item. * * considerProgress: Whether or not the test driver view must consider to give a test progress feedback. * * @param AssessmentTestSession $session A given AssessmentTestSession object. * @param array $testMeta An associative array containing meta-data about the test definition taken by the candidate. * @param string $qtiTestDefinitionUri The URI of a reference to an Assessment Test definition in the knowledge base. * @param string $qtiTestCompilationUri The Uri of a reference to an Assessment Test compilation in the knowledge base. * @param string $standalone * @param string $compilationDirs An array containing respectively the private and public compilation directories. * @return array The context of the candidate session. */ public static function buildAssessmentTestContext(AssessmentTestSession $session, array $testMeta, $qtiTestDefinitionUri, $qtiTestCompilationUri, $standalone, $compilationDirs) { $context = array(); // The state of the test session. $context['state'] = $session->getState(); // Default values for the test session context. $context['navigationMode'] = null; $context['submissionMode'] = null; $context['remainingAttempts'] = 0; $context['isAdaptive'] = false; $hasBeenPaused = false; if (common_ext_ExtensionsManager::singleton()->isEnabled('taoProctoring')) { $hasBeenPaused = \oat\taoProctoring\helpers\DeliveryHelper::getHasBeenPaused($session->getSessionId()); } $context['hasBeenPaused'] = $hasBeenPaused; if ($session->getState() === AssessmentTestSessionState::INTERACTING) { $config = common_ext_ExtensionsManager::singleton()->getExtensionById('taoQtiTest')->getConfig('testRunner'); // The navigation mode. $context['navigationMode'] = $session->getCurrentNavigationMode(); // The submission mode. $context['submissionMode'] = $session->getCurrentSubmissionMode(); // The number of remaining attempts for the current item. $context['remainingAttempts'] = $session->getCurrentRemainingAttempts(); // Whether or not the current step is time out. $context['isTimeout'] = self::isTimeout($session); // The identifier of the current item. $context['itemIdentifier'] = $session->getCurrentAssessmentItemRef()->getIdentifier(); // The state of the current AssessmentTestSession. $context['itemSessionState'] = $session->getCurrentAssessmentItemSession()->getState(); // Whether the current item is adaptive. $context['isAdaptive'] = $session->isCurrentAssessmentItemAdaptive(); // Whether the current item is the very last one of the test. $context['isLast'] = $session->getRoute()->isLast(); // The current position in the route. $context['itemPosition'] = $session->getRoute()->getPosition(); // Time constraints. $context['timeConstraints'] = self::buildTimeConstraints($session); // Test title. $context['testTitle'] = $session->getAssessmentTest()->getTitle(); // Test Part title. $context['testPartId'] = $session->getCurrentTestPart()->getIdentifier(); // Current Section title. $context['sectionTitle'] = $session->getCurrentAssessmentSection()->getTitle(); // Number of items composing the test session. $context['numberItems'] = $session->getRouteCount(AssessmentTestSession::ROUTECOUNT_FLOW); // Number of items completed during the test session. $context['numberCompleted'] = self::testCompletion($session); // Number of items presented during the test session. $context['numberPresented'] = $session->numberPresented(); // Whether or not the progress of the test can be inferred. $context['considerProgress'] = self::considerProgress($session, $testMeta, $config); // Whether or not the deepest current section is visible. $context['isDeepestSectionVisible'] = $session->getCurrentAssessmentSection()->isVisible(); // The URLs to be called to move forward/backward in the Assessment Test Session or skip or comment. $context['moveForwardUrl'] = self::buildActionCallUrl($session, 'moveForward', $qtiTestDefinitionUri, $qtiTestCompilationUri, $standalone); $context['moveBackwardUrl'] = self::buildActionCallUrl($session, 'moveBackward', $qtiTestDefinitionUri, $qtiTestCompilationUri, $standalone); $context['nextSectionUrl'] = self::buildActionCallUrl($session, 'nextSection', $qtiTestDefinitionUri, $qtiTestCompilationUri, $standalone); $context['skipUrl'] = self::buildActionCallUrl($session, 'skip', $qtiTestDefinitionUri, $qtiTestCompilationUri, $standalone); $context['commentUrl'] = self::buildActionCallUrl($session, 'comment', $qtiTestDefinitionUri, $qtiTestCompilationUri, $standalone); $context['timeoutUrl'] = self::buildActionCallUrl($session, 'timeout', $qtiTestDefinitionUri, $qtiTestCompilationUri, $standalone); $context['endTestSessionUrl'] = self::buildActionCallUrl($session, 'endTestSession', $qtiTestDefinitionUri, $qtiTestCompilationUri, $standalone); $context['keepItemTimedUrl'] = self::buildActionCallUrl($session, 'keepItemTimed', $qtiTestDefinitionUri, $qtiTestCompilationUri, $standalone); // If the candidate is allowed to move backward e.g. first item of the test. $context['canMoveBackward'] = $session->canMoveBackward(); // The places in the test session where the candidate is allowed to jump to. $context['jumps'] = self::buildPossibleJumps($session); // The test review screen setup if (!empty($config['test-taker-review']) && $context['considerProgress']) { // The navigation map in order to build the test navigator $navigator = self::getNavigatorMap($session); if ($navigator !== NavigationMode::LINEAR) { $context['navigatorMap'] = $navigator['map']; $context['itemFlagged'] = self::getItemFlag($session, $context['itemPosition']); } else { $navigator = self::countItems($session); } // Extract the progression stats $context['numberFlagged'] = $navigator['numberItemsFlagged']; $context['numberItemsPart'] = $navigator['numberItemsPart']; $context['numberItemsSection'] = $navigator['numberItemsSection']; $context['numberCompletedPart'] = $navigator['numberCompletedPart']; $context['numberCompletedSection'] = $navigator['numberCompletedSection']; $context['numberPresentedPart'] = $navigator['numberPresentedPart']; $context['numberPresentedSection'] = $navigator['numberPresentedSection']; $context['numberFlaggedPart'] = $navigator['numberFlaggedPart']; $context['numberFlaggedSection'] = $navigator['numberFlaggedSection']; $context['itemPositionPart'] = $navigator['itemPositionPart']; $context['itemPositionSection'] = $navigator['itemPositionSection']; // The URLs to be called to move to a particular item in the Assessment Test Session or mark item for later review. $context['jumpUrl'] = self::buildActionCallUrl($session, 'jumpTo', $qtiTestDefinitionUri, $qtiTestCompilationUri, $standalone); $context['markForReviewUrl'] = self::buildActionCallUrl($session, 'markForReview', $qtiTestDefinitionUri, $qtiTestCompilationUri, $standalone); } else { // Setup data for progress bar when displaying position and timed section exit control $numberItems = self::countItems($session); $context['numberCompletedPart'] = $numberItems['numberCompletedPart']; $context['numberCompletedSection'] = $numberItems['numberCompletedSection']; $context['numberItemsSection'] = $numberItems['numberItemsSection']; $context['numberItemsPart'] = $numberItems['numberItemsPart']; $context['itemPositionPart'] = $numberItems['itemPositionPart']; $context['itemPositionSection'] = $numberItems['itemPositionSection']; } // The code to be executed to build the ServiceApi object to be injected in the QTI Item frame. $context['itemServiceApiCall'] = self::buildServiceApi($session, $qtiTestDefinitionUri, $qtiTestCompilationUri); // Rubric Blocks. $rubrics = array(); // -- variables used in the included rubric block templates. // base path (base URI to be used for resource inclusion). $basePathVarName = TAOQTITEST_BASE_PATH_NAME; ${$basePathVarName} = $compilationDirs['public']->getPublicAccessUrl(); // state name (the variable to access to get the state of the assessmentTestSession). $stateName = TAOQTITEST_RENDERING_STATE_NAME; ${$stateName} = $session; // views name (the variable to be accessed for the visibility of rubric blocks). $viewsName = TAOQTITEST_VIEWS_NAME; ${$viewsName} = array(View::CANDIDATE); foreach ($session->getRoute()->current()->getRubricBlockRefs() as $rubric) { $data = $compilationDirs['private']->read($rubric->getHref()); $tmpDir = \tao_helpers_File::createTempDir(); $tmpFile = $tmpDir . basename($rubric->getHref()); file_put_contents($tmpFile, $data); ob_start(); include $tmpFile; $rubrics[] = ob_get_clean(); unlink($tmpFile); rmdir($tmpDir); } $context['rubrics'] = $rubrics; // Comment allowed? Skipping allowed? Logout or Exit allowed ? $context['allowComment'] = self::doesAllowComment($session); $context['allowSkipping'] = self::doesAllowSkipping($session); $context['exitButton'] = self::doesAllowExit($session); $context['logoutButton'] = self::doesAllowLogout($session); $context['categories'] = self::getCategories($session); // loads the specific config into the context object $configMap = array('timerWarning' => 'timerWarning', 'progress-indicator' => 'progressIndicator', 'progress-indicator-scope' => 'progressIndicatorScope', 'test-taker-review' => 'reviewScreen', 'test-taker-review-region' => 'reviewRegion', 'test-taker-review-scope' => 'reviewScope', 'test-taker-review-prevents-unseen' => 'reviewPreventsUnseen', 'test-taker-review-can-collapse' => 'reviewCanCollapse', 'next-section' => 'nextSection', 'keep-timer-up-to-timeout' => 'keepTimerUpToTimeout'); foreach ($configMap as $configKey => $contextKey) { if (isset($config[$configKey])) { $context[$contextKey] = $config[$configKey]; } } // optionally extend the context if (isset($config['extraContextBuilder']) && class_exists($config['extraContextBuilder'])) { $builder = new $config['extraContextBuilder'](); if ($builder instanceof \oat\taoQtiTest\models\TestContextBuilder) { $builder->extendAssessmentTestContext($context, $session, $testMeta, $qtiTestDefinitionUri, $qtiTestCompilationUri, $standalone, $compilationDirs); } else { common_Logger::i('Try to use an extra context builder class that is not an instance of \\oat\\taoQtiTest\\models\\TestContextBuilder!'); } } } return $context; }