Total Complexity | 109 |
Total Lines | 764 |
Duplicated Lines | 11.13 % |
Changes | 10 | ||
Bugs | 2 | Features | 3 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like unicon.matthews.xapi.service.DefaultXapiToCaliperConversionService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | /** |
||
53 | @Component |
||
54 | public class DefaultXapiToCaliperConversionService implements XapiConversionService { |
||
55 | |||
56 | private BidiMap<Action, String> verbActionMap; |
||
57 | private BidiMap<Type, String> objectEntityMap; |
||
58 | private Map<Action, EventType> actionEventMap; |
||
59 | |||
60 | @PostConstruct |
||
61 | private void init() { |
||
62 | verbActionMap = new DualHashBidiMap<Action, String>(); |
||
63 | verbActionMap.put(Action.ABANDONED, "https://w3id.org/xapi/adl/verbs/abandoned"); |
||
64 | //verbActionMap.put(Action.ACTIVATED, ); |
||
65 | verbActionMap.put(Action.ATTACHED, "http://activitystrea.ms/schema/1.0/attach"); |
||
66 | verbActionMap.put(Action.BOOKMARKED, "http://id.tincanapi.com/verb/bookmarked"); |
||
67 | //verbActionMap.put(Action.CHANGED_RESOLUTION, ); |
||
68 | //verbActionMap.put(Action.CHANGED_SIZE, ); |
||
69 | //verbActionMap.put(Action.CHANGED_VOLUME, ); |
||
70 | //verbActionMap.put(Action.CLASSIFIED, ); |
||
71 | //verbActionMap.put(Action.CLOSED_POPOUT, ); |
||
72 | verbActionMap.put(Action.COMMENTED, "http://adlnet.gov/expapi/verbs/commented"); |
||
73 | verbActionMap.put(Action.COMPLETED, "http://adlnet.gov/expapi/verbs/completed"); |
||
74 | //verbActionMap.put(Action.DEACTIVATED, ); |
||
75 | //verbActionMap.put(Action.DESCRIBED, ); |
||
76 | verbActionMap.put(Action.DISLIKED, "http://activitystrea.ms/schema/1.0/dislike"); |
||
77 | //verbActionMap.put(Action.DISABLED_CLOSED_CAPTIONING, ); |
||
78 | //verbActionMap.put(Action.ENABLED_CLOSED_CAPTIONING, ); |
||
79 | //verbActionMap.put(Action.ENDED, ); |
||
80 | //verbActionMap.put(Action.ENTERED_FULLSCREEN, ); |
||
81 | //verbActionMap.put(Action.EXITED_FULLSCREEN, ); |
||
82 | //verbActionMap.put(Action.FORWARDED_TO, ); |
||
83 | verbActionMap.put(Action.GRADED, "http://adlnet.gov/expapi/verbs/scored"); |
||
84 | //verbActionMap.put(Action.HID, ); |
||
85 | //verbActionMap.put(Action.HIGHLIGHTED, ); |
||
86 | //verbActionMap.put(Action.JUMPED_TO, ); |
||
87 | //verbActionMap.put(Action.IDENTIFIED, ); |
||
88 | verbActionMap.put(Action.LIKED, "http://activitystrea.ms/schema/1.0/like"); |
||
89 | //verbActionMap.put(Action.LINKED, ); |
||
90 | |||
91 | // alternatives |
||
92 | // https://w3id.org/xapi/adl/verbs/logged-in |
||
93 | // https://w3id.org/xapi/adl/verbs/logged-out |
||
94 | |||
95 | verbActionMap.put(Action.LOGGED_IN, "https://brindlewaye.com/xAPITerms/verbs/loggedin/"); |
||
96 | verbActionMap.put(Action.LOGGED_OUT, "https://brindlewaye.com/xAPITerms/verbs/loggedout/"); |
||
97 | |||
98 | //verbActionMap.put(Action.MUTED, ); |
||
99 | //verbActionMap.put(Action.NAVIGATED_TO, ); |
||
100 | //verbActionMap.put(Action.OPENED_POPOUT, ); |
||
101 | verbActionMap.put(Action.PAUSED, "http://id.tincanapi.com/verb/paused"); |
||
102 | //verbActionMap.put(Action.RANKED, ); |
||
103 | verbActionMap.put(Action.QUESTIONED, "http://adlnet.gov/expapi/verbs/asked"); |
||
104 | //verbActionMap.put(Action.RECOMMENDED, ); |
||
105 | verbActionMap.put(Action.REPLIED, "http://adlnet.gov/expapi/verbs/responded"); |
||
106 | //verbActionMap.put(Action.RESTARTED, ); |
||
107 | verbActionMap.put(Action.RESUMED, "http://adlnet.gov/expapi/verbs/resumed"); |
||
108 | verbActionMap.put(Action.REVIEWED, "http://id.tincanapi.com/verb/reviewed"); |
||
109 | //verbActionMap.put(Action.REWOUND, ); |
||
110 | verbActionMap.put(Action.SEARCHED, "http://activitystrea.ms/schema/1.0/search"); |
||
111 | verbActionMap.put(Action.SHARED, "http://activitystrea.ms/schema/1.0/share"); |
||
112 | //verbActionMap.put(Action.SHOWED, ); |
||
113 | verbActionMap.put(Action.SKIPPED, "http://id.tincanapi.com/verb/skipped"); |
||
114 | verbActionMap.put(Action.STARTED, "http://activitystrea.ms/schema/1.0/start"); |
||
115 | verbActionMap.put(Action.SUBMITTED, "http://activitystrea.ms/schema/1.0/submit"); |
||
116 | //verbActionMap.put(Action.SUBSCRIBED, ); |
||
117 | verbActionMap.put(Action.TAGGED, "http://activitystrea.ms/schema/1.0/tag"); |
||
118 | //verbActionMap.put(Action.TIMED_OUT, ); |
||
119 | verbActionMap.put(Action.VIEWED, "http://id.tincanapi.com/verb/viewed"); |
||
120 | //verbActionMap.put(Action.UNMUTED, ); |
||
121 | |||
122 | objectEntityMap = new DualHashBidiMap<Type, String>(); |
||
123 | // TODO support other xapi annotation types |
||
124 | objectEntityMap.put(EntityType.ANNOTATION, "http://risc-inc.com/annotator/activities/highlight"); |
||
125 | //objectEntityMap.put(EntityType.ATTEMPT, arg1); |
||
126 | //objectEntityMap.put(EntityType.COURSE_OFFERING, arg1); |
||
127 | objectEntityMap.put(EntityType.COURSE_SECTION, "http://adlnet.gov/expapi/activities/course"); |
||
128 | objectEntityMap.put(EntityType.DIGITAL_RESOURCE, "http://adlnet.gov/expapi/activities/media"); |
||
129 | //objectEntityMap.put(EntityType.ENTITY, ); |
||
130 | objectEntityMap.put(EntityType.GROUP, "http://activitystrea.ms/schema/1.0/group"); |
||
131 | objectEntityMap.put(EntityType.LEARNING_OBJECTIVE, "http://adlnet.gov/expapi/activities/objective"); |
||
132 | //objectEntityMap.put(EntityType.MEMBERSHIP, arg1); |
||
133 | objectEntityMap.put(EntityType.PERSON, "http://activitystrea.ms/schema/1.0/person"); |
||
134 | objectEntityMap.put(EntityType.ORGANIZATION, "http://activitystrea.ms/schema/1.0/organization"); |
||
135 | //objectEntityMap.put(EntityType.RESPONSE, arg1); |
||
136 | //objectEntityMap.put(EntityType.RESULT, arg1); |
||
137 | //objectEntityMap.put(EntityType.SESSION, arg1); |
||
138 | objectEntityMap.put(EntityType.SOFTWARE_APPLICATION, "http://activitystrea.ms/schema/1.0/application"); |
||
139 | //objectEntityMap.put(EntityType.VIEW, arg1); |
||
140 | //objectEntityMap.put(DigitalResourceType.MEDIA_LOCATION, arg1); |
||
141 | objectEntityMap.put(DigitalResourceType.MEDIA_OBJECT, "http://adlnet.gov/expapi/activities/media"); |
||
142 | //objectEntityMap.put(DigitalResourceType.READING, arg1); |
||
143 | objectEntityMap.put(DigitalResourceType.WEB_PAGE, "http://activitystrea.ms/schema/1.0/page"); |
||
144 | |||
145 | actionEventMap = new HashMap<Action,EventType>(); |
||
|
|||
146 | actionEventMap.put(Action.ABANDONED, EventType.ASSIGNABLE); |
||
147 | actionEventMap.put(Action.ACTIVATED, EventType.ASSIGNABLE); |
||
148 | actionEventMap.put(Action.ATTACHED, EventType.ANNOTATION); |
||
149 | actionEventMap.put(Action.BOOKMARKED, EventType.ANNOTATION); |
||
150 | actionEventMap.put(Action.CHANGED_RESOLUTION, EventType.MEDIA); |
||
151 | actionEventMap.put(Action.CHANGED_SIZE, EventType.MEDIA); |
||
152 | actionEventMap.put(Action.CHANGED_VOLUME, EventType.MEDIA); |
||
153 | actionEventMap.put(Action.CLASSIFIED, EventType.ANNOTATION); |
||
154 | actionEventMap.put(Action.CLOSED_POPOUT, EventType.MEDIA); |
||
155 | actionEventMap.put(Action.COMMENTED, EventType.ANNOTATION); |
||
156 | actionEventMap.put(Action.COMPLETED, EventType.ASSIGNABLE); |
||
157 | actionEventMap.put(Action.DEACTIVATED, EventType.ASSIGNABLE); |
||
158 | actionEventMap.put(Action.DESCRIBED, EventType.ANNOTATION); |
||
159 | actionEventMap.put(Action.DISLIKED, EventType.ANNOTATION); |
||
160 | actionEventMap.put(Action.DISABLED_CLOSED_CAPTIONING, EventType.MEDIA); |
||
161 | actionEventMap.put(Action.ENABLED_CLOSED_CAPTIONING, EventType.MEDIA); |
||
162 | actionEventMap.put(Action.ENDED, EventType.MEDIA); |
||
163 | actionEventMap.put(Action.ENTERED_FULLSCREEN, EventType.MEDIA); |
||
164 | actionEventMap.put(Action.EXITED_FULLSCREEN, EventType.MEDIA); |
||
165 | actionEventMap.put(Action.FORWARDED_TO, EventType.MEDIA); |
||
166 | actionEventMap.put(Action.GRADED, EventType.OUTCOME); |
||
167 | actionEventMap.put(Action.HID, EventType.ASSIGNABLE); |
||
168 | actionEventMap.put(Action.HIGHLIGHTED, EventType.ANNOTATION); |
||
169 | actionEventMap.put(Action.JUMPED_TO, EventType.MEDIA); |
||
170 | actionEventMap.put(Action.IDENTIFIED, EventType.ANNOTATION); |
||
171 | actionEventMap.put(Action.LIKED, EventType.ANNOTATION); |
||
172 | actionEventMap.put(Action.LINKED, EventType.ANNOTATION); |
||
173 | actionEventMap.put(Action.LOGGED_IN, EventType.SESSION); |
||
174 | actionEventMap.put(Action.LOGGED_OUT, EventType.SESSION); |
||
175 | actionEventMap.put(Action.MUTED, EventType.MEDIA); |
||
176 | actionEventMap.put(Action.NAVIGATED_TO, EventType.NAVIGATION); |
||
177 | actionEventMap.put(Action.OPENED_POPOUT, EventType.MEDIA); |
||
178 | actionEventMap.put(Action.PAUSED, EventType.MEDIA); |
||
179 | actionEventMap.put(Action.RANKED, EventType.ANNOTATION); |
||
180 | actionEventMap.put(Action.QUESTIONED, EventType.ANNOTATION); |
||
181 | actionEventMap.put(Action.RECOMMENDED, EventType.ANNOTATION); |
||
182 | actionEventMap.put(Action.REPLIED, EventType.ANNOTATION); |
||
183 | actionEventMap.put(Action.RESTARTED, EventType.ASSESSMENT); |
||
184 | actionEventMap.put(Action.RESUMED, EventType.MEDIA); |
||
185 | actionEventMap.put(Action.REVIEWED, EventType.ASSIGNABLE); |
||
186 | actionEventMap.put(Action.REWOUND, EventType.MEDIA); |
||
187 | actionEventMap.put(Action.SEARCHED, EventType.READING); |
||
188 | actionEventMap.put(Action.SHARED, EventType.ANNOTATION); |
||
189 | actionEventMap.put(Action.SHOWED, EventType.ASSIGNABLE); |
||
190 | actionEventMap.put(Action.SKIPPED, EventType.ASSESSMENT_ITEM); |
||
191 | actionEventMap.put(Action.STARTED, EventType.EVENT); |
||
192 | actionEventMap.put(Action.SUBMITTED, EventType.EVENT); |
||
193 | actionEventMap.put(Action.SUBSCRIBED, EventType.ANNOTATION); |
||
194 | actionEventMap.put(Action.TAGGED, EventType.ANNOTATION); |
||
195 | actionEventMap.put(Action.TIMED_OUT, EventType.SESSION); |
||
196 | actionEventMap.put(Action.VIEWED, EventType.EVENT); |
||
197 | actionEventMap.put(Action.UNMUTED, EventType.MEDIA); |
||
198 | |||
199 | } |
||
200 | |||
201 | public Event fromXapi(Statement statement) { |
||
202 | // EVENT TIME |
||
203 | LocalDateTime eventTime = null; |
||
204 | String timestamp = statement.getTimestamp(); |
||
205 | if (StringUtils.isNotBlank(timestamp)) { |
||
206 | if (timestamp.endsWith("Z")) { |
||
207 | Instant instant = Instant.parse(timestamp); |
||
208 | eventTime = LocalDateTime.ofInstant(instant, ZoneId.of(ZoneOffset.UTC.getId())); |
||
209 | } |
||
210 | else { |
||
211 | ZonedDateTime zdt = ZonedDateTime.parse(timestamp); |
||
212 | ZonedDateTime utc_zone = zdt.toLocalDateTime().atZone(ZoneOffset.UTC); |
||
213 | eventTime = utc_zone.toLocalDateTime(); |
||
214 | } |
||
215 | |||
216 | } |
||
217 | else { |
||
218 | eventTime = LocalDateTime.now(ZoneId.of(ZoneOffset.UTC.getId())); |
||
219 | } |
||
220 | // EVENT TIME END |
||
221 | |||
222 | // ACTOR |
||
223 | Agent caliperActor = null; |
||
224 | String actorId = null; |
||
225 | XApiActor xapiActor = statement.getActor(); |
||
226 | if (xapiActor != null) { |
||
227 | |||
228 | String actorType = null; |
||
229 | Map<String, String> actorExtensions = new HashMap<>(); |
||
230 | String actorName = xapiActor.getName(); |
||
231 | |||
232 | String openId = xapiActor.getOpenid(); |
||
233 | String mbox = xapiActor.getMbox(); |
||
234 | XApiAccount xapiAccount = xapiActor.getAccount(); |
||
235 | |||
236 | if (StringUtils.isNotBlank(openId)) { |
||
237 | actorId = openId; |
||
238 | actorType = ACTOR_TYPE_OPENID; |
||
239 | } |
||
240 | else if (StringUtils.isNotBlank(mbox)) { |
||
241 | actorId = mbox; |
||
242 | actorType = ACTOR_TYPE_MBOX; |
||
243 | } |
||
244 | else if (xapiAccount != null) { |
||
245 | String accountName = xapiAccount.getName(); |
||
246 | String homePage = xapiAccount.getHomePage(); |
||
247 | |||
248 | if (StringUtils.isNotBlank(homePage)) { |
||
249 | |||
250 | if (StringUtils.isNotBlank(accountName)) { |
||
251 | actorId = accountName; |
||
252 | actorExtensions.put("HOMEPAGE", homePage); |
||
253 | } |
||
254 | else { |
||
255 | actorId = homePage; |
||
256 | } |
||
257 | } |
||
258 | else if (StringUtils.isNotBlank(accountName)) { |
||
259 | actorId = accountName; |
||
260 | } |
||
261 | |||
262 | actorType = ACTOR_TYPE_ACCOUNT; |
||
263 | } |
||
264 | |||
265 | caliperActor |
||
266 | = new Agent.Builder() |
||
267 | .withContext(Context.CONTEXT.getValue()) |
||
268 | .withName(actorName) |
||
269 | .withId(actorId) |
||
270 | .withType(actorType) |
||
271 | .withExtensions(actorExtensions) |
||
272 | .build(); |
||
273 | } |
||
274 | // ACTOR END |
||
275 | |||
276 | // RESULT |
||
277 | Entity caliperResult = null; |
||
278 | XApiResult xapiResult = statement.getResult(); |
||
279 | if (xapiResult != null) { |
||
280 | Map<String,String> resultExtensions = null; |
||
281 | Map<URI,java.lang.Object> xapiResultExtensions = xapiResult.getExtensions(); |
||
282 | if (xapiResultExtensions != null && !xapiResultExtensions.isEmpty()) { |
||
283 | resultExtensions = new HashMap<>(); |
||
284 | for (Map.Entry<URI,java.lang.Object> entry : xapiResultExtensions.entrySet()) { |
||
285 | resultExtensions.put(entry.getKey().toString(),entry.getValue().toString()); |
||
286 | } |
||
287 | } |
||
288 | |||
289 | Double score = null; |
||
290 | XApiScore xapiScore = xapiResult.getScore(); |
||
291 | if (xapiScore != null) { |
||
292 | score = xapiScore.getRaw(); |
||
293 | } |
||
294 | |||
295 | caliperResult |
||
296 | = new Entity.Builder() |
||
297 | .withId(UUID.randomUUID().toString()) |
||
298 | .withContext(Context.CONTEXT.getValue()) |
||
299 | .withExtensions(resultExtensions) |
||
300 | .withActor(actorId) |
||
301 | .withType("http://purl.imsglobal.org/caliper/v1/Result") |
||
302 | .withTotalScore(score) |
||
303 | .build(); |
||
304 | } |
||
305 | // END Result |
||
306 | |||
307 | // ACTION |
||
308 | String caliperAction = null; |
||
309 | XApiVerb xapiVerb = statement.getVerb(); |
||
310 | if (xapiVerb != null) { |
||
311 | String verbId = xapiVerb.getId(); |
||
312 | caliperAction = xApiVerbToCaliperAction(verbId); |
||
313 | } |
||
314 | // ACTION END |
||
315 | |||
316 | // OBJECT |
||
317 | Entity caliperObject = null; |
||
318 | XApiObject xapiObject = statement.getObject(); |
||
319 | if (xapiObject != null) { |
||
320 | |||
321 | String objectType = xapiObjectTypeToCaliperEntityType(null); |
||
322 | String objectName = null; |
||
323 | String objectDescription = null; |
||
324 | Map<String,String> objectExtensions = null; |
||
325 | String objectId = xapiObject.getId(); |
||
326 | |||
327 | XApiObjectDefinition xapiObjectDefinition = xapiObject.getDefinition(); |
||
328 | if (xapiObjectDefinition != null) { |
||
329 | String xapiObjectDefinitionType = xapiObjectDefinition.getType(); |
||
330 | if (StringUtils.isNotBlank(xapiObjectDefinitionType)) { |
||
331 | objectType = xapiObjectTypeToCaliperEntityType(xapiObjectDefinitionType); |
||
332 | } |
||
333 | |||
334 | Map<String,String> names = xapiObjectDefinition.getName(); |
||
335 | if (names != null) { |
||
336 | if (names.size() == 1) { |
||
337 | objectName = CollectionUtils.get(names, 0).getValue(); |
||
338 | } |
||
339 | else { |
||
340 | // default to en? |
||
341 | objectName = names.get("en"); |
||
342 | } |
||
343 | } |
||
344 | |||
345 | Map<String,String> descriptions = xapiObjectDefinition.getDescription(); |
||
346 | if (descriptions != null) { |
||
347 | if (descriptions.size() == 1) { |
||
348 | objectDescription = CollectionUtils.get(descriptions, 0).getValue(); |
||
349 | } |
||
350 | else { |
||
351 | // default to en? |
||
352 | objectDescription = descriptions.get("en"); |
||
353 | } |
||
354 | } |
||
355 | |||
356 | Map<URI,java.lang.Object> extensions = xapiObjectDefinition.getExtensions(); |
||
357 | if (extensions != null && !extensions.isEmpty()) { |
||
358 | objectExtensions = new HashMap<String,String>(extensions.size()); |
||
359 | for (URI key : extensions.keySet()) { |
||
360 | objectExtensions.put(key.toString(), extensions.get(key).toString()); |
||
361 | } |
||
362 | } |
||
363 | } |
||
364 | |||
365 | caliperObject |
||
366 | = new Entity.Builder() |
||
367 | .withId(objectId) |
||
368 | .withContext(Context.CONTEXT.getValue()) |
||
369 | .withType(objectType) |
||
370 | .withName(objectName) |
||
371 | .withDescription(objectDescription) |
||
372 | .withExtensions(objectExtensions) |
||
373 | .build(); |
||
374 | } |
||
375 | // OBJECT END |
||
376 | |||
377 | Group caliperGroup = null; |
||
378 | XApiContext xapiContext = statement.getContext(); |
||
379 | if (xapiContext != null) { |
||
380 | Map<String,String> contextExtensions = null; |
||
381 | Map<URI,java.lang.Object> extensions = xapiContext.getExtensions(); |
||
382 | if (extensions != null && !extensions.isEmpty()) { |
||
383 | contextExtensions = new HashMap<String,String>(extensions.size()); |
||
384 | for (URI key : extensions.keySet()) { |
||
385 | contextExtensions.put(key.toString(), extensions.get(key).toString()); |
||
386 | } |
||
387 | } |
||
388 | |||
389 | XApiContextActivities xapiContextActivities = xapiContext.getContextActivities(); |
||
390 | if (xapiContextActivities != null) { |
||
391 | List<XApiObject> contextActivityParents = xapiContextActivities.getParent(); |
||
392 | |||
393 | if (contextActivityParents != null && contextActivityParents.size() == 1 |
||
394 | && contextActivityParents.get(0).getId().contains("portal/site")) { |
||
395 | caliperGroup |
||
396 | = new Group.Builder() |
||
397 | .withId(StringUtils.substringAfterLast(contextActivityParents.get(0).getId(), "/")) |
||
398 | .withContext(Context.CONTEXT.getValue()) |
||
399 | .withType("http://purl.imsglobal.org/caliper/v1/lis/CourseSection") |
||
400 | .build(); |
||
401 | } |
||
402 | |||
403 | if (caliperGroup == null) { |
||
404 | List<XApiObject> groupings = xapiContextActivities.getGrouping(); |
||
405 | if (groupings != null && !groupings.isEmpty()) { |
||
406 | XApiObject grouping = null; |
||
407 | String id = null; |
||
408 | String type = null; |
||
409 | |||
410 | if (groupings.size() == 1) { |
||
411 | grouping = groupings.get(0); |
||
412 | |||
413 | if (extensions != null) { |
||
414 | Object paramMap = null; |
||
415 | try { |
||
416 | paramMap = extensions.get(new URI("http://lrs.learninglocker.net/define/extensions/moodle_logstore_standard_log")); |
||
417 | } |
||
418 | catch (URISyntaxException e) { |
||
419 | // TODO |
||
420 | } |
||
421 | if (paramMap != null && paramMap instanceof Map) { |
||
422 | Map<String, String> groupExtMap = (Map<String, String>)paramMap; |
||
423 | id = groupExtMap.get("courseid"); |
||
424 | type = "http://purl.imsglobal.org/caliper/v1/lis/CourseSection"; |
||
425 | } |
||
426 | } |
||
427 | } |
||
428 | else { |
||
429 | for (XApiObject xo : groupings) { |
||
430 | grouping = xo; |
||
431 | XApiObjectDefinition xapiObjectDefinition = xo.getDefinition(); |
||
432 | if (xapiObjectDefinition != null) { |
||
433 | if ("http://lrs.learninglocker.net/define/type/moodle/course".equals(xapiObjectDefinition.getType())) { |
||
434 | type = "http://purl.imsglobal.org/caliper/v1/lis/CourseSection"; |
||
435 | Map<URI, Object> groupExt = xapiObjectDefinition.getExtensions(); |
||
436 | if (groupExt != null) { |
||
437 | try { |
||
438 | Object paramMap = groupExt.get(new URI("http://lrs.learninglocker.net/define/extensions/moodle_course")); |
||
439 | if (paramMap instanceof Map) { |
||
440 | Map<String, String> groupExtMap = (Map<String, String>)paramMap; |
||
441 | id = groupExtMap.get("id"); |
||
442 | } |
||
443 | |||
444 | } |
||
445 | catch (URISyntaxException e) { |
||
446 | //TODO |
||
447 | } |
||
448 | } |
||
449 | |||
450 | break; |
||
451 | } |
||
452 | } |
||
453 | } |
||
454 | } |
||
455 | |||
456 | if (grouping != null) { |
||
457 | String name = null; |
||
458 | String description = null; |
||
459 | XApiObjectDefinition xapiObjectDefinition = grouping.getDefinition(); |
||
460 | if (xapiObjectDefinition != null) { |
||
461 | |||
462 | if (StringUtils.isBlank(type) && StringUtils.isNoneBlank(xapiObjectDefinition.getType())) { |
||
463 | type = xapiObjectDefinition.getType(); |
||
464 | } |
||
465 | |||
466 | Map<String,String> names =xapiObjectDefinition.getName(); |
||
467 | if (names != null) { |
||
468 | if (names.size() == 1) { |
||
469 | name = CollectionUtils.get(names, 0).getValue(); |
||
470 | } |
||
471 | else { |
||
472 | // default to en? |
||
473 | name = names.get("en"); |
||
474 | } |
||
475 | } |
||
476 | |||
477 | Map<String,String> descriptions = xapiObjectDefinition.getDescription(); |
||
478 | if (descriptions != null) { |
||
479 | if (descriptions.size() == 1) { |
||
480 | description = CollectionUtils.get(descriptions, 0).getValue(); |
||
481 | } |
||
482 | else { |
||
483 | // default to en? |
||
484 | description = descriptions.get("en"); |
||
485 | } |
||
486 | } |
||
487 | } |
||
488 | |||
489 | List<XApiObject> parents = xapiContextActivities.getParent(); |
||
490 | SubOrganizationOf subOrganizationOf = null; |
||
491 | View Code Duplication | if (parents != null && parents.size() == 1) { |
|
492 | XApiObject parent = parents.get(0); |
||
493 | String parentId = parent.getId(); |
||
494 | String parentType = "http://purl.imsglobal.org/caliper/v1/lis/CourseOffering"; |
||
495 | String parentName = null; |
||
496 | String parentDescription = null; |
||
497 | XApiObjectDefinition parentXapiObjectDefinition = parent.getDefinition(); |
||
498 | if (parentXapiObjectDefinition != null) { |
||
499 | |||
500 | if (StringUtils.isNoneBlank(parentXapiObjectDefinition.getType())) { |
||
501 | parentType = parentXapiObjectDefinition.getType(); |
||
502 | } |
||
503 | |||
504 | Map<String,String> names = parentXapiObjectDefinition.getName(); |
||
505 | if (names != null) { |
||
506 | if (names.size() == 1) { |
||
507 | parentName = CollectionUtils.get(names, 0).getValue(); |
||
508 | } |
||
509 | else { |
||
510 | // default to en? |
||
511 | parentName = names.get("en"); |
||
512 | } |
||
513 | } |
||
514 | |||
515 | Map<String,String> descriptions = parentXapiObjectDefinition.getDescription(); |
||
516 | if (descriptions != null) { |
||
517 | if (descriptions.size() == 1) { |
||
518 | parentDescription = CollectionUtils.get(descriptions, 0).getValue(); |
||
519 | } |
||
520 | else { |
||
521 | // default to en? |
||
522 | parentDescription = descriptions.get("en"); |
||
523 | } |
||
524 | } |
||
525 | |||
526 | subOrganizationOf |
||
527 | = new SubOrganizationOf.Builder() |
||
528 | .withId(parentId) |
||
529 | .withContext(Context.CONTEXT.getValue()) |
||
530 | .withType(parentType) |
||
531 | .withName(parentName) |
||
532 | .withDescription(parentDescription) |
||
533 | .build(); |
||
534 | } |
||
535 | } |
||
536 | |||
537 | caliperGroup |
||
538 | = new Group.Builder() |
||
539 | .withId(id) |
||
540 | .withContext(Context.CONTEXT.getValue()) |
||
541 | .withType(type) |
||
542 | .withName(name) |
||
543 | .withDescription(description) |
||
544 | .withExtensions(contextExtensions) |
||
545 | .withSubOrganizationOf(subOrganizationOf) |
||
546 | .build(); |
||
547 | } |
||
548 | View Code Duplication | else if (xapiContextActivities.getParent() != null) { |
|
549 | XApiObject parent = xapiContextActivities.getParent().get(0); |
||
550 | String name = null; |
||
551 | String description = null; |
||
552 | XApiObjectDefinition xapiObjectDefinition = parent.getDefinition(); |
||
553 | if (xapiObjectDefinition != null) { |
||
554 | if (StringUtils.isBlank(type) && StringUtils.isNoneBlank(xapiObjectDefinition.getType())) { |
||
555 | type = xapiObjectDefinition.getType(); |
||
556 | } |
||
557 | |||
558 | Map<String,String> names = xapiObjectDefinition.getName(); |
||
559 | if (names != null) { |
||
560 | if (names.size() == 1) { |
||
561 | name = CollectionUtils.get(names, 0).getValue(); |
||
562 | } |
||
563 | else { |
||
564 | // default to en? |
||
565 | name = names.get("en"); |
||
566 | } |
||
567 | } |
||
568 | |||
569 | Map<String,String> descriptions = xapiObjectDefinition.getDescription(); |
||
570 | if (descriptions != null) { |
||
571 | if (descriptions.size() == 1) { |
||
572 | description = CollectionUtils.get(descriptions, 0).getValue(); |
||
573 | } |
||
574 | else { |
||
575 | // default to en? |
||
576 | description = descriptions.get("en"); |
||
577 | } |
||
578 | } |
||
579 | } |
||
580 | |||
581 | caliperGroup |
||
582 | = new Group.Builder() |
||
583 | .withId(parent.getId()) |
||
584 | .withContext(Context.CONTEXT.getValue()) |
||
585 | .withType(type) |
||
586 | .withName(name) |
||
587 | .withDescription(description) |
||
588 | .withExtensions(contextExtensions) |
||
589 | .build(); |
||
590 | } |
||
591 | } |
||
592 | } |
||
593 | } |
||
594 | } |
||
595 | |||
596 | return |
||
597 | new Event.Builder() |
||
598 | .withAction(caliperAction) |
||
599 | .withAgent(caliperActor) |
||
600 | .withObject(caliperObject) |
||
601 | .withEventTime(eventTime) |
||
602 | .withContext(xapiToCaliperType(statement)) |
||
603 | .withGroup(caliperGroup) |
||
604 | .withGenerated(caliperResult) |
||
605 | .build(); |
||
606 | |||
607 | } |
||
608 | |||
609 | public Statement toXapi(Event event) throws URISyntaxException { |
||
610 | Statement statement = new Statement(); |
||
611 | |||
612 | // ID |
||
613 | statement.setId(event.getId()); |
||
614 | // END ID |
||
615 | |||
616 | // EVENT TIME |
||
617 | LocalDateTime eventTime = event.getEventTime(); |
||
618 | if (eventTime != null) { |
||
619 | Instant instant = eventTime.toInstant(ZoneOffset.UTC); |
||
620 | //DateTimeFormatter fmt = DateTimeFormatter.ISO_INSTANT; |
||
621 | statement.setTimestamp(instant.toString()); |
||
622 | } |
||
623 | // END EVENT TIME |
||
624 | |||
625 | // ACTOR |
||
626 | Agent actor = event.getAgent(); |
||
627 | if (actor != null) { |
||
628 | Map<String,String> actorExtensions = actor.getExtensions(); |
||
629 | XApiActor xapiActor = new XApiActor(); |
||
630 | |||
631 | String actorId = actor.getId(); |
||
632 | String actorType = actor.getType(); |
||
633 | xapiActor.setName(actor.getName()); |
||
634 | |||
635 | if (ACTOR_TYPE_OPENID.equals(actorType)) { |
||
636 | xapiActor.setOpenid(actorId); |
||
637 | } |
||
638 | else if (ACTOR_TYPE_MBOX.equals(actorType)) { |
||
639 | xapiActor.setMbox(actorId); |
||
640 | } |
||
641 | else { |
||
642 | XApiAccount xapiAccount = new XApiAccount(); |
||
643 | |||
644 | if (actorExtensions != null && !actorExtensions.isEmpty()) { |
||
645 | String homePage = actorExtensions.get("HOMEPAGE"); |
||
646 | if (StringUtils.isNotBlank(homePage)) { |
||
647 | xapiAccount.setHomePage(homePage); |
||
648 | } |
||
649 | } |
||
650 | |||
651 | xapiAccount.setName(actorId); |
||
652 | xapiActor.setAccount(xapiAccount); |
||
653 | } |
||
654 | statement.setActor(xapiActor); |
||
655 | } |
||
656 | // ACTOR END |
||
657 | |||
658 | // RESULT |
||
659 | Entity caliperGeneratable = event.getGenerated(); |
||
660 | if (caliperGeneratable != null) { |
||
661 | Map<String,String> caliperResultExtensions = caliperGeneratable.getExtensions(); |
||
662 | |||
663 | Map<URI,java.lang.Object> xapiResultExtensions = null; |
||
664 | if (caliperResultExtensions != null && !caliperResultExtensions.isEmpty()) { |
||
665 | xapiResultExtensions = new HashMap<>(); |
||
666 | for (Map.Entry<String,String> entry : caliperResultExtensions.entrySet()) { |
||
667 | xapiResultExtensions.put(new URI(entry.getKey()), entry.getValue()); |
||
668 | } |
||
669 | |||
670 | XApiResult xapiResult = new XApiResult(); |
||
671 | xapiResult.setExtensions(xapiResultExtensions); |
||
672 | statement.setResult(xapiResult); |
||
673 | } |
||
674 | } |
||
675 | // END Result |
||
676 | |||
677 | |||
678 | // ACTION |
||
679 | String action = event.getAction(); |
||
680 | if (StringUtils.isNotBlank(action)) { |
||
681 | XApiVerb xapiVerb = new XApiVerb(); |
||
682 | xapiVerb.setId(caliperActionToXapiVerb(action)); |
||
683 | statement.setVerb(xapiVerb); |
||
684 | } |
||
685 | // ACTION END |
||
686 | |||
687 | // OBJECT |
||
688 | Entity object = event.getObject(); |
||
689 | if (object != null) { |
||
690 | |||
691 | XApiObject xapiObject = new XApiObject(); |
||
692 | XApiObjectDefinition xapiObjectDefinition = new XApiObjectDefinition(); |
||
693 | |||
694 | String name = object.getName(); |
||
695 | if (StringUtils.isNotBlank(name)) { |
||
696 | xapiObjectDefinition.setName(Collections.singletonMap("en", name)); |
||
697 | } |
||
698 | |||
699 | String description = object.getDescription(); |
||
700 | if (StringUtils.isNotBlank(description)) { |
||
701 | xapiObjectDefinition.setDescription(Collections.singletonMap("en", description)); |
||
702 | } |
||
703 | |||
704 | xapiObjectDefinition.setType(caliperEntityTypeToXapiObjectType(object.getType())); |
||
705 | |||
706 | Map<String,String> extensions = object.getExtensions(); |
||
707 | if (extensions != null && !extensions.isEmpty()) { |
||
708 | Map<URI, java.lang.Object> xapiExtensions = new HashMap<>(); |
||
709 | for (String key : extensions.keySet()) { |
||
710 | xapiExtensions.put(new URI(key), extensions.get(key)); |
||
711 | } |
||
712 | xapiObjectDefinition.setExtensions(xapiExtensions); |
||
713 | } |
||
714 | |||
715 | xapiObject.setDefinition(xapiObjectDefinition); |
||
716 | xapiObject.setId(object.getId()); |
||
717 | statement.setObject(xapiObject); |
||
718 | } |
||
719 | // OBJECT END |
||
720 | |||
721 | Group group = event.getGroup(); |
||
722 | if (group != null) { |
||
723 | XApiContext xapiContext = new XApiContext(); |
||
724 | |||
725 | Map<String,String> contextExtensions = group.getExtensions(); |
||
726 | if (contextExtensions != null && !contextExtensions.isEmpty()) { |
||
727 | Map<URI,java.lang.Object> extensions = new HashMap<>(); |
||
728 | for (String key : contextExtensions.keySet()) { |
||
729 | extensions.put(new URI(key), extensions.get(key)); |
||
730 | } |
||
731 | xapiContext.setExtensions(extensions); |
||
732 | } |
||
733 | |||
734 | XApiContextActivities xapiContextActivities = new XApiContextActivities(); |
||
735 | XApiObject grouping = new XApiObject(); |
||
736 | grouping.setId(group.getId()); |
||
737 | xapiContextActivities.setGrouping(Collections.singletonList(grouping)); |
||
738 | |||
739 | xapiContext.setContextActivities(xapiContextActivities); |
||
740 | statement.setContext(xapiContext); |
||
741 | |||
742 | } |
||
743 | |||
744 | return statement; |
||
745 | } |
||
746 | |||
747 | private String xApiVerbToCaliperAction(String xapiVerbId) { |
||
748 | Action caliperAction = verbActionMap.getKey(xapiVerbId); |
||
749 | if (caliperAction == null) { |
||
750 | return xapiVerbId; |
||
751 | } |
||
752 | |||
753 | return caliperAction.getValue(); |
||
754 | } |
||
755 | |||
756 | private String caliperActionToXapiVerb(String caliperAction) { |
||
757 | String verb = null; |
||
758 | try { |
||
759 | verb = verbActionMap.get(Action.valueOf(caliperAction)); |
||
760 | if (StringUtils.isBlank(verb)) { |
||
761 | return DEFAULT_XAPI_VERB; |
||
762 | } |
||
763 | |||
764 | } |
||
765 | catch (IllegalArgumentException e) { |
||
766 | return DEFAULT_XAPI_VERB; |
||
767 | } |
||
768 | |||
769 | return verb; |
||
770 | } |
||
771 | |||
772 | private String xapiObjectTypeToCaliperEntityType(String xapiType) { |
||
773 | |||
774 | if (StringUtils.isBlank(xapiType)) { |
||
775 | return EntityType.DIGITAL_RESOURCE.getValue(); |
||
776 | } |
||
777 | |||
778 | Type caliperType = objectEntityMap.getKey(xapiType); |
||
779 | if (caliperType == null) { |
||
780 | return xapiType; |
||
781 | } |
||
782 | |||
783 | return caliperType.getValue(); |
||
784 | } |
||
785 | |||
786 | private String caliperEntityTypeToXapiObjectType(String caliperType) { |
||
787 | String xapiType = objectEntityMap.get(caliperType); |
||
788 | if (StringUtils.isBlank(xapiType)) { |
||
789 | return caliperType; |
||
790 | } |
||
791 | |||
792 | return xapiType; |
||
793 | } |
||
794 | |||
795 | private String xapiToCaliperType(Statement statement) { |
||
796 | // default to event |
||
797 | String type = EventType.EVENT.getValue(); |
||
798 | // change if there is something more specific |
||
799 | XApiVerb xapiVerb = statement.getVerb(); |
||
800 | if (xapiVerb != null) { |
||
801 | String verbId = xapiVerb.getId(); |
||
802 | Action caliperAction = verbActionMap.getKey(verbId); |
||
803 | EventType eventType = actionEventMap.get(caliperAction); |
||
804 | if (eventType != null) { |
||
805 | type = eventType.getValue(); |
||
806 | } |
||
807 | } |
||
808 | |||
809 | return type; |
||
810 | } |
||
811 | |||
812 | private static final String ACTOR_TYPE_MBOX = "foaf:mbox"; |
||
813 | private static final String ACTOR_TYPE_OPENID = "http://openid.net/"; |
||
814 | private static final String ACTOR_TYPE_ACCOUNT = "https://github.com/adlnet/xAPI-Spec/blob/master/xAPI.md#agentaccount"; |
||
815 | |||
816 | private static final String DEFAULT_XAPI_VERB = "http://adlnet.gov/expapi/verbs/experienced"; |
||
817 | } |
||
818 |
The Java documentation explain EnumMap.