Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
/**
4
 * Class H5PReport
5
 * @property  fillInProcessor
6
 */
7
class H5PReport {
8
 
9
  private static $version = '1.1.0';
10
 
11
  private static $processorMap = array(
12
    'compound' => 'CompoundProcessor',
13
    'fill-in' => 'FillInProcessor',
14
    'long-fill-in' => 'FillInProcessor',
15
    'true-false' => 'TrueFalseProcessor',
16
    'matching' => 'MatchingProcessor',
17
    'choice' => 'ChoiceProcessor',
18
    'long-choice' => 'LongChoiceProcessor',
19
  );
20
 
21
  private static $versionExtension = 'https://h5p.org/x-api/h5p-reporting-version';
22
  private static $contentTypeExtension = 'https://h5p.org/x-api/h5p-machine-name';
23
 
24
  public static $contentTypeProcessors = array(
25
    'H5P.DocumentationTool' => 'DocumentationToolProcessor',
26
    'H5P.GoalsPage' => 'GoalsPageProcessor',
27
    'H5P.GoalsAssessmentPage' => 'GoalsAssessmentPageProcessor',
28
    'H5P.StandardPage' => 'StandardPageProcessor',
29
    'H5P.FreeTextQuestion' => 'IVOpenEndedQuestionProcessor',
30
  );
31
 
32
  private $processors = array();
33
 
34
  /**
35
   * Generate the proper report depending on xAPI data.
36
   *
37
   * @param object $xapiData
38
   * @param string $forcedProcessor Force a processor type
39
   * @param bool $disableScoring Disables scoring for the report
40
   *
41
   * @return string A report
42
   */
43
  public function generateReport($xapiData, $forcedProcessor = null, $disableScoring = false) {
44
    $interactionType = $xapiData->interaction_type;
45
    if (!self::isSupportedVersion($xapiData)) {
46
      return self::renderUnsupportedVersionPage($xapiData);
47
    }
48
 
49
    $contentTypeProcessor = self::getContentTypeProcessor($xapiData);
50
    if (isset($contentTypeProcessor)) {
51
      $interactionType = $contentTypeProcessor;
52
    }
53
 
54
    if (isset($forcedProcessor)) {
55
      $interactionType = $forcedProcessor;
56
    }
57
 
58
    if (!isset(self::$processorMap[$interactionType]) && !isset(self::$contentTypeProcessors[$interactionType])) {
59
      return ''; // No processor found
60
    }
61
 
62
    if (!isset($this->processors[$interactionType])) {
63
      // Not used before. Initialize new processor
64
      if (array_key_exists($interactionType, self::$contentTypeProcessors)) {
65
        $this->processors[$interactionType] = new self::$contentTypeProcessors[$interactionType]();
66
      }
67
      else {
68
        $this->processors[$interactionType] = new self::$processorMap[$interactionType]();
69
      }
70
    }
71
 
72
    // Generate and return report from xAPI data
73
    // Allow compound content types to have styles in case they are rendering gradable containers
74
    return $this->processors[$interactionType]
75
      ->generateReport($xapiData, $disableScoring, ($interactionType == "compound" ? true : false));
76
  }
77
 
78
  /**
79
   * Generate the proper report for dynamically gradable content types depending on xAPI data.
80
   *
81
   * @param object $xapiData
82
   * @return string A report
83
   */
84
  public function generateGradableReports($xapiData) {
85
    $results = array();
86
 
87
    foreach ($xapiData as $childData) {
88
     $interactionType = self::getContentTypeProcessor($childData);
89
 
90
     if (!isset($this->processors[$interactionType])) {
91
       // Not used before. Initialize new processor
92
       if (array_key_exists($interactionType, self::$contentTypeProcessors)) {
93
         $this->processors[$interactionType] = new self::$contentTypeProcessors[$interactionType]();
94
       }
95
     }
96
 
97
     if ($interactionType == 'H5P.FreeTextQuestion') {
98
       array_push($results, $childData);
99
     }
100
    }
101
 
102
    if (count($results) > 0) {
103
      return self::buildContainer($results);
104
    }
105
 
106
    // Return nothing if there are no reports
107
    return ' ';
108
  }
109
 
110
  /**
111
   * Generate the wrapping element for a grading container
112
   *
113
   * @param object $results
114
   *
115
   * @return string HTML of the container and within it, gradable elements
116
   */
117
  private function buildContainer($results) {
118
    $container = '<div id="gradable-container" class="h5p-iv-open-ended-grading-container">';
119
 
120
    foreach ($results as $index=>$child) {
121
      $container .= self::buildChild($child, $index);
122
    }
123
 
124
    $container .= '</div>';
125
 
126
    return $container;
127
  }
128
 
129
  /**
130
   * Generate each of the gradable elements
131
   *
132
   * @param object $data
133
   * @param int $index
134
   *
135
   * @return string HTML of a gradable element
136
   */
137
  private function buildChild($data, $index) {
138
    // Generate and return report from xAPI data
139
    $interactionType = self::getContentTypeProcessor($data);
140
    return $this->processors[$interactionType]
141
      ->generateReport($data, false, true);
142
  }
143
 
144
  /**
145
   * Removes gradable children from xAPI data
146
   *
147
   * @return array
148
   */
149
  public function stripGradableChildren($xapiData) {
150
    return array_filter($xapiData, function ($data) {
151
      $contentTypeProcessor = H5PReport::getContentTypeProcessor($data);
152
      $interactionType = $contentTypeProcessor;
153
      return $interactionType !== 'H5P.FreeTextQuestion';
154
    });
155
  }
156
 
157
  /**
158
   * List of CSS stylesheets used by the processors when rendering the report.
159
   *
160
   * @return array
161
   */
162
  public function getStylesUsed() {
163
    $styles = array(
164
      'styles/shared-styles.css'
165
    );
166
 
167
    // Fetch style used by each report processor
168
    foreach ($this->processors as $processor) {
169
      $style = $processor->getStyle();
170
      if (!empty($style)) {
171
        $styles[] = $style;
172
      }
173
    }
174
 
175
    return $styles;
176
  }
177
 
178
 
179
  /**
180
   * List of JS scripts to be used by the processors when rendering the report.
181
   *
182
   * @return array
183
   */
184
  public function getScriptsUsed() {
185
    $scripts = [];
186
 
187
    // Fetch scripts used by each report processor
188
    foreach ($this->processors as $processor) {
189
      $script = $processor->getScript();
190
      if (!empty($script)) {
191
        $scripts[] = $script;
192
      }
193
    }
194
 
195
    return $scripts;
196
  }
197
 
198
  /**
199
   * Caches instance of report generator.
200
   * @return \H5PReport
201
   */
202
  public static function getInstance() {
203
    static $instance;
204
 
205
    if (!$instance) {
206
      $instance = new H5PReport();
207
    }
208
 
209
    return $instance;
210
  }
211
 
212
  /**
213
   * Attempts to retrieve content type processor from xapi data
214
   * @param object $xapiData
215
   *
216
   * @return string|null Content type processor
217
   */
218
  public static function getContentTypeProcessor($xapiData) {
219
    if (!isset($xapiData->additionals)) {
220
      return null;
221
    }
222
 
223
    $extras = json_decode($xapiData->additionals);
224
 
225
    if (!isset($extras->extensions) || !isset($extras->extensions->{self::$contentTypeExtension})) {
226
      return null;
227
    }
228
 
229
    $processor = $extras->extensions->{self::$contentTypeExtension};
230
    if (!array_key_exists($processor, self::$contentTypeProcessors)) {
231
      return null;
232
    }
233
 
234
    return $processor;
235
  }
236
 
237
    /**
238
     * Get required reporting module version from statement
239
     *
240
     * @param $xapiData
241
     *
242
     * @return string Defaults to 1.0.0
243
     */
244
  public static function getVersion($xapiData) {
245
    if (!isset($xapiData->additionals)) {
246
      return '1.0.0';
247
    }
248
 
249
    $additionals = json_decode($xapiData->additionals);
250
    if (!isset($additionals->contextExtensions->{self::$versionExtension})) {
251
      return '1.0.0';
252
    }
253
 
254
    return $additionals->contextExtensions->{self::$versionExtension};
255
  }
256
 
257
    /**
258
     * Check is render report from statement is supported
259
     *
260
     * @param $xapiData
261
     *
262
     * @return bool
263
     */
264
  public static function isSupportedVersion($xapiData) {
265
    $reportingVersion = array_map('intval', explode('.', self::$version));
266
    $statementVersion = array_map('intval', explode('.', self::getVersion($xapiData)));
267
 
268
    // Sanitation
269
    if (!count($statementVersion) === 3) {
270
      return false;
271
    }
272
 
273
    // Check major version
274
    if ($reportingVersion[0] < $statementVersion[0]) {
275
      return false;
276
    }
277
 
278
    // Check minor version
279
    $hasOutdatedMinorVersion = $reportingVersion[0] === $statementVersion[0]
280
                               && $reportingVersion[1] < $statementVersion[1];
281
    if ($hasOutdatedMinorVersion) {
282
      return false;
283
    }
284
 
285
    // Patch versions are assumed to be compatible
286
    return true;
287
  }
288
 
289
    /**
290
     * Display message saying that report could not be rendered
291
     *
292
     * @param $xapiData
293
     *
294
     * @return string
295
     */
296
  public static function renderUnsupportedVersionPage($xapiData) {
297
    $requiredVersion = self::getVersion($xapiData);
298
    $installedVersion = self::$version;
299
    return "<div>Version {$requiredVersion} of the reporting module is required to render this report. Currently installed: {$installedVersion}</div>";
300
  }
301
}