0) {\n $('#deactivate_standard').prop(\"disabled\", false);\n $(document).on(\"change\", \"#deactivate_standard\", function () {\n if (this.checked) {\n $('#inputfieldset').prop(\"disabled\", false);\n $('#activate_standard').prop(\"value\", false);\n str.get_string('different_period', 'block_evasys_sync').done(function (s) {\n $('#evaluationperiod').text(s);\n });\n enable();\n } else {\n $('#inputfieldset').prop(\"disabled\", true);\n $('#activate_standard').prop(\"value\", true);\n str.get_string('standard_period', 'block_evasys_sync').done(function (s) {\n $('#evaluationperiod').text(s);\n });\n disable();\n }\n });\n }\n };\n\n var disable = function() {\n $('[name=minute_start]')[0].disabled = true;\n $('[name=hour_start]')[0].disabled = true;\n $('[name=day_start]')[0].disabled = true;\n $('[name=month_start]')[0].disabled = true;\n $('[name=year_start]')[0].disabled = true;\n $('[name=minute_end]')[0].disabled = true;\n $('[name=hour_end]')[0].disabled = true;\n $('[name=day_end]')[0].disabled = true;\n $('[name=month_end]')[0].disabled = true;\n $('[name=year_end]')[0].disabled = true;\n $('#only_end').val(false);\n };\n\n var enable = function() {\n $('[name=minute_start]')[0].disabled = false;\n $('[name=hour_start]')[0].disabled = false;\n $('[name=day_start]')[0].disabled = false;\n $('[name=month_start]')[0].disabled = false;\n $('[name=year_start]')[0].disabled = false;\n $('[name=minute_end]')[0].disabled = false;\n $('[name=hour_end]')[0].disabled = false;\n $('[name=day_end]')[0].disabled = false;\n $('[name=month_end]')[0].disabled = false;\n $('[name=year_end]')[0].disabled = false;\n };\n\n return {\n init: init\n };\n});"],"file":"standardtime.min.js"}
\ No newline at end of file
diff --git a/amd/src/post_dialog.js b/amd/src/post_dialog.js
index ef3012a..b79362f 100644
--- a/amd/src/post_dialog.js
+++ b/amd/src/post_dialog.js
@@ -7,7 +7,7 @@ define(['jquery', 'core/notification', 'core/str'], function($, notification, st
{'key' : 'confirm_box', component: 'block_evasys_sync'},
]).done(function(s) {
notification.alert(s[0], s[1], s[2]);
- }
+ }
).fail(notification.exception);
};
@@ -18,7 +18,7 @@ define(['jquery', 'core/notification', 'core/str'], function($, notification, st
{'key' : 'confirm_box', component: 'block_evasys_sync'},
]).done(function(s) {
notification.alert(s[0], s[1], s[2]);
- }
+ }
).fail(notification.exception);
};
@@ -29,7 +29,7 @@ define(['jquery', 'core/notification', 'core/str'], function($, notification, st
{'key' : 'confirm_box', component: 'block_evasys_sync'},
]).done(function(s) {
notification.alert(s[0], s[1], s[2]);
- }
+ }
).fail(notification.exception);
};
@@ -40,7 +40,7 @@ define(['jquery', 'core/notification', 'core/str'], function($, notification, st
{'key' : 'confirm_box', component: 'block_evasys_sync'},
]).done(function(s) {
notification.alert(s[0], s[1], s[2]);
- }
+ }
).fail(notification.exception);
};
diff --git a/block_evasys_sync.php b/block_evasys_sync.php
index b323edc..9be99b0 100644
--- a/block_evasys_sync.php
+++ b/block_evasys_sync.php
@@ -34,6 +34,7 @@ public function init() {
*/
public function get_content() {
global $OUTPUT;
+
$evasyssynccheck = optional_param('evasyssynccheck', 0, PARAM_BOOL);
$status = optional_param('status', "", PARAM_TEXT);
@@ -60,7 +61,7 @@ public function get_content() {
// If we are not in sync mode, we display either the course mapping or the check status button.
if ($evasyssynccheck !== 1) {
- $inlsf = !empty($this->page->course->idnumber);
+ $inlsf = (!empty($this->page->course->idnumber) or $this->page->course->idnumber === '0');
$hasextras = \block_evasys_sync\course_evasys_courses_allocation::record_exists_select(
"course = {$this->page->course->id} AND NOT evasyscourses = ''");
@@ -82,13 +83,13 @@ public function get_content() {
if ($ismodeautomated) {
$this->page->requires->js_call_amd('block_evasys_sync/invite_manager', 'init');
} else {
- $categoryhasstandardtime = \block_evasys_sync\evasys_synchronizer::get_standard_timemode($this->page->course->category);
// Only use standardtime js if no record exists.
if (!$record) {
$this->page->requires->js_call_amd('block_evasys_sync/standardtime', 'init');
}
}
+ $categoryhasstandardtime = \block_evasys_sync\evasys_synchronizer::get_standard_timemode($this->page->course->category);
$evasyssynchronizer = new \block_evasys_sync\evasys_synchronizer($this->page->course->id);
try {
$evasyscourses = $evasyssynchronizer->get_courses_from_lsf();
@@ -97,7 +98,6 @@ public function get_content() {
$this->content->text .= html_writer::div(get_string('syncnotpossible', 'block_evasys_sync'));
return $this->content;
}
-
if ($ismodeautomated) {
$href = new moodle_url('/course/view.php',
array('id' => $this->page->course->id, "evasyssynccheck" => true));
@@ -110,6 +110,7 @@ public function get_content() {
$enddisabled = false;
$emailsentnotice = false;
$periodsetnotice = false;
+ $wasstarted = false;
// Set start to today and end to a week from now.
$start = time();
@@ -134,6 +135,8 @@ public function get_content() {
// If the persistenceclass exists and the state is automatic and not opened
// the period must have been set.
$periodsetnotice = true;
+ } else {
+ $wasstarted = true;
}
if ($state >= course_evaluation_allocation::STATE_AUTO_OPENED || $nostudents) {
// If the course was already opened, disable the start date. If there are no students disable all controls.
@@ -149,7 +152,7 @@ public function get_content() {
$end = $record->get('enddate');
$recordhasstandardtime = $record->get('usestandardtime');
} else {
- if (!$ismodeautomated && $categoryhasstandardtime) {
+ if ($categoryhasstandardtime) {
$start = $categoryhasstandardtime['start'];
$end = $categoryhasstandardtime['end'];
$recordhasstandardtime = true;
@@ -161,15 +164,27 @@ public function get_content() {
$this->page->requires->js_call_amd('block_evasys_sync/initialize', 'init', array($start, $end, $jsmodestring));
// Initialize variables to pass to mustache.
+ $unknownidnumbercourse = false;
+ $unknownextracourse = false;
$courses = array();
$hassurveys = false;
$startoption = ($startdisabled xor $enddisabled);
- $warning = false;
+ $warningnotallclosed = false;
+ $warningnotallopen = false;
$invalidcourses = false;
// Query course data (put in function).
foreach ($evasyscourses as $evasyscourseinfo) {
$course = array();
$course['evasyscoursetitle'] = $evasyssynchronizer->get_course_name($evasyscourseinfo['id']);
+ if ($course['evasyscoursetitle'] == 'Unknown') {
+ $couseidnum = $this->page->course->idnumber;
+ if ($evasyscourseinfo['lsf_id'] == $couseidnum) {
+ $unknownidnumbercourse = true;
+ } else {
+ $unknownextracourse = true;
+ }
+ continue;
+ }
$course['technicalid'] = $evasyssynchronizer->get_course_id($evasyscourseinfo['id']);
$course['evasyscourseid'] = $evasyscourseinfo['id'];
$course['c_participants'] = format_string($evasyssynchronizer->get_amount_participants($evasyscourseinfo['id']));
@@ -184,12 +199,18 @@ public function get_content() {
$rawsurvey->surveyStatus == 'open') {
// If the evaluation has ended as far as we know, but there are still open evaluations output a warning...
// and enable all controls.
- $warning = true;
+ $warningnotallclosed = true;
$startdisabled = false;
$enddisabled = false;
}
+ if (($record !== false && ($record->get('state') == course_evaluation_allocation::STATE_AUTO_NOTOPENED
+ || $record->get('state') == course_evaluation_allocation::STATE_AUTO_OPENED)
+ && $rawsurvey->surveyStatus == 'closed')
+ or ($ismodeautomated and $record === false and $rawsurvey->surveyStatus == 'closed')) {
+ $warningnotallopen = true;
+ }
if (($record === false || $record->get('state') == course_evaluation_allocation::STATE_MANUAL) &&
- $rawsurvey->surveyStatus == 'closed') {
+ $rawsurvey->surveyStatus == 'closed' && !$ismodeautomated) {
// In case of a manual evaluation get the status of the evaluation by...
// checking whether the evaluations are closed.
$emailsentnotice = false;
@@ -211,9 +232,7 @@ public function get_content() {
// Append this course.
$courses[] = $course;
}
-
- $standardttimemode = (!$ismodeautomated && $recordhasstandardtime && !$record);
-
+ $standardttimemode = ($recordhasstandardtime && !$wasstarted);
// Create the data object for the mustache table.
$data = array(
'href' => $href,
@@ -224,7 +243,7 @@ public function get_content() {
* In case of the automated workflow, we require surveys
* in order to be able to automatically trigger the evaluation. */
'showcontrols' => ($hassurveys || !$ismodeautomated) && count($evasyscourses) > 0 && !$invalidcourses,
- 'usestandardtimelayout' => (!$ismodeautomated && $recordhasstandardtime && !$record),
+ 'usestandardtimelayout' => $standardttimemode,
// Choose mode.
'direct' => $ismodeautomated,
'startdisabled' => $startdisabled || $standardttimemode,
@@ -241,9 +260,15 @@ public function get_content() {
// Defines if an lsf course is already mapped to the moodle course.
'optional' => !empty($evasyscourses),
// Outputs a warning that there are open course when there shouldn't.
- 'warning' => $warning
+ 'warningnotallclosed' => $warningnotallclosed,
+ 'warningnotallopen' => $warningnotallopen,
+ 'invalididnumberwarning' => $unknownidnumbercourse,
+ 'invalidextrawarning' => $unknownextracourse,
+ // If there is a internal state that is reserved for auto/manual mode, but the mode doesn't match warn the user.
+ 'inconsistentmodeswarning' => isset($state) ?
+ ($state == course_evaluation_allocation::STATE_MANUAL and $ismodeautomated)
+ or ($state < course_evaluation_allocation::STATE_MANUAL and !$ismodeautomated) : false
);
-
$this->content->text .= $OUTPUT->render_from_template("block_evasys_sync/block", $data);
$this->content->footer = '';
return $this->content;
diff --git a/classes/course_evasys_courses_allocation.php b/classes/course_evasys_courses_allocation.php
index 5e688d5..8a6f6ca 100644
--- a/classes/course_evasys_courses_allocation.php
+++ b/classes/course_evasys_courses_allocation.php
@@ -66,4 +66,16 @@ public static function raw_get_evasyscourses($courseid) {
array_unique($coursearray);
return $coursearray;
}
+
+ public static function get_record_by_course(int $course, $exception = true) {
+ global $DB;
+ if (!$record = $DB->get_record(self::TABLE, array('course' => $course))) {
+ if (!$exception) {
+ return false;
+ } else {
+ throw new \dml_missing_record_exception(self::TABLE);
+ }
+ }
+ return new static(0, $record);
+ }
}
\ No newline at end of file
diff --git a/classes/evasys_api.php b/classes/evasys_api.php
new file mode 100644
index 0000000..f82f2b6
--- /dev/null
+++ b/classes/evasys_api.php
@@ -0,0 +1,329 @@
+.
+/**
+ * Created by PhpStorm.
+ * User: robintschudi
+ * Date: 08.11.19
+ * Time: 15:56
+ */
+
+namespace block_evasys_sync;
+
+defined('MOODLE_INTERNAL') || die();
+
+class evasys_api {
+
+ private $soapclient;
+ private static $instance;
+
+ private function __construct() {
+ $this->soapclient = $this->init_soap_client();
+ }
+
+ public static function get_instance() {
+ if (defined("BEHAT_SITE_RUNNING")) {
+ return evasys_api_testable::get_instance();
+ }
+ if (!self::$instance) {
+ self::$instance = new evasys_api();
+ }
+ return self::$instance;
+ }
+
+ public function get_course($evasyskennung) {
+ $soapresult = $this->soapclient->GetCourse($evasyskennung, 'PUBLIC', true, false);
+ return $soapresult;
+ }
+
+ public function send_invitation_to_participants_of_survey($surveyid) {
+ $this->soapclient->SendInvitationToParticipants($surveyid);
+ }
+
+ public function insert_participants($personlist, $evasyscourse) {
+ $soapresult = $this->soapclient->InsertParticipants($personlist, $evasyscourse, 'PUBLIC', false);
+ return $soapresult;
+ }
+
+ public function create_passwords($evasysid, $pwcount) {
+ $this->soapclient->GetPswdsBySurvey($evasysid, $pwcount, 1, true, false);
+ }
+
+ public function insert_close_task($task) {
+ return $this->soapclient->InsertCloseTask($task);
+ }
+
+ public function close_survey($surveyid) {
+ return $this->soapclient->CloseSurvey($surveyid);
+ }
+
+ public function get_form($formid) {
+ $soapresult = $this->soapclient->GetForm($formid, 'INTERNAL', false);
+ return $soapresult;
+ }
+
+ private function init_soap_client() {
+ $soapclient = new \SoapClient(get_config('block_evasys_sync', 'evasys_wsdl_url'), [
+ 'trace' => 1,
+ 'exceptions' => 0,
+ 'location' => get_config('block_evasys_sync', 'evasys_soap_url')
+ ]);
+
+ $headerbody = new \SoapVar([
+ new \SoapVar(get_config('block_evasys_sync', 'evasys_username'),
+ XSD_STRING, null, null, 'Login', null),
+ new \SoapVar(get_config('block_evasys_sync', 'evasys_password'),
+ XSD_STRING, null, null, 'Password', null),
+ ], SOAP_ENC_OBJECT);
+ $header = new \SOAPHEADER('soap', 'Header', $headerbody);
+ $soapclient->__setSoapHeaders($header);
+ return $soapclient;
+ }
+
+}
+
+class evasys_api_testable extends evasys_api {
+ private static $instance;
+
+ private function __construct() {
+ }
+
+ public static function get_instance() {
+ if (!self::$instance) {
+ self::$instance = new evasys_api_testable();
+ }
+ return self::$instance;
+ }
+
+ /* Format: (var dump of SINGLE! survey evasys course)
+ object(stdClass)[402]
+ public 'm_nCourseId' => int 166410
+ public 'm_sProgramOfStudy' => string '' (length=0)
+ public 'm_sCourseTitle' => string 'AutoMultiSurvey' (length=15)
+ public 'm_sRoom' => string '' (length=0)
+ public 'm_nCourseType' => int 1
+ public 'm_sPubCourseId' => string '1002 WS 2018/19' (length=15)
+ public 'm_sExternalId' => string '' (length=0)
+ public 'm_nCountStud' => int 2
+ public 'm_sCustomFieldsJSON' => string '{}' (length=2)
+ public 'm_nUserId' => int 73350
+ public 'm_nFbid' => int 338
+ public 'm_nPeriodId' => int 40
+ public 'm_aoParticipants' =>
+ object(stdClass)[404]
+ public 'm_aoSecondaryInstructors' =>
+ object(stdClass)[401]
+ public 'm_oSurveyHolder' =>
+ object(stdClass)[411]
+ public 'm_aSurveys' =>
+ object(stdClass)[412]
+ public 'Surveys' =>
+ object(stdClass)[413]
+ public 'm_nSurveyId' => int 330416933
+ public 'm_nState' => int 0
+ public 'm_sTitle' => string 'AutoMultiSurvey' (length=15)
+ public 'm_cType' => string 'o' (length=1)
+ public 'm_nFrmid' => int 832
+ public 'm_nStuid' => int 34763
+ public 'm_nVerid' => int 166410
+ public 'm_nOpenState' => int 1
+ public 'm_nFormCount' => int 0
+ public 'm_nPswdCount' => int 2
+ public 'm_sLastDataCollectionDate' => string '' (length=0)
+ public 'm_nPageLinkOffset' => int 0
+ public 'm_sMaskTan' => string '' (length=0)
+ public 'm_nMaskState' => int 0
+ public 'm_oPeriod' =>
+ object(stdClass)[414]
+ public 'm_nPeriodId' => int 40
+ public 'm_sTitel' => string 'WS 2018/19' (length=10)
+ public 'm_sStartDate' => string '2018-10-01' (length=10)
+ public 'm_sEndDate' => string '2019-03-31' (length=10)
+ */
+ /*
+ * Single survey:
+ public 'm_aSurveys' =>
+ object(stdClass)[412]
+ public 'Surveys' =>
+ object(stdClass)[413]
+ public 'm_nSurveyId' => int 330416933
+ public 'm_nState' => int 0
+ public 'm_sTitle' => string 'AutoMultiSurvey' (length=15)
+ public 'm_cType' => string 'o' (length=1)
+ public 'm_nFrmid' => int 832
+ public 'm_nStuid' => int 34763
+ public 'm_nVerid' => int 166410
+ public 'm_nOpenState' => int 1
+ public 'm_nFormCount' => int 0
+ public 'm_nPswdCount' => int 2
+ public 'm_sLastDataCollectionDate' => string '' (length=0)
+ public 'm_nPageLinkOffset' => int 0
+ public 'm_sMaskTan' => string '' (length=0)
+ public 'm_nMaskState' => int 0
+ public 'm_oPeriod' =>
+ object(stdClass)[414]
+ public 'm_nPeriodId' => int 40
+ public 'm_sTitel' => string 'WS 2018/19' (length=10)
+ public 'm_sStartDate' => string '2018-10-01' (length=10)
+ public 'm_sEndDate' => string '2019-03-31' (length=10)
+ Multi survey:
+ public 'm_aSurveys' =>
+ object(stdClass)[412]
+ public 'Surveys' =>
+ array (size=2)
+ 0 =>
+ object(stdClass)[409]
+ public 'm_nSurveyId' => int 330416933
+ public 'm_nState' => int 0
+ public 'm_sTitle' => string 'AutoMultiSurvey' (length=15)
+ public 'm_cType' => string 'o' (length=1)
+ public 'm_nFrmid' => int 832
+ public 'm_nStuid' => int 34763
+ public 'm_nVerid' => int 166410
+ public 'm_nOpenState' => int 1
+ public 'm_nFormCount' => int 0
+ public 'm_nPswdCount' => int 2
+ public 'm_sLastDataCollectionDate' => string '' (length=0)
+ public 'm_nPageLinkOffset' => int 0
+ public 'm_sMaskTan' => string '' (length=0)
+ public 'm_nMaskState' => int 0
+ public 'm_oPeriod' =>
+ object(stdClass)[410]
+ ...
+ 1 =>
+ object(stdClass)[411]
+ public 'm_nSurveyId' => int 2114887341
+ public 'm_nState' => int 0
+ public 'm_sTitle' => string 'AutoMultiSurvey' (length=15)
+ public 'm_cType' => string 'o' (length=1)
+ public 'm_nFrmid' => int 784
+ public 'm_nStuid' => int 34763
+ public 'm_nVerid' => int 166410
+ public 'm_nOpenState' => int 1
+ public 'm_nFormCount' => int 0
+ public 'm_nPswdCount' => int 2
+ public 'm_sLastDataCollectionDate' => string '' (length=0)
+ public 'm_nPageLinkOffset' => int 0
+ public 'm_sMaskTan' => string '' (length=0)
+ public 'm_nMaskState' => int 0
+ public 'm_oPeriod' =>
+ object(stdClass)[410]
+ */
+
+ public function get_course($evasyskennung) {
+ // Get data into the structure that would have been returned by the evasys-api SOAP.
+ if (!$evasyskennung) {
+ throw new \SoapFault(101, "Testerror");
+ }
+ // If the lsf-course is invalid, it will return null on the details. This will result in the $evasyskennung
+ // being set to null . " " . null or " " so we need to check for " " here.
+ if (is_null($evasyskennung) or $evasyskennung == " ") {
+ $courseclass = new \stdClass();
+ $courseclass->m_nCourseId = 'Unknown';
+ $courseclass->m_sCourseTitle = 'Unknown';
+ return $courseclass;
+ }
+
+ global $COURSE;
+ $courseid = $COURSE->id;
+ $lsfid = \behat_block_evasys_sync::$evalsfkeyassoc[$evasyskennung];
+ $fullcoursedata = \behat_block_evasys_sync::get_coursedata_by_courseid($courseid)->evacourses;
+
+ // At the moment this check could be removed, since all cases of invalid links are treated the same,
+ // so having a working lsf course with a non working evasys course does not get tested.
+ // This check is here if anyone ever wants to output the debug details of where the conn fails and test that.
+ if (!isset($fullcoursedata[$lsfid]) or !$fullcoursedata[$lsfid]->valid) {
+ $courseclass = new \stdClass();
+ $courseclass->m_nCourseId = 'Unknown';
+ $courseclass->m_sCourseTitle = 'Unknown';
+ return $courseclass;
+ }
+ $coursedata = $fullcoursedata[$lsfid];
+
+ $courseclass = new \stdClass();
+ $courseclass->m_nCourseId = 1;
+ $courseclass->m_sCourseTitle = $coursedata->title;
+ $courseclass->m_Pub_CourseId = intval($coursedata->veranstnr);
+ $courseclass->m_nCountStud = intval($coursedata->studentcount);
+ $courseclass->m_aoParticipants = new \stdClass();
+ $courseclass->m_aoParticipants->Persons = array();
+ for ($i = 0; $i < $coursedata->studentcount; $i++) {
+ array_push($courseclass->m_aoParticipants->Persons, "I'm a person");
+ }
+ $surveyholder = new \stdClass();
+ $surveydata = $coursedata->surveys;
+ if (count($surveydata) == 1) {
+ // If it's just one survey it's an object.
+ $surveys = new \stdClass();
+ foreach ($surveydata as $singlesurveydata) {
+ $surveys->m_nSurveyId = intval($singlesurveydata->num);
+ $surveys->m_nState = 0;
+ $surveys->m_sTitle = "Survey" . $singlesurveydata->num;
+ $surveys->m_nFrmid = intval($singlesurveydata->formid);
+ $surveys->m_nOpenState = $singlesurveydata->is_open == 't' ? 1 : 0;
+ $surveys->m_nFormCount = intval($singlesurveydata->form_count);
+ $surveys->m_nPswdCount = intval($singlesurveydata->pswd_count);
+ }
+ } else {
+ // Otherwise it's an array. Best design ever.
+ $surveys = array();
+ foreach ($surveydata as $singlesurveydata) {
+ $survey = new \stdClass();
+ $survey->m_nSurveyId = intval($singlesurveydata->num);
+ $survey->m_nState = 0;
+ $survey->m_sTitle = "Survey" . $singlesurveydata->num;
+ $survey->m_nFrmid = intval($singlesurveydata->formid);
+ $survey->m_nOpenState = $singlesurveydata->is_open == 't' ? 1 : 0;
+ $survey->m_nFormCount = intval($singlesurveydata->form_count);
+ $survey->m_nPswdCount = intval($singlesurveydata->pswd_count);
+ array_push($surveys, $survey);
+ }
+ }
+ $surveyobject = new \stdClass();
+ $surveyobject->Surveys = $surveys;
+ $surveyholder->m_aSurveys = $surveyobject;
+ $courseclass->m_oSurveyHolder = $surveyholder;
+ return $courseclass;
+ }
+
+ public function send_invitation_to_participants_of_survey($surveyid) {
+ return true;
+ }
+
+ public function insert_participants($personlist, $evasyscourse) {
+ return true;
+ }
+
+ public function create_passwords($evasysid, $pwcount) {
+ return true;
+ }
+
+ public function insert_close_task($task) {
+ return true;
+ }
+
+ public function close_survey($surveyid) {
+ return true;
+ }
+
+
+ public function get_form($formid) {
+ $formclass = new \stdClass();
+ $formclass->FormId = 1;
+ $formclass->FormName = "A Form";
+ $formclass->FormTitle = "A form title";
+ return $formclass;
+ }
+}
diff --git a/classes/evasys_inviter.php b/classes/evasys_inviter.php
index 6aa1495..d8b2021 100644
--- a/classes/evasys_inviter.php
+++ b/classes/evasys_inviter.php
@@ -17,7 +17,11 @@
namespace block_evasys_sync;
defined('MOODLE_INTERNAL') || die();
-require_once($CFG->dirroot . "/local/lsf_unification/lib_his.php");
+if (!defined('BEHAT_SITE_RUNNING')) {
+ require_once($CFG->dirroot . "/local/lsf_unification/lib_his.php");
+} else {
+ require_once($CFG->dirroot . "/blocks/evasys_sync/classes/lsf_api_mock_testable.php");
+}
require_once($CFG->libdir . '/adminlib.php');
require_once($CFG->dirroot . '/course/lib.php');
/**
@@ -29,10 +33,10 @@
class evasys_inviter {
public static $instance = null;
- private $soapclient;
+ private $evasysapi;
private function __construct() {
- $this->soapclient = $this->init_soap_client();
+ $this->evasysapi = evasys_api::get_instance();
}
public static function get_instance() {
@@ -85,7 +89,7 @@ public static function get_evasysids($courseid) {
* @return array IDs of surveys
*/
public function get_evasys_course_surveys($evasyskennung, $all = true) {
- $soapresult = $this->soapclient->GetCourse($evasyskennung, 'PUBLIC', true, false);
+ $soapresult = $this->evasysapi->get_course($evasyskennung);
$surveyids = $soapresult->m_oSurveyHolder->m_aSurveys;
if (is_soap_fault($soapresult)) {
return array();
@@ -156,7 +160,7 @@ public function open_evasys_surveys($courses) {
$surveyids = array_unique($surveyids);
foreach ($surveyids as $surveyid) {
- $this->soapclient->SendInvitationToParticipants($surveyid);
+ $this->evasysapi->send_invitation_to_participants_of_survey($surveyid);
}
}
@@ -236,7 +240,7 @@ public function push_users_in_moodlecourse($moodlecourse) {
$soapresult = false;
foreach ($evasyscourses as $evasyscourse) {
- $soapresult = $this->soapclient->InsertParticipants($personlist, $evasyscourse, 'PUBLIC', false);
+ $soapresult = $this->evasysapi->insert_participants($personlist, $evasyscourse);
if (!is_soap_fault($soapresult) && $soapresult) {
// Create enough passwords, then try again.
$this->make_sure_enough_passwords_are_available($evasyscourse);
@@ -248,12 +252,12 @@ public function push_users_in_moodlecourse($moodlecourse) {
}
public function make_sure_enough_passwords_are_available($evasyscourseid) {
- $evasyscourse = $this->soapclient->GetCourse($evasyscourseid, 'PUBLIC' , false, false);
+ $evasyscourse = $this->evasysapi->get_course($evasyscourseid);
if (!is_soap_fault($evasyscourse)) {
$usercount = $evasyscourse->m_nCountStud;
$surveys = $this->get_evasys_course_surveys($evasyscourseid);
foreach ($surveys as $survey) {
- $this->soapclient->GetPswdsBySurvey((string)$survey->m_nSurveyId, $usercount, 1, true, false);
+ $this->evasysapi->create_passwords((string)$survey->m_nSurveyId, $usercount);
}
}
}
@@ -270,22 +274,6 @@ public static function get_enrolled_student_email_adresses_from_usernames($moodl
return $emailadresses;
}
- public function init_soap_client() {
- $soapclient = new \SoapClient(get_config('block_evasys_sync', 'evasys_wsdl_url'), [
- 'trace' => 1,
- 'exceptions' => 0,
- 'location' => get_config('block_evasys_sync', 'evasys_soap_url')
- ]);
-
- $headerbody = new \SoapVar([
- new \SoapVar(get_config('block_evasys_sync', 'evasys_username'), XSD_STRING, null, null, 'Login', null),
- new \SoapVar(get_config('block_evasys_sync', 'evasys_password'), XSD_STRING, null, null, 'Password', null),
- ], SOAP_ENC_OBJECT);
- $header = new \SOAPHEADER('soap', 'Header', $headerbody);
- $soapclient->__setSoapHeaders($header);
- return $soapclient;
- }
-
public function set_close_task($surveyid) {
$time = new \DateTime();
$time->add(new \DateInterval("PT10S"));
@@ -294,13 +282,13 @@ public function set_close_task($surveyid) {
$task->StartTime = $time->format("Y-m-d\TH:i:s");
$task->Status = 3;
$task->SendReport = false;
- $soapresult = $this->soapclient->InsertCloseTask($task);
+ $soapresult = $this->evasysapi->insert_close_task($task);
if (is_soap_fault($soapresult)) {
// The Close Task has already been executed. This can happen if the Survey was reopened for some Reason.
// Because of this, we'll still close the survey, however we can't send the result mail.
// Also there might be another Error. In any Case we want to make sure the Survey gets closed.
// If this was called by the close survey method the evaluationkoordinator will be notified.
- $this->soapclient->CloseSurvey($surveyid);
+ $this->evasysapi->close_survey($surveyid);
return false;
}
return true;
diff --git a/classes/evasys_synchronizer.php b/classes/evasys_synchronizer.php
index 085c0e3..1218691 100755
--- a/classes/evasys_synchronizer.php
+++ b/classes/evasys_synchronizer.php
@@ -18,20 +18,24 @@
defined('MOODLE_INTERNAL') || die();
-require_once($CFG->dirroot . "/local/lsf_unification/lib_his.php");
+if (!defined('BEHAT_SITE_RUNNING')) {
+ require_once($CFG->dirroot . "/local/lsf_unification/lib_his.php");
+} else {
+ require_once($CFG->dirroot . "/blocks/evasys_sync/classes/lsf_api_mock_testable.php");
+}
require_once($CFG->libdir . '/adminlib.php');
require_once($CFG->dirroot . '/course/lib.php');
class evasys_synchronizer {
private $courseid;
- protected $soapclient;
private $blockcontext;
private $courseinformation;
private $lsfcourses;
+ private $evasysapi;
public function __construct($courseid) {
$this->courseid = $courseid;
- $this->init_soap_client();
+ $this->evasysapi = evasys_api::get_instance();
$this->blockcontext = \context_course::instance($courseid); // TODO Course context or block context? Check caps.
$this->courseinformation = $this->get_course_information();
}
@@ -42,18 +46,6 @@ public function get_courses_from_lsf() {
return $this->lsfcourses;
}
$course = get_course($this->courseid);
-
- // Fetch veranstnr from LSF view.
- if ($course->idnumber) {
- establish_secondary_DB_connection();
- $lsfentry = get_course_by_veranstid(intval($course->idnumber));
- close_secondary_DB_connection();
-
- if (!is_object($lsfentry)) {
- throw new \Exception('Cannot sync: Connection to LSF could not be established. Please try again later.');
- }
- $maincourse = trim($lsfentry->veranstid);
- }
// Fetch persistent object id.
$pid = $DB->get_field('block_evasys_sync_courses', 'id', array('course' => $this->courseid));
// Get all associated courses.
@@ -64,45 +56,30 @@ public function get_courses_from_lsf() {
$extras = [];
}
// If noone has associated the course itself, we force that.
- if (isset($maincourse) && !empty($maincourse)) {
- if (!in_array($maincourse, $extras)) {
- $extras[] = $maincourse;
+ if (!(is_null($course->idnumber)) && !($course->idnumber == '')) {
+ if (!in_array($course->idnumber, $extras)) {
+ $extras[] = $course->idnumber;
}
}
- $extras = array_filter($extras);
establish_secondary_DB_connection();
// Fetch metadata (id, title) for the courses.
$result = array();
- foreach ($extras as $course) {
- $courseinfo = get_course_by_veranstid(intval($course));
+ foreach ($extras as $lsfcourse) {
+ $courseinfo = get_course_by_veranstid(intval($lsfcourse));
$result[] = array(
'title' => $courseinfo->titel,
- 'id' => trim($courseinfo->veranstnr) . ' ' . trim($courseinfo->semestertxt));
+ 'id' => trim($courseinfo->veranstnr) . ' ' . trim($courseinfo->semestertxt),
+ 'lsf_id' => $lsfcourse);
}
close_secondary_DB_connection();
$this->lsfcourses = $result;
return $this->lsfcourses;
}
- private function init_soap_client() {
- $this->soapclient = new \SoapClient(get_config('block_evasys_sync', 'evasys_wsdl_url'), [
- 'trace' => 1,
- 'exceptions' => 0,
- 'location' => get_config('block_evasys_sync', 'evasys_soap_url')
- ]);
-
- $headerbody = new \SoapVar([
- new \SoapVar(get_config('block_evasys_sync', 'evasys_username'), XSD_STRING, null, null, 'Login', null),
- new \SoapVar(get_config('block_evasys_sync', 'evasys_password'), XSD_STRING, null, null, 'Password', null),
- ], SOAP_ENC_OBJECT);
- $header = new \SOAPHEADER('soap', 'Header', $headerbody);
- $this->soapclient->__setSoapHeaders($header);
- }
-
private function get_course_information() {
$result = [];
foreach ($this->get_courses_from_lsf() as $course) {
- $soapresult = $this->soapclient->GetCourse($course['id'], 'PUBLIC', true, true);
+ $soapresult = $this->evasysapi->get_course($course['id']);
if (is_soap_fault($soapresult)) {
// This happens e.g. if there is no corresponding course in EvaSys.
return null;
@@ -142,15 +119,6 @@ public function get_surveys($courseid) {
return $enrichedsurveys;
}
- public function get_all_surveys() {
- // Gets all surveys from the associated evasys courses.
- $surveys = [];
- foreach ($this->lsfcourses as $course) {
- $surveys = array_merge($surveys, $this->get_surveys($course['id']));
- }
- return $surveys;
- }
-
public function get_course_name($coursekey) {
if (isset($this->courseinformation[$coursekey])) {
return $this->courseinformation[$coursekey]->m_sCourseTitle;
@@ -178,10 +146,6 @@ private function enrich_survey($rawsurvey) {
$enrichedsurvey->formName = $this->get_form_name($rawsurvey->m_nFrmid);
$enrichedsurvey->formIdPub = $this->get_public_formid($rawsurvey->m_nFrmid);
$enrichedsurvey->formId = $rawsurvey->m_nFrmid;
- $start = $rawsurvey->m_oPeriod->m_sStartDate;
- $end = $rawsurvey->m_oPeriod->m_sEndDate;
- $enrichedsurvey->startDate = $start;
- $enrichedsurvey->endDate = $end;
return $enrichedsurvey;
}
@@ -194,13 +158,13 @@ private function get_survey_status($statusnumber) {
}
private function get_public_formid($formid) {
- $soapresult = $this->soapclient->GetForm($formid, 'INTERNAL', false);
+ $soapresult = $this->evasysapi->get_form($formid);
$formidpub = $soapresult->FormName;
return $formidpub;
}
private function get_form_name($formid) {
- $soapresult = $this->soapclient->GetForm($formid, 'INTERNAL', false);
+ $soapresult = $this->evasysapi->get_form($formid);
$formname = $soapresult->FormTitle;
return $formname;
}
@@ -252,19 +216,19 @@ public function sync_students() {
$personlist = new \SoapVar($students, SOAP_ENC_OBJECT, null, null, 'PersonList', null);
$this->courseinformation = $this->get_course_information();
foreach ($this->courseinformation as $course) {
- $soapresult = $this->soapclient->InsertParticipants($personlist, $course->m_sPubCourseId, 'PUBLIC', false);
- $course = $this->soapclient->GetCourse($course->m_sPubCourseId, 'PUBLIC', true, true); // Update usercount.
+ $soapresult = $this->evasysapi->insert_participants($personlist, $course->m_sPubCourseId);
+ $course = $this->evasysapi->get_course($course->m_sPubCourseId); // Update usercount.
$usercountnow = $course->m_nCountStud;
// The m_aSurveys element might be an empty object!
if (!empty((array) $course->m_oSurveyHolder->m_aSurveys)) {
if (is_array($course->m_oSurveyHolder->m_aSurveys->Surveys)) {
foreach ($course->m_oSurveyHolder->m_aSurveys->Surveys as $survey) {
$id = $survey->m_nSurveyId;
- $this->soapclient->GetPswdsBySurvey($id, $usercountnow, 1, true, false);
+ $this->evasysapi->create_passwords($id, $usercountnow);
}
} else {
$id = $course->m_oSurveyHolder->m_aSurveys->Surveys->m_nSurveyId;
- $this->soapclient->GetPswdsBySurvey($id, $usercountnow, 1, true, false); // Create new TAN's.
+ $this->evasysapi->create_passwords($id, $usercountnow); // Create new TAN's.
}
}
if (is_soap_fault($soapresult)) {
diff --git a/classes/lsf_api_mock_testable.php b/classes/lsf_api_mock_testable.php
new file mode 100644
index 0000000..9ab83c8
--- /dev/null
+++ b/classes/lsf_api_mock_testable.php
@@ -0,0 +1,72 @@
+.
+
+/**
+ * Created by PhpStorm.
+ * User: robintschudi
+ * Date: 08.11.19
+ * Time: 16:11
+ */
+
+namespace block_evasys_sync;
+
+use Behat\Behat\Tester\Exception\PendingException;
+use core_privacy\local\deprecated;
+use Matrix\Exception;
+
+defined('MOODLE_INTERNAL') || die();
+
+function establish_secondary_DB_connection() { // phpcs:ignore @codingStandardsIgnoreLine
+ return true;
+}
+
+function close_secondary_DB_connection() { // phpcs:ignore @codingStandardsIgnoreLine
+ return true;
+}
+
+function get_course_by_veranstid ($courseid) {
+ // Only return used values. fill the rest with dummys.
+ global $COURSE;
+ $moodleid = $COURSE->id;
+ require_once(__DIR__. './../tests/behat/behat_block_evasys_sync.php');
+ $fulldata = \behat_block_evasys_sync::get_coursedata_by_courseid($moodleid);
+ $coursedata = $fulldata->evacourses[$courseid];
+
+ $result = new \stdClass();
+ if (!$coursedata->valid) {
+ $result->veranstid = null;
+ $result->veranstnr = null;
+ $result->semester = null;
+ $result->semestertxt = null;
+ $result->veranstaltungsart = null;
+ $result->titel = null;
+ $result->urlveranst = null;
+ // If the coursedata is not valid we dont assign the evasyskey to lsfkey.
+ // First this could easily be overwritten because all invalid cousrses have evasyskey " ".
+ // Second we check for " " being the evasyskey in the evasysapi before ever accessing $coursedata->valid.
+ } else {
+ $result->veranstid = 1;
+ $result->veranstnr = $coursedata->veranstnr;
+ $result->semester = 1;
+ $result->semestertxt = $coursedata->semestertxt;
+ $result->veranstaltungsart = 1;
+ $result->titel = 1;
+ $result->urlveranst = 1;
+ // Associate evasyskey with lsfkey for easier reverse lookup.
+ \behat_block_evasys_sync::$evalsfkeyassoc[$coursedata->veranstnr . " " . $coursedata->semestertxt] = $courseid;
+ }
+ return $result;
+}
diff --git a/db/install.xml b/db/install.xml
index 67f166f..f274c73 100644
--- a/db/install.xml
+++ b/db/install.xml
@@ -28,6 +28,7 @@
+
diff --git a/generator.py b/generator.py
new file mode 100644
index 0000000..833acd5
--- /dev/null
+++ b/generator.py
@@ -0,0 +1,400 @@
+import copy
+from steps import *
+
+PATH = "../fulltest.feature"
+CONDENSE_TESTS = True
+# parameters
+category = 1
+coursename = "C1"
+tags = "@block @block_evasys_sync @block_evasys_sync_fulltest"
+# options
+OPTIONS = {
+ "automode": ["manual", "auto"],
+ "standardtimemode": [0, 1],
+ "students": ["none", "onlytutors", "multi"],
+ "idnumber": ["none", "invalid", "one"],
+ "mapped": ["none", "invalid", "one", "multi"],
+ "internalstate": ["notopened", "opened", "closed", "manual", "none"],
+ "actualstate": ["open", "closed", "mixed"],
+ "recordstandardtime": ["recordstandardtimemode", "recordnonstandardtimemode", "norecordstandardtimemode"]
+}
+
+ILLEGALSTATES = [
+ {"recordstandardtime": "recordstandardtimemode", "internalstate": "none"},
+ {"recordstandardtime": "recordnonstandardtimemode", "internalstate": "none"},
+ {"recordstandardtime": "norecordstandardtimemode", "internalstate": "notopened"},
+ {"recordstandardtime": "norecordstandardtimemode", "internalstate": "opened"},
+ {"recordstandardtime": "norecordstandardtimemode", "internalstate": "closed"},
+ {"recordstandardtime": "norecordstandardtimemode", "internalstate": "manual"},
+]
+
+
+def illegal_state(scenario):
+ for fullillegalstate in ILLEGALSTATES:
+ illegal = True
+ for illegalstate in fullillegalstate.keys():
+ if scenario[list(OPTIONS.keys()).index(illegalstate)] != fullillegalstate[illegalstate]:
+ illegal = False
+ break
+ if illegal:
+ return True
+ if scenario[5] == "none" and scenario[7] != "norecordstandardtimemode":
+ print(scenario)
+ return False
+
+
+def get_checks(mode, standardtime, students_state, idnumber_state, mapped_state, internal_state, actual_state, recordstandardtimemode):
+ """
+ This Function will take the scenario parameters and construct the resulting checks.
+ :return: The combined checks for the parameters.
+ """
+ if not recordstandardtimemode == "norecordstandardtimemode":
+ standardtime = 1 if recordstandardtimemode == "recordstandardtimemode" else 0
+ # If there are no mapped courses the Block not offer the option to "Show surveys"
+ if (idnumber_state == "none") and (mapped_state == "none"):
+ return "Then I should see \"Change mapping\"\n And I should not see \"Name:\"\n "
+
+ checks = ""
+ # In all other cases (even if there are only invalid entries) we need to click the button to start code execution
+ checks += "And I load the evasys block\n"
+
+ # First we want to check for idnumber and mapped checks. If there are invalid entries in either of these
+ # there should be a warning. However, it should not disable the ability to request a evaluation or set a timeframe
+ # in manual mode this was explicitly requested, and there might be an analog usecase in automatic mode.
+ checks += idnumber_checks[idnumber_state]
+ checks += mapped_checks[mapped_state]
+
+ no_valid_mappings = ((idnumber_state == "none" or idnumber_state == "invalid")
+ and (mapped_state == "none" or mapped_state == "invalid"))
+ # if there is an internal state that is reserved for manual or automode we want to check we want to output
+ # a warning if this is not in fact the correct mode
+ if ((internal_state != "manual" and internal_state != "none") and mode == "manual") or (
+ internal_state == "manual" and mode == "auto"):
+ checks += inconsistent_mode + "\n"
+ else:
+ # if the internal state isn't conflicting we want to check, if the block is correctly displaying for that state
+ checks += checks_internalstate(internal_state, actual_state, students_state, no_valid_mappings, mode, standardtime)
+ # finally if there is no entry yet or the planned date is in the future we want to check that
+ # there is an option to set the date for the evaluation
+ if (internal_state == "none" and (not no_valid_mappings or mode == "manual")) \
+ or (internal_state == "notopened" and mode == "auto" and not no_valid_mappings):
+ checks += automode_checks[mode] + "\n"
+ # If our own states don't match those of evasys we want to output a warning
+ if mode != "manual" and not no_valid_mappings:
+ # In manual mode we don't check for matching states because we have no way of knowing what the state should be.
+ if internal_state == "closed" and actual_state != "closed":
+ checks += "And I should see \"There are some open surveys, but all surveys should be closed.\"" + "\n"
+ elif internal_state != "closed" and internal_state != "manual" and actual_state != "open":
+ checks += "And I should see \"There are some closed surveys, but all surveys should be open.\"" + "\n"
+
+ # if the standardtimemodecheckbox should be present from the start we also want to check that
+ if not no_valid_mappings:
+ checks += checks_standardtimemode(standardtime, mode, internal_state, recordstandardtimemode) + "\n"
+ # if there are no students that are eligible to evaluate we want to output a warning
+ checks += student_checks[students_state]
+ checks = checks.replace("\n", "\n ")
+ return checks
+=======
+ checks = ""
+ if (idnumber_state == "none" or idnumber_state == "invalid") and (mapped_state == "none" or mapped_state == "invalid"):
+ if idnumber_state == "invalid" or mapped_state == "invalid":
+ return "And I press \"Show status of surveys\"\n" + "Then I should see \"Change mapping\"\nAnd I should not see \"Name:\"\n"
+ return "Then I should see \"Change mapping\"\nAnd I should not see \"Name:\"\n"
+
+
+ checks += "And I press \"Show status of surveys\"\n"
+ checks += idnumber_checks[idnumber_state]
+ checks += mapped_checks[mapped_state]
+ if ((internal_state != "manual" and internal_state != "none") and mode == "manual") or (internal_state == "manual" and mode == "auto"):
+ checks += inconsistent_mode + "\n"
+ else:
+ checks += internal_state_checks[internal_state]
+ if internal_state == "none" or (internal_state == "notopened" and mode == "auto"):
+ checks += automode_checks[mode] + "\n"
+
+ if internal_state == "closed" and actual_state != "closed":
+ checks += "And I should see \"There are some open surveys, but all surveys should be closed.\"" + "\n"
+ elif internal_state != "closed" and actual_state != "open":
+ checks += "And I should see \"Some of the surveys have been closed ahead of schedule!\"" + "\n"
+ checks += standardtimemode_checks[standardtime] + "\n"
+ checks += student_checks[students_state]
+ return checks
+>>>>>>> cc0cc54... corrected mistake
+
+
+def get_postcondensing_checks(idnumber_state, mapped_state):
+ """
+ :return: checks for a scenario that should NOT be considered in the condense step
+ """
+ postchecks = ""
+ # Check that surveys are actually shown.
+ # We don't want to include this in the sorting, because it only tests a for loop, but multiplies the tests by 2.
+ postchecks += postcheck_idnumber(idnumber_state)
+ postchecks += postcheck_mappedstate(mapped_state)
+
+ postchecks = postchecks.replace("\n", "\n ")
+ return postchecks
+
+
+def get_combi_sentence(set, dictionary, param_name):
+ """
+ This function takes a set of states for a scenario parameter and checks whether they all result in the same
+ description based on the provided dictionary. If they do it'll return this shared description.
+ Otherwise it'll return a description to reflect that the result should be the same for both options.
+ :param set: Set of states
+ :param dictionary: dictionary that holds the descriptions for the parametertype
+ :param param_name: name of the parameter
+ :return: String description for the set of parameters
+ """
+ set2 = set.copy()
+ last_response = dictionary[set.pop()]
+ while 1:
+ if len(set) == 0:
+ return last_response
+ if not last_response == dictionary[set.pop()]:
+ break
+ sentence = last_response
+ sentence += "This should be valid regardless of " + param_name + " being set to "
+ while len(set2) > 1:
+ sentence += str(set2.pop()) + ", "
+ sentence = sentence[:len(sentence) - 2]
+ sentence += " or " + str(set2.pop()) + "\n"
+ return sentence
+
+
+def get_combi_description(mode, standardtime, students_state, idnumber_state, mapped_state, internal_state,
+ actual_state, recordstandardtimemode):
+ """
+ This will construct the description for a scenario that covers multiple states.
+ :return: String the description of the scenario.
+ """
+ if "none" in mapped_state and "none" in idnumber_state:
+ return "If there are no related evasys-courses I should not see any.\n "
+ if not recordstandardtimemode == "norecordstandardtimemode":
+ standardtime = {1} if recordstandardtimemode == "recordstandardtimemode" else {0}
+ description = ""
+ description += get_combi_sentence(standardtime, standardtimemode_descriptions, "standardtime")
+ description += get_combi_sentence(students_state, students_descriptions, "number of students")
+ description += get_combi_sentence(idnumber_state, idnumber_descriptions, "idnumber")
+ description += get_combi_sentence(mapped_state, mapped_descriptions, "mapped courses")
+ description += description_modeconsistency(mode, internal_state, actual_state)
+ description = description.replace("\n", "\n ")
+ return description
+
+
+def get_scenario(mode, standardtime, students_state, idnumber_state, mapped_state, internal_state, actual_state, recordstandardtimemode):
+ """
+ This constructs the actual scenario by building all courses, states etc.
+ :return: string the combined enviroment specifiing string for a scenario
+ """
+ behat_scenario = step_manual_auto_mode[mode].replace("{{category}}", str(category)) + "\n"
+ behat_scenario += step_standardtime_mode[standardtime].replace("{{category}}", str(category)) + "\n"
+ behat_scenario += step_students_state[students_state] + "\n"
+ behat_scenario += step_idnumberstate(idnumber_state, actual_state)
+ behat_scenario += step_mappedstate(mapped_state, actual_state)
+ behat_scenario += step_internal_state[internal_state].replace("{{course}}", str(coursename)) + "\n"
+ behat_scenario += step_record_standardtimemode[recordstandardtimemode].replace("{{course}}", str(coursename)) + "\n"
+ behat_scenario += "And I turn editing mode on\n"
+ behat_scenario += "And I add the \"EvaSys Sync\" block\n"
+ behat_scenario += "And I turn editing mode off\n"
+ behat_scenario = behat_scenario.replace("\n", "\n ")
+
+ if actual_state == "mixed" and not ((idnumber_state == "one" and (mapped_state == "multi" or mapped_state == "one"))
+ or mapped_state == "multi"):
+ # a mixed state is impossible with less than 2 lsfscourseids, so we skip this.
+ return False
+ return behat_scenario
+
+
+def make_scenario(description, scenario, checks):
+ """
+ :return: Scenarion specified by its description, enviroment and checks
+ """
+ return " Scenario: " + description + scenario + checks + "\n"
+
+
+def count_full_scenarios(condesed_array):
+ """
+ Given an array of condenses scenarios (aka. scenarios with sets instead of single values) this function will
+ compute how many unique scenarios those condensed scenarios hold.
+ :param condesed_array: array of scenarios with sets of parameters
+ :return: number of unique scenarios covered by all condensed scenarios.
+ """
+ i = 0
+ for array in condesed_array:
+ j = 1
+ for param in array:
+ j *= len(param)
+ i += j
+ return i
+
+
+def array_diff(array1, array2):
+ """
+ :param array1: first array
+ :param array2: second array
+ :return: returns the number of missmatches between array1 and 2 aswell as the index of the last missmatch
+ """
+ diff = 0
+ position = 0
+ for i in range(0, len(array1)):
+ if not array1[i] == array2[i]:
+ position = i
+ diff += 1
+ return diff, position
+
+
+def condense_keep_options(fullarray):
+ """
+ Given an array of scenarios this function will combine these into new scenarios that cover the exact same range
+ of scenarios, but will combine them.
+ { off, off, on } and { on, off, on } will be combined to { on/off, off, on}
+ The array of scenarios passed should all have the same result ( checks )
+ :param fullarray: array of scenarios
+ :return: condensed array of scenarios
+ """
+ changed = True
+ for i in range(0, len(fullarray)):
+ for j in range(0, len(fullarray[i])):
+ fullarray[i][j] = set([fullarray[i][j]]) # Do not replace with literal!
+ while changed:
+ new_array = copy.deepcopy(fullarray)
+ changed = False
+ for array in fullarray:
+ for array2 in fullarray:
+ diffcount, index = array_diff(array, array2)
+ if diffcount == 1:
+ changed = True
+ if array2 in new_array:
+ new_array.remove(array2)
+ if array in new_array:
+ new_array.remove(array)
+ tmp = copy.deepcopy(array)
+ tmp[index] = array[index].union(array2[index])
+ new_array.append(tmp)
+ break
+ if changed:
+ break
+ fullarray = new_array
+
+ return fullarray
+
+
+def build_cartesian_product(array_dict):
+ cartesian_product = []
+ for key in array_dict:
+ if len(cartesian_product) == 0:
+ cartesian_product = [[option] for option in array_dict[key]]
+ continue
+ else:
+ cartesian_product = [old_options + [new_options]
+ for old_options in cartesian_product
+ for new_options in array_dict[key]]
+ return cartesian_product
+
+
+def main():
+ # open feature file
+ behattestfile = open(PATH, "w")
+ output = ""
+ i = 0
+ # initialize feature data
+ output += tags + "\n"
+ output += "Feature: " + feature_desc + "\n\n"
+ output += background + "\n\n"
+
+ # Build cartesian product of all options
+ cartesianoptions = build_cartesian_product(OPTIONS)
+
+ # get the checks for all options to be able to condense options with the same expected outcome
+ uncondensed_dict = {}
+ for scenario in cartesianoptions:
+ check = get_checks(scenario[0], scenario[1], scenario[2], scenario[3], scenario[4], scenario[5], scenario[6], scenario[7])
+ if not check in uncondensed_dict.keys():
+ uncondensed_dict[check] = []
+ uncondensed_dict[check].append(scenario)
+
+ # condense those scenarios
+ condensed_dict = {}
+ if CONDENSE_TESTS:
+ i = 0
+ for check in uncondensed_dict.keys():
+ scenarios = uncondensed_dict[check]
+ condensed_dict[check] = condense_keep_options(scenarios)
+ i += 1
+ print("Condensed " + str(i) + " of " + str(len(uncondensed_dict.keys())))
+ else:
+ for check in uncondensed_dict.keys():
+ fullarray = uncondensed_dict[check]
+ for i in range(0, len(fullarray)):
+ for j in range(0, len(fullarray[i])):
+ fullarray[i][j] = set([fullarray[i][j]])
+ condensed_dict[check] = fullarray
+
+ testcases = 0
+ endcases = 0
+ # check that the number of cases stayed the same
+ for check in condensed_dict.keys():
+ testcases += len(condensed_dict[check]) # get original number of cases
+ endcases += count_full_scenarios(condensed_dict[check]) # get number of cases that can be built from condesed scenarios
+
+ # finally build the actual behat-suite
+ for check in condensed_dict.keys():
+ for scenario in condensed_dict[check]:
+ # do a deep-copy of the scenario because sets can only be accessed by pop which alters the set itself
+ scenario_copy = copy.deepcopy(scenario)
+ desc = get_combi_description(scenario[0], scenario[1], scenario[2], scenario[3], scenario[4], scenario[5],
+ scenario[6], scenario[7])
+ mode = scenario_copy[0].pop()
+ standardtime = scenario_copy[1].pop()
+ students_state = scenario_copy[2].pop()
+ idnumber_state = scenario_copy[3].pop()
+ mapped_state = scenario_copy[4].pop()
+ internal_state = scenario_copy[5].pop()
+ actual_state = scenario_copy[6].pop()
+ recordstandardtime = scenario_copy[7].pop()
+ if actual_state == "mixed" and len(scenario_copy[6]) > 0:
+ actual_state = scenario_copy[6].pop()
+
+ corrected = True
+ illegal = False
+ illegals = []
+ while corrected:
+ corrected = False
+ if illegal_state([mode, standardtime, students_state, idnumber_state, mapped_state, internal_state,
+ actual_state, recordstandardtime]):
+ corrected = True
+ illegals.append([mode, standardtime, students_state, idnumber_state, mapped_state, internal_state,
+ actual_state, recordstandardtime])
+ if len(scenario_copy[5]) >= 1:
+ internal_state = scenario_copy[5].pop()
+ elif len(scenario_copy[7]) >= 1:
+ recordstandardtime = scenario_copy[7].pop()
+ else:
+ if len(illegals) > 1:
+ print("Illegal multiscenario")
+ print(illegals)
+ illegal = True
+ corrected = False
+ if illegal:
+ continue
+ scen_text = get_scenario(mode, standardtime, students_state, idnumber_state, mapped_state, internal_state,
+ actual_state, recordstandardtime)
+ if not scen_text:
+ print("Warning impossible scenario!!!")
+ continue
+ postcheck = get_postcondensing_checks(idnumber_state, mapped_state)
+ output += make_scenario(desc, scen_text, check + postcheck)
+
+ output = output.replace("\n \n", "\n\n") # remove whitespaces on empty lines
+ output = output[:len(output) - 1] # remove last newline
+ # write actual data
+ behattestfile.write(output)
+ behattestfile.close()
+ # output statistics
+ print("Startcases: %i, Endcases: %i" % (len(cartesianoptions), endcases))
+ print("Condensed %i scenarios to %i testcases with %i scenarios" % (len(cartesianoptions), len(condensed_dict), testcases))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/lang/de/block_evasys_sync.php b/lang/de/block_evasys_sync.php
index 10ad74f..b52c71e 100755
--- a/lang/de/block_evasys_sync.php
+++ b/lang/de/block_evasys_sync.php
@@ -47,6 +47,7 @@
$string['standard_period'] = "Standard-Evaluationszeitraum:";
$string['different_period'] = "Abweichender Evaluationszeitraum:";
$string['time_set'] = "Standard-Evaluationszeitraum gesetzt";
+$string['evasys_sync:modifymapping'] = "Zugeordnete Evasys-Kurse bearbeiten";
// Multi allocation strings.
@@ -65,7 +66,7 @@
$string['requestagain'] = 'Erneut einladen oder beauftragen';
$string['title_send_success'] = "Evaluation erfolgreich gestartet";
$string['content_send_success'] = 'Es wurden {$a->sent} von {$a->total} Einladungsmails versendet.
' .
- '{$a->queued} Evaluationsperioden wurden festgelegt.';
+ '{$a->queued} Evaluationsperioden wurden festgelegt.';
$string['title_send_failure'] = "Fehler beim Versand";
$string['send_error'] = "Es gab einen Fehler beim automatischen Versenden, bitte kontaktieren Sie Ihren Support, oder benutzen Sie den manuellen Versand von EvaSys";
$string['not_enough_dates'] = "Bitte geben Sie Daten für ALLE Umfragen an!";
@@ -118,15 +119,15 @@
$string['title_failure'] = "Evaluation nicht beauftragt";
$string['content_success'] = "Sie haben die Evaluation erfolgreich beantragt.
" .
- "!!!DIE EVALUATION HAT NOCH NICHT BEGONNEN!!!
" .
- "Sie müssen nichts weiter tun, ".
- "Ihr*e Evaluationsbeauftragte*r wird nach den Richtlinien Ihres Fachbereichs weiter verfahren.";
+ "!!!DIE EVALUATION HAT NOCH NICHT BEGONNEN!!!
" .
+ "Sie müssen nichts weiter tun, ".
+ "Ihr*e Evaluationsbeauftragte*r wird nach den Richtlinien Ihres Fachbereichs weiter verfahren.";
$string['content_uptodate'] = "Ihr*e Evaluationsbeauftragte*r hat bereist einen Auftrag zum Durchführen der Evaluation von Ihnen erhalten.
" .
- "Für Fragen zum Status Ihrer Evaluation kontaktieren Sie bitte Ihre*n Evaluationsbeauftragte*n.";
+ "Für Fragen zum Status Ihrer Evaluation kontaktieren Sie bitte Ihre*n Evaluationsbeauftragte*n.";
$string['content_failure'] = "Leider konnte die Evaluation nicht beauftragt werden.
" .
- "Bitte wenden Sie sich an den Support.";
+ "Bitte wenden Sie sich an den Support.";
$string['confirm_box'] = "Verstanden";
$string['content_confirm_reactivate_automated_closed'] = 'Sind Sie sicher, dass Sie die Evaluation erneut beginnen möchten?';
@@ -215,4 +216,9 @@
// Notices.
$string['evalperiodsetnotice'] = 'Evaluationszeitraum gesetzt';
-$string['emailsentnotice'] = 'Evaluation beauftragt';
\ No newline at end of file
+$string['emailsentnotice'] = 'Evaluation beauftragt';
+$string['warning_invalid_idnumber'] = "Der fest zugeordnete Evasys-kurs ist ungültig!";
+$string['warning_invalid_extra'] = "Einer der zugeordneten Evasys-kurse ist ungültig";
+$string['warning_not_all_closed'] = "Einige Umfragen sind geöffnet, aber alle Umfragen sollten geschlossen sein.";
+$string['warning_not_all_open'] = "Einige Umfragen sind geschlossen, aber alle Umfragen sollten offen sein.";
+$string['warning_inconsistent_modes'] = "Diese Evaluation wurde bereits in einem anderen Modus gestartet";
\ No newline at end of file
diff --git a/lang/en/block_evasys_sync.php b/lang/en/block_evasys_sync.php
index 03a097b..22780e1 100755
--- a/lang/en/block_evasys_sync.php
+++ b/lang/en/block_evasys_sync.php
@@ -46,6 +46,7 @@
$string['different_period'] = "Exceptional evaluationperiod:";
$string['activate_nonstandard_time'] = "Alter evaluationperiod for special courses";
$string['time_set'] = "Standard evaluationperiod set";
+$string['evasys_sync:modifymapping'] = "Map and unmap Evasys courses to moodle courses.";
// Multi allocation strings.
@@ -64,7 +65,7 @@
$string['requestagain'] = 'Request or invite again';
$string['title_send_success'] = "Evaluation started";
$string['content_send_success'] = '{$a->sent} of {$a->total} emails have been send.
'.
- '{$a->queued} jobs have been queued';
+ '{$a->queued} jobs have been queued';
$string['send_error'] = "There was an error while trying to send emails. Please contact Your local support, or send the Emails manually via EvaSys";
// Form strings.
@@ -103,7 +104,7 @@
$string['confirm_box'] = "OK";
$string['direct_already'] = "You have already sent invitations to all students.".
- "No new invitations have been send";
+ "No new invitations have been send";
$string['direct_title_info'] = "Invitation already complete";
$string['title_send_rejected'] = "Invalid Date";
@@ -225,3 +226,8 @@
// Notices.
$string['evalperiodsetnotice'] = 'Evaluationperiod has been set';
$string['emailsentnotice'] = 'Evaluation has been requested';
+$string['warning_invalid_idnumber'] = "The hard linked evasys-course is invalid!";
+$string['warning_invalid_extra'] = "One of the dynamically mapped evasys-courses is invalid";
+$string['warning_not_all_closed'] = "There are some open surveys, but all surveys should be closed.";
+$string['warning_not_all_open'] = "There are some closed surveys, but all surveys should be open.";
+$string['warning_inconsistent_modes'] = "This evaluation was already started in another mode";
\ No newline at end of file
diff --git a/templates/block.mustache b/templates/block.mustache
index f4f1677..dd84f64 100644
--- a/templates/block.mustache
+++ b/templates/block.mustache
@@ -40,6 +40,8 @@ Example context (json):
{{#onlyend}}{{/onlyend}}
{{#nostudents}}{{# str}}syncnostudents, block_evasys_sync{{/str}}{{/nostudents}}
+ {{#invalididnumberwarning}}{{# str}}warning_invalid_idnumber, block_evasys_sync{{/str}}
{{/invalididnumberwarning}}
+ {{#invalidextrawarning}}{{# str}}warning_invalid_extra, block_evasys_sync{{/str}}
{{/invalidextrawarning}}
{{#courses}}
{{#.}}
-{{#warning}}{{# str}}warning_inconsistent_states, block_evasys_sync{{/str}}
{{/warning}}
+{{#warningnotallopen}}{{# str}}warning_not_all_open, block_evasys_sync{{/str}}
{{/warningnotallopen}}
+{{#warningnotallclosed}}{{# str}}warning_not_all_closed, block_evasys_sync{{/str}}
{{/warningnotallclosed}}
+{{#inconsistentmodeswarning}}{{# str}}warning_inconsistent_modes, block_evasys_sync{{/str}}
{{/inconsistentmodeswarning}}
{{#emailsentnotice}}{{# str}}emailsentnotice, block_evasys_sync{{/str}}
{{/emailsentnotice}}
{{#evaluationperiodsetnotice}}{{# str}}evalperiodsetnotice, block_evasys_sync{{/str}}
{{/evaluationperiodsetnotice}}
{{#showcontrols}}
diff --git a/templates/coursemapping.mustache b/templates/coursemapping.mustache
index 24d60c6..6bd2af8 100644
--- a/templates/coursemapping.mustache
+++ b/templates/coursemapping.mustache
@@ -7,7 +7,7 @@ Example context (json):
}
}}
-