/**
  * From a moodle course_modinfo object, it creates an intuitel LO
  * @author elever
  * @see \intuitel\LOFactory::createLOFromNative()
  * @param \course_modinfo $rawData : moodle course_modinfo object
  * @return CourseLO $course : intuitelLO course object
  */
 function createLOFromNative($rawData)
 {
     $lmscourse = $rawData->get_course();
     $courseLoid = Intuitel::getIDFactory()->getLoIdfromId('course', $lmscourse->id);
     // course object is created, constructor sets the compulsory attributes: LOId, loName, hasPrecedingSib, hasParent; and the optional: hasFollowingSib
     $course = new CourseLO($courseLoid, $lmscourse->fullname, null, null);
     // optional attributes
     $courseLang = $this->getLang($rawData);
     $course->setLang($courseLang);
     $childrenLoId = $this->getChildren($rawData);
     $course->sethasChildren($childrenLoId);
     return $course;
 }
 /**
  * Compute a list of LOs for this LMS
  *
  * @param array $attributes
  *        	name=>value filter array
  */
 public function findLObyAttributes(array $attributes)
 {
     if (array_key_exists('loId', $attributes) && array_key_exists('getFullCourse', $attributes) && $attributes['getFullCourse'] == 'true') {
         // retrieve all objects in course
         $id = $attributes['loId'];
         $loId = $id instanceof LOId ? $id : new LOId($id);
         try {
             $adaptor = Intuitel::getAdaptorInstanceForCourseLOId($loId);
             $list = $adaptor->findLOAll();
             return $list;
         } catch (UnknownLOException $ex) {
             return array();
         } catch (UnknownIDException $ex) {
             return array();
         }
     } else {
         if (array_key_exists('loId', $attributes)) {
             // return one object
             $id = $attributes['loId'];
             $loId = $id instanceof LOId ? $id : new LOId($id);
             try {
                 $lo = IntuitelAdaptor::createLO($loId);
                 return array($lo);
             } catch (UnknownLOException $ex) {
                 return array();
             } catch (UnknownIDException $ex) {
                 return array();
             }
         } else {
             // search every course by params
             $courses = Intuitel::getIntuitelEnabledCourses();
             $total_list = array();
             foreach ($courses as $course) {
                 $filter = array('loId' => $course->loId, 'getFullCourse' => 'true');
                 $list = $this->findLObyAttributes($filter);
                 $total_list = array_merge($total_list, $list);
             }
             // filter out unwanted items
             $final_list = $this->filterUnmatched($total_list, $attributes);
             return $final_list;
         }
     }
 }
 /**
  * Get the list of INUITEL-enabled courses in the LMS
  * @return array(intuitel\CourseLO)
  */
 public static function getIntuitelEnabledCourses()
 {
     return Intuitel::getAdaptorInstanceForCourse(null)->getIntuitelEnabledCourses();
 }
 /**
  * (non-PHPdoc)
  * @see \intuitel\IntuitelAdaptor::getUseData()
  */
 public function getUseData($lo, $native_user_id)
 {
     $useData = array('completion' => null, 'grade' => null, 'grademax' => null, 'grademin' => null, 'accessed' => null, 'seenPercentage' => null);
     // if this is a section get access data (a section is seen once main screen of the course is seen)
     if (get_class($lo) == 'intuitel\\SectionLO') {
         $courseLOid = $lo->getParent();
         $courseid = Intuitel::getIDFactory()->getIdfromLoId($courseLOid);
         $useData['accessed'] = block_intuitel_get_access_status_course($courseid, $native_user_id);
     } else {
         if (get_class($lo) == 'intuitel\\CourseLO') {
             //the lo is the CourseLO, get the completion, accessed and grade data
             $loId = $lo->getloId();
             $lo_id = Intuitel::getIDFactory()->getIdfromLoId($loId);
             $useData['accessed'] = block_intuitel_get_access_status_course($lo_id, $native_user_id);
             $useData['completion'] = block_intuitel_get_completion_status_course($lo_id, $native_user_id);
             block_intuitel_get_grade_info_course($lo_id, $native_user_id);
         } else {
             //this is a module (activity or resource)
             $loId = $lo->getloId();
             $lo_id = Intuitel::getIDFactory()->getIdfromLoId($loId);
             $cm = get_coursemodule_from_id(null, $lo_id);
             $course_modinfo = get_fast_modinfo($cm->course);
             $coursemodule_info = $course_modinfo->get_cm($lo_id);
             //get the completion status from Moodle
             $useData['completion'] = block_intuitel_get_completion_status($coursemodule_info, $native_user_id);
             $useData['accessed'] = block_intuitel_get_access_status($lo_id, $native_user_id);
             $grade_info = block_intuitel_get_grade_info($coursemodule_info, $native_user_id);
             //grade_info contains info of the grade obtained as well as maximum and minimum grade of the module
             if ($grade_info != null) {
                 $useData['grade'] = $grade_info['grade'];
                 $useData['grademax'] = $grade_info['grademax'];
                 $useData['grademin'] = $grade_info['grademin'];
             }
             if ($lo->media == 'video' || $lo->media == 'audio') {
                 $viewed = block_intuitel_get_access_status($lo_id, $native_user_id);
                 if ($viewed) {
                     $useData['seenPercentage'] = 100;
                 } else {
                     $useData['seenPercentage'] = 0;
                 }
             }
         }
     }
     return $useData;
 }
 /**
  * For an activity LO, retrieves the moodle id of the following activity/resource (course_module) in the section of the activity
  * or null if this is the last activity in that section
  * @param \cm_info $rawData Moodle internal info for a course module
  * @return string|NULL  id of the following activity/resource (Course_module)
  */
 private function getFollowingSib($rawData)
 {
     $course_info = $rawData->get_modinfo();
     //get the course_modinfo object with all data of the course
     $section = $course_info->get_section_info($rawData->sectionnum);
     $cmsId = explode(',', $section->sequence);
     //array with the ids of the cms in that section
     $position = array_keys($cmsId, $rawData->id);
     //position of the cm in the sequence
     $pos = $position[0];
     $num_mods = count($cmsId);
     while (true) {
         // 	if($position[0]==(count($cmsId)-1)){   //it is the last in the section, not previous cm
         if ($pos == $num_mods - 1) {
             //it is the last in the section, not previous cm
             $FollowingSib = null;
             break;
         } else {
             // get the previous cm in the sequence
             $FollowingSib = $cmsId[$pos + 1];
             // check if type is ignored
             $factory = Intuitel::getAdaptorInstance()->getGuessedFactory($course_info->get_cm($FollowingSib));
             if (!$factory->toBeIgnored()) {
                 break;
             }
         }
         $pos++;
     }
     // end of while
     return $FollowingSib;
 }
    /**
     * (non-PHPdoc)
     * @see \intuitel\simulator::process_learner_update_request()
     */
    public function process_learner_update_request($loId, $learnerid, $courseLo)
    {
        if ($learnerid instanceof UserId) {
            $userID = $learnerid;
        } else {
            $userID = new UserId((string) $learnerid);
        }
        $user = $this->intuitelAdaptor->getNativeUserFromUId($userID);
        // Select user interface language the same way Mooodle does:
        // First Course forced lang
        // second user preference
        $courseid = Intuitel::getIDFactory()->getIdfromLoId($courseLo->loId);
        global $DB;
        if (!($course = $DB->get_record("course", array("id" => $courseid)))) {
            print_error('coursemisconf');
        }
        global $USER;
        $USER = $user;
        if ($course->lang != '') {
            $USER->lang = $course->lang;
        }
        global $SESSION;
        $SESSION->lang = $USER->lang;
        // end language selection
        $timeFrom = time() - $this->time_window;
        $events_user = $this->intuitelAdaptor->getLearnerUpdateData(array($user->id), $courseLo, $timeFrom, null, false);
        $events = array();
        if (array_key_exists((string) $userID->id, $events_user)) {
            $events = $events_user[(string) $userID->id];
        }
        list($revisits, $durations) = IntuitelController::compute_revisits($events);
        $this->durations = $durations;
        $this->revisits = $revisits;
        $currentKOEvent = count($durations) > 0 ? $durations[0] : null;
        $previousKOEvent = count($durations) > 1 ? $durations[1] : null;
        $previousKO = $previousKOEvent != null ? $this->intuitelAdaptor->createLO($previousKOEvent->loId) : null;
        $previous_use_data = $previousKO != null ? $this->intuitelAdaptor->getUseData($previousKO, $user->id) : array(array());
        $currentKO = $currentKOEvent != null ? $this->intuitelAdaptor->createLO($currentKOEvent->loId) : null;
        $current_use_data = $currentKO != null ? $this->intuitelAdaptor->getUseData($currentKO, $user->id) : array(array());
        $currentKOType = $currentKOEvent != null ? Intuitel::getIDFactory()->getType($currentKOEvent->loId) : null;
        $previousKOType = $previousKOEvent != null ? Intuitel::getIDFactory()->getType($previousKOEvent->loId) : null;
        //
        // simulate intelligent behaviour
        //
        /**
         * Use case: Obvius and tedious tutor: Inform me what I'm doing.
         * DEBUG Use case
         */
        if (false && $currentKO) {
            // disabled
            $mid = Intuitel::getIDFactory()->getNewMessageUUID();
            $count = $revisits[(string) $currentKOEvent->loId];
            $previous = '';
            if ($previousKOEvent) {
                $previous = "Antes has estado en {$previousKOEvent->loId} durante {$previousKOEvent->duration} segundos";
            }
            $this->tugFragment .= <<<xml
<intuirr:Tug uId="jmb0001" mId="{$mid}">
\t<intuirr:MType>2</intuirr:MType>
\t<intuirr:MData>Mensaje Obvio de depuración: Ahora estás en {$currentKOEvent->loId} donde has estado {$count} veces. {$previous}.</intuirr:MData>
</intuirr:Tug>
xml;
        }
        /*         * ********************
         * Use case: Fast Pace User spends less than N seconds in previous LO
         */
        if ($previousKOEvent != null && $previousKOEvent->duration < $this->enough_time && $previousKOType != 'course' && $previousKOType != 'forum' && $previousKOType != 'folder' && (!$previous_use_data['grade'] || !$previous_use_data['grade'] > $previous_use_data['grademax'] * 2 / 3)) {
            $mid = Intuitel::getIDFactory()->getNewMessageUUID();
            $a = new \stdClass();
            $a->duration = $previousKOEvent->duration;
            $a->loId = (string) $previousKOEvent->loId;
            $advice_duration_str = get_string('advice_duration', 'block_intuitel', $a);
            $this->tugFragment .= <<<xml
<intuirr:Tug uId="jmb0001" mId="{$mid}">
\t<intuirr:MType>2</intuirr:MType>
\t<intuirr:MData>{$advice_duration_str}</intuirr:MData>
</intuirr:Tug>
xml;
            $this->selectedLORE[] = new loreRecommendation($previousKOEvent->loId, 70);
        }
        /*         * ****************************
         * Use case: out-of-sequence
         */
        $previousSiblingCount = $currentKO && $currentKO->hasPrecedingSib && array_key_exists($currentKO->hasPrecedingSib->id, $revisits) ? $revisits[$currentKO->hasPrecedingSib->id] : 0;
        if ($currentKO && $currentKO->hasPrecedingSib && $previousSiblingCount == 0 && !($current_use_data['grade'] > $current_use_data['grademax'] * 2 / 3)) {
            $mid = Intuitel::getIDFactory()->getNewMessageUUID();
            $a = new \stdClass();
            $a->previousLoId = (string) $currentKO->hasPrecedingSib;
            $a->currentLoId = (string) $currentKO->loId;
            $advice_sequence_str = get_string('advice_outofsequence', 'block_intuitel', $a);
            $this->tugFragment .= <<<xml
<intuirr:Tug uId="jmb0001" mId="{$mid}">
\t<intuirr:MType>2</intuirr:MType>
\t<intuirr:MData>{$advice_sequence_str}</intuirr:MData>
</intuirr:Tug>
xml;
            // recommend
            $this->selectedLORE[] = new loreRecommendation($currentKO->hasPrecedingSib, 100);
            $this->selectedLORE[] = new loreRecommendation($currentKO->loId, 100);
        }
        /*         * *****************************
         * Use case: excessive revisits
         */
        //    print_object($this->revisits[$currentKOEvent->loId->id]);die;
        if ($currentKOEvent != null && $this->revisits[$currentKOEvent->loId->id] > 2 && $currentKOType != 'course' && $currentKOType != 'forum' && $currentKOType != 'folder') {
            $count = $revisits[$currentKOEvent->loId->id];
            $mid = Intuitel::getIDFactory()->getNewMessageUUID();
            $params_str = new \stdClass();
            $params_str->count = $count;
            $params_str->loId = (string) $currentKOEvent->loId;
            $advice_revisits_str = get_string('advice_revisits', 'block_intuitel', $params_str);
            $this->tugFragment .= <<<xml
<intuirr:Tug uId="jmb0001" mId="{$mid}">
\t<intuirr:MType>2</intuirr:MType>
\t<intuirr:MData>{$advice_revisits_str}</intuirr:MData>
</intuirr:Tug>
xml;
        }
        /*
         * Test case. Graded activity is abandoned with low grade
         */
        if ($previousKOEvent != null && $previous_use_data['grade'] < $previous_use_data['grademax'] * 2 / 3) {
            $grade = number_format($previous_use_data['grade']);
            $grademax = number_format($previous_use_data['grademax']);
            $mid = Intuitel::getIDFactory()->getNewMessageUUID();
            $params_str = new \stdClass();
            $params_str->loId = (string) $previousKOEvent->loId;
            $params_str->grade = $grade;
            $params_str->grademax = $grademax;
            $advice_grade_str = get_string('advice_grade', 'block_intuitel', $params_str);
            $this->tugFragment .= <<<xml
<intuirr:Tug uId="jmb0001" mId="{$mid}">
\t<intuirr:MType>2</intuirr:MType>
\t<intuirr:MData>{$advice_grade_str}</intuirr:MData>
</intuirr:Tug>
xml;
            // recommends previous LO
            if (isset($previousKO->hasPrecedingSib)) {
                $this->selectedLORE[] = new loreRecommendation($previousKO->hasPrecedingSib, 100);
            }
            $this->selectedLORE[] = new loreRecommendation($previousKOEvent->loId, 80);
        }
        /*
         * Test case. Graded activity is abandoned with high grade. The student is congratulated for good result
         */
        if ($previousKOEvent != null && $previous_use_data['grade'] > $previous_use_data['grademax'] * 2 / 3) {
            $grade = number_format($previous_use_data['grade']);
            $grademax = number_format($previous_use_data['grademax']);
            $mid = Intuitel::getIDFactory()->getNewMessageUUID();
            $params_str = new \stdClass();
            $params_str->loId = (string) $previousKOEvent->loId;
            $params_str->grade = $grade;
            $params_str->grademax = $grademax;
            $congratulate_grade_str = get_string('congratulate_grade', 'block_intuitel', $params_str);
            $this->tugFragment .= <<<xml
<intuirr:Tug uId="jmb0001" mId="{$mid}">
\t<intuirr:MType>1</intuirr:MType>
\t<intuirr:MData>{$congratulate_grade_str}</intuirr:MData>
</intuirr:Tug>
xml;
            // recommends previous LO
            if (isset($previousKO->hasPrecedingSib)) {
                $this->selectedLORE[] = new loreRecommendation($previousKO->hasPrecedingSib, 100);
            }
            $this->selectedLORE[] = new loreRecommendation($previousKOEvent->loId, 80);
        }
        /*
         * Test case. Graded activity is visited with high grade. The student is remembered about
         */
        if ($currentKOEvent != null && $current_use_data['grade'] > $current_use_data['grademax'] * 2 / 3) {
            $grade = number_format($current_use_data['grade']);
            $grademax = number_format($current_use_data['grademax']);
            $mid = Intuitel::getIDFactory()->getNewMessageUUID();
            $params_str = new \stdClass();
            $params_str->loId = (string) $currentKOEvent->loId;
            $params_str->grade = $grade;
            $params_str->grademax = $grademax;
            $remember_graded_str = get_string('remember_already_graded', 'block_intuitel', $params_str);
            $this->tugFragment .= <<<xml
<intuirr:Tug uId="jmb0001" mId="{$mid}">
\t<intuirr:MType>1</intuirr:MType>
\t<intuirr:MData>{$remember_graded_str}</intuirr:MData>
</intuirr:Tug>
xml;
        }
        if ($currentKO != null) {
            /*
             * First LO in the course
             */
            $parent = $currentKO->hasParent != null ? $this->intuitelAdaptor->createLO($currentKO->hasParent) : null;
            if ($currentKO->hasPrecedingSib == null && $parent != null && $parent->hasPrecedingSib == null) {
                $this->tugFragment .= <<<xml
<intuirr:Tug uId="intuitelstudent1" mId="db4d7ada-58fb-4939-ba0d-41c9ac585efd" rId="dcbcf826-a5fe-4fe8-bcc2-609c299afe04">
    <intuirr:MType>4</intuirr:MType>
    <intuirr:MData xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    &lt;![CDATA[
    &lt;h4&gt;You just started the available material. Very good!&lt;/h4&gt;
    Please, select a Learning Pathway:&lt;br/&gt;
    &lt;input type=&quot;radio&quot; name=&quot;invalidateLpSelection[]&quot; value=&quot;1&quot;&gt;Classically structured learning path sequenced by matters.&lt;/input&gt;&lt;br/&gt;
\t&lt;input type=&quot;radio&quot; name=&quot;invalidateLpSelection[]&quot; value=&quot;2&quot;&gt;Hierarchically structured material organized by levels of abstraction.&lt;/input&gt;&lt;br/&gt;
\t]]&gt;</intuirr:MData>
    </intuirr:Tug>
xml;
            }
            /*
             * Last LO in the course
             */
            if ($currentKO->hasFollowingSib == null && $parent != null && $parent->hasFollowingSib == null) {
                $this->tugFragment .= <<<xml
<intuirr:Tug uId="intuitelstudent1" mId="db4d7ada-58fb-4939-ba0d-41c9ac585efd" rId="dcbcf826-a5fe-4fe8-bcc2-609c299afe04">
    <intuirr:MType>4</intuirr:MType>
    <intuirr:MData xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">&lt;![CDATA[&lt;h4&gt;You just completed all your assigned material. Very good!&lt;/h4&gt;
Do you want to restart it following another Learning Pathway? &lt;select name="invalidateLpSelection[]" &gt;&lt;option name="invalidateLpSelection-Y" value="Y"&gt;yes&lt;/option&gt;&lt;option name="invalidateLpSelection-N" value="N"&gt;no&lt;/option&gt;&lt;/select&gt;]]&gt;</intuirr:MData>
    </intuirr:Tug>
xml;
            }
            /*
                          $this->tugFragment.= <<<xml
                          <Tug uId="jmb0001" mId="12345678-1234-abcd-ef12-123456789014">
                          <MType>1</MType>
                          <MData>Estás visitando $currentKOEvent->loId y vienes de $previousKOEvent->loId donde has estado $previousKOEvent->duration segundos.</MData>
                          </Tug>
                          xml;
            */
            // if currentKO is the course use previousKO
            if (!isset($currentKO->hasParent)) {
                // course
                $currentKO = $previousKO;
            }
            if (isset($currentKO->hasFollowingSib)) {
                $following = $this->intuitelAdaptor->createLO($currentKOEvent->loId);
                $followingType = Intuitel::getIDFactory()->getType($following->loId);
                if ($followingType == 'section') {
                    $this->selectedLORE[] = new loreRecommendation($following->hasFollowingSib, 70);
                } else {
                    $this->selectedLORE[] = new loreRecommendation($currentKO->hasFollowingSib, 70);
                }
            } else {
                if (isset($currentKO->hasParent)) {
                    // suppose section
                    $sectionLo = $currentKO->hasParent ? $this->intuitelAdaptor->createLO($currentKO->hasParent) : null;
                    //section
                    $nextsectionLo = $sectionLo != null && $sectionLo->hasFollowingSib ? $this->intuitelAdaptor->createLO($sectionLo->hasFollowingSib) : null;
                    // next section
                    if ($nextsectionLo && isset($nextsectionLo->hasChild[0])) {
                        $this->selectedLORE[] = new loreRecommendation($nextsectionLo->hasChild[0], 70);
                    }
                }
            }
        }
        // simulate a sequencing
        $lastLoreRec = count($this->selectedLORE) > 0 ? $this->selectedLORE[count($this->selectedLORE) - 1] : null;
        $lastLoreId = $lastLoreRec ? $lastLoreRec->loId : null;
        for ($i = 0; $i < 4; $i++) {
            if (!$lastLoreId) {
                break;
            }
            try {
                $loreLO = $this->intuitelAdaptor->createLO($lastLoreId);
                if (isset($loreLO->hasFollowingSib)) {
                    $lastLoreId = $loreLO->hasFollowingSib;
                    $this->selectedLORE[] = new loreRecommendation($lastLoreId, $lastLoreRec->score / ($i + 2));
                }
            } catch (UnknownLOTypeException $e) {
                break;
            }
        }
    }
 private function getMedia($loId)
 {
     // get the id of the section in Moodle
     $id = Intuitel::getIDFactory()->getIdfromLoId($loId);
     $section_info = get_section_info($id);
     $media = block_intuitel_getMediaType($section_info->summary);
     //array containing the media types contained in the summary of the section
     return $media;
 }
 public function getIDRegExpr()
 {
     $lmsid = Intuitel::getAdaptorInstance()->getLMSId();
     return "/{$lmsid}-([a-zA-z]+)([0-9]+)/";
 }