| 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.