package org.netevolved.tincan; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.UUID; import java.util.concurrent.Executors; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.entermediadb.asset.MediaArchive; import org.ijsolutions.testbench.Question; import org.ijsolutions.testbench.Response; import org.ijsolutions.testbench.Test; import org.ijsolutions.testbench.TestBench; import org.ijsolutions.testbench.TestNode; import org.joda.time.DateTime; import org.joda.time.Period; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.openedit.CatalogEnabled; import org.openedit.Data; import org.openedit.ModuleManager; import org.openedit.OpenEditException; import org.openedit.WebPageRequest; import org.openedit.data.Searcher; import org.openedit.data.SearcherManager; import org.openedit.event.WebEvent; import org.openedit.users.User; import org.openedit.users.UserSearcher; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.JsonObject; import com.rusticisoftware.tincan.Activity; import com.rusticisoftware.tincan.ActivityDefinition; import com.rusticisoftware.tincan.Agent; import com.rusticisoftware.tincan.Context; import com.rusticisoftware.tincan.ContextActivities; import com.rusticisoftware.tincan.Extensions; import com.rusticisoftware.tincan.InteractionComponent; import com.rusticisoftware.tincan.InteractionType; import com.rusticisoftware.tincan.LanguageMap; import com.rusticisoftware.tincan.RemoteLRS; import com.rusticisoftware.tincan.Result; import com.rusticisoftware.tincan.Score; import com.rusticisoftware.tincan.Statement; import com.rusticisoftware.tincan.StatementsResult; import com.rusticisoftware.tincan.TCAPIVersion; import com.rusticisoftware.tincan.Verb; import com.rusticisoftware.tincan.documents.StateDocument; import com.rusticisoftware.tincan.lrsresponses.LRSResponse; import com.rusticisoftware.tincan.lrsresponses.StateLRSResponse; import com.rusticisoftware.tincan.lrsresponses.StatementLRSResponse; import com.rusticisoftware.tincan.lrsresponses.StatementsResultLRSResponse; import com.rusticisoftware.tincan.v10x.StatementsQuery; public class TinCan implements CatalogEnabled { private static final Log log = LogFactory.getLog(TinCan.class); protected SearcherManager fieldSearcherManager; protected String fieldCatalogId; protected ModuleManager fieldModuleManager; protected RemoteLRS fieldLrs; public RemoteLRS getLrs() { if (fieldLrs == null) { fieldLrs = new RemoteLRS(TCAPIVersion.V100); try { TestBench bench = getTestBench(); String setting = bench.getSetting("lrs-endpoint"); fieldLrs.setEndpoint(setting); fieldLrs.setUsername(bench.getSetting("lrs-username")); fieldLrs.setPassword(bench.getSetting("lrs-password")); String setting2 = bench.getSetting("lrs-auth"); fieldLrs.setAuth(setting2); fieldLrs.setVersion(TCAPIVersion.V100); } catch (MalformedURLException e) { throw new OpenEditException(e); } } return fieldLrs; } public SearcherManager getSearcherManager() { return fieldSearcherManager; } public void setSearcherManager(SearcherManager inSearcherManager) { fieldSearcherManager = inSearcherManager; } public String getCatalogId() { return fieldCatalogId; } public void setCatalogId(String inCatalogId) { fieldCatalogId = inCatalogId; } public ModuleManager getModuleManager() { return fieldModuleManager; } public void setModuleManager(ModuleManager inModuleManager) { fieldModuleManager = inModuleManager; } protected MediaArchive getMediaArchive() { MediaArchive archive = (MediaArchive) getModuleManager().getBean(getCatalogId(), "mediaArchive"); return archive; } protected TestBench getTestBench() { TestBench archive = (TestBench) getModuleManager().getBean(getCatalogId(), "testBench"); return archive; } public StatementsResult getStatements(Date since, String inURL) throws Exception { StatementsQuery query = new StatementsQuery(); query.setSince(new DateTime(since)); query.setUntil(new DateTime(new Date())); query.setLimit(1000); StatementsResultLRSResponse lrsResponse = null; if (inURL == null) { lrsResponse = getLrs().queryStatements(query); } else { lrsResponse = getLrs().moreStatements(inURL); } if (lrsResponse.getSuccess()) { StatementsResult result = lrsResponse.getContent(); return result; } else { //log.info("Error in statements found, trying to fix: " + lrsResponse.getErrMsg()); String response = new String(lrsResponse.getResponse().getContentBytes()); response = response.replaceAll("\"http://\"", "\"http://asdf\""); ObjectMapper mapper = new ObjectMapper(); JsonNode actualObj = mapper.readTree(response); return new StatementsResult(actualObj); } } public void composeStatement(User inUser, String inVerb, String inActivityId, WebEvent inEvent, boolean localonly) { Map properties = inEvent.getProperties(); log.info("About to send statement for " + inUser + inVerb + inActivityId); //TinCanIDAssistant assistant = (TinCanIDAssistant)inEvent.getValue("assistant"); Executors.newSingleThreadExecutor().execute(new Runnable() { @Override public void run() { try { Statement statement = new Statement(); Agent agent = new Agent(); agent.setMbox("mailto:" + inUser.getEmail()); agent.setName(inUser.getName()); Verb verb = new Verb(inVerb); String verbdisplay = properties.get("verb_display"); if (verbdisplay != null) { verb.setDisplay(new LanguageMap()); verb.getDisplay().put("en", verbdisplay); } Activity activity = new Activity(inActivityId); String activityname = properties.get("name"); ActivityDefinition definition = new ActivityDefinition(); activity.setDefinition(definition); definition.setName(new LanguageMap()); if (activityname != null) { definition.getName().put("en-US", activityname); } Context context = new Context(); Extensions extensions = new Extensions(); ObjectMapper mapper = new ObjectMapper(); HashMap extensionmap = new HashMap(); ArrayList keystoexclude = new ArrayList(); keystoexclude.add("verb_display"); keystoexclude.add("verb"); keystoexclude.add("name"); keystoexclude.add("activity"); for (Iterator iterator = properties.keySet().iterator(); iterator.hasNext();) { String key = (String) iterator.next(); if (keystoexclude.contains(key)) { continue; } Object val = properties.get(key); if (val instanceof String) { String value = properties.get(key); extensionmap.put("http://netevolved.com/testbench/tincan/extensions/" + key, value); } } JsonNode jnode = mapper.valueToTree(extensionmap); extensions.put("http://netevolved.com/testbench/tincan/extensions/", jnode); context.setExtensions(extensions); statement.setActor(agent); statement.setVerb(verb); statement.setObject(activity); statement.setContext(context); Date timestamp = inEvent.getDate("timestamp"); if (timestamp != null) { DateTime stamp = new DateTime(timestamp); statement.setTimestamp(stamp); statement.setStored(stamp); } Object source = inEvent.getSource(); if (source instanceof Test) { handleTest(inEvent, statement); //TODO use assistant to get correct parent (ie course, class...) id and set on statement context or wherever it needs to go } if (source instanceof TestNode) { handleTestQuestion(inEvent, statement); } Searcher searcher = getTestBench().getSearcher("lrsactivities"); StatementLRSResponse lrsRes = getLrs().saveStatement(statement); if (lrsRes.getSuccess()) { log.info("sent successfully"); getMediaArchive().fireSharedMediaEvent("tincan/retrievestatements"); } else { log.info("send failed!"); //log.info(lrsRes.getResponse().getContent()); log.info(lrsRes.getErrMsg()); ArrayList statements = new ArrayList(); statements.add(statement); //processStatements(getTestBench(), searcher, statements); } } catch (IllegalArgumentException e) { throw new OpenEditException(e); } catch (URISyntaxException e) { throw new OpenEditException(e); } } }); } protected void handleTestQuestion(WebEvent inEvent, Statement statement) { TestNode node = (TestNode) inEvent.getSource(); TestBench testbench = getTestBench(); //Test test = node.getTest(); ArrayList choices = new ArrayList(); Question question = node.getQuestion(); List responses = question.getResponses(); for (Iterator iterator = responses.iterator(); iterator.hasNext();) { Response response = (Response) iterator.next(); InteractionComponent component = new InteractionComponent(); component.setId(response.getId()); LanguageMap map = new LanguageMap(); map.put("en-US", response.getText()); component.setDescription(map); choices.add(component); } ActivityDefinition definition = ((Activity) statement.getObject()).getDefinition(); definition.setChoices(choices); //def is the activity definition defined above ArrayList correctResponsesPattern = new ArrayList(); LanguageMap questionname = definition.getName(); String questiontext = testbench.clean(question.getText(), 40); if (question.hasCorrectResponse()) { correctResponsesPattern.add(question.getCorrectResponse().getId()); definition.setCorrectResponsesPattern(correctResponsesPattern); } if ("multiplechoice".equals(question.get("questiontype"))) { definition.setInteractionType(InteractionType.CHOICE); if (questiontext == null || questiontext.length() == 0) { questiontext = "multiple choice"; } } else if ("dropdown".equals(question.get("questiontype"))) { definition.setInteractionType(InteractionType.CHOICE); if (questiontext == null || questiontext.length() == 0) { questiontext = "dropdown"; } } else if ("textfield".equals(question.get("questiontype"))) { definition.setInteractionType(InteractionType.FILL_IN); if (questiontext == null || questiontext.length() == 0) { questiontext = "text field"; } } else if ("longanswer".equals(question.get("questiontype"))) { definition.setInteractionType(InteractionType.LONG_FILL_IN); if (questiontext == null || questiontext.length() == 0) { questiontext = "long answer"; } } else { definition.setInteractionType(InteractionType.OTHER); if (questiontext == null || questiontext.length() == 0) { questiontext = "other quesiton type"; } } questionname.put("en-US", questiontext); Result questionresult = new Result(); questionresult.setSuccess(node.wasUserCorrect()); Period period = new Period(0, 0, node.getTimeSpentOnQuestion(), 0); questionresult.setDuration(period); if (node.getResponse() != null) { questionresult.setResponse(node.getResponse().getId()); } statement.setResult(questionresult); } protected void handleTest(WebEvent inEvent, Statement statement) { Test test = (Test) inEvent.getSource(); inEvent.setProperty("learningGroup", test.get("class")); inEvent.setProperty("testplan", test.get("plan")); inEvent.setProperty("course", test.get("course")); Score score = new Score(); score.setRaw(Double.valueOf(test.getCorrectResponses().size())); score.setScaled(test.getPercentageScore()); score.setMax(Double.valueOf(test.getNodes().size())); score.setMin(0.0); Result result = new Result(); result.setScore(score); result.setSuccess(test.isTestPassed()); result.setCompletion(test.isComplete()); Period period = new Period(0, 0, test.getTotalSeconds(), 0); result.setDuration(period); statement.setResult(result); } public void logEvent(WebEvent inEvent) { String inVerb = inEvent.get("verb"); String activityid = inEvent.get("activity"); composeStatement(inEvent.getUser(), inVerb, activityid, inEvent, !isConnected()); } private boolean isConnected() { TestBench bench = getTestBench(); String setting = bench.getSetting("lrs-endpoint"); return setting != null; } public String getLaunchParams(WebPageRequest inReq, User inUser, String inTestPlan) throws Exception { return getLaunchParams(inReq, inUser, inTestPlan, false); } public String getLaunchParams(WebPageRequest inReq, User inUser, String inTestPlan, boolean includeRegistration) throws Exception { TestBench bench = getTestBench(); String endpoint = bench.getSetting("lrs-endpoint"); if (endpoint == null || endpoint.length() == 0) { endpoint = "/" + bench.getApplicationId() + "/xAPI"; } String classid = inReq.findValue("classid"); String courseid = inReq.findValue("courseid"); String auth = bench.getSetting("lrs-auth"); StringBuffer buffer = new StringBuffer(); buffer.append("?endpoint="); buffer.append(endpoint); buffer.append("&auth="); buffer.append(auth); JsonObject actor = new JsonObject(); actor.addProperty("mbox", "mailto:" + inUser.getEmail()); actor.addProperty("name", inUser.getName()); buffer.append("&actor="); buffer.append(URLEncoder.encode(actor.toString(), "UTF-8")); buffer.append("&activityid="); String activityid = getActivityUri(classid, courseid, inTestPlan); buffer.append(URLEncoder.encode(activityid, "UTF-8")); //buffer.append("&context="); if (includeRegistration) { Data registration = bench.getSearcher("registration").query().exact("user", inUser.getId()).exact("testplan", inTestPlan).searchOne(); if (registration == null) { registration = bench.getSearcher("registration").createNewData(); registration.setValue("user", inUser.getId()); registration.setValue("testplan", inTestPlan); String registrationid = UUID.randomUUID().toString(); registration.setProperty("registrationuuid", registrationid); registration.setValue("class", classid); registration.setValue("course", courseid); bench.getSearcher("registration").saveData(registration); } buffer.append("®istration="); buffer.append(registration.get("registrationuuid")); } String finalurl = buffer.toString(); return finalurl; //#set($tincan = "?endpoint=http://34.227.195.177/data/xAPI/&auth=$testbench.getSetting("lrs-auth")&actor=%7B%22mbox%22%3A%22mailto%3A$user.email%22%2C%20%22name%22%3A%22$user%22%7D&activityid=5dkde6RVDlH_course_id®istration=760e3480-ba55-4991-94b0-01820dbd23a2") } public void processStatements(TestBench inBench, Searcher searcher, ArrayList statementList, HashMap extradetails) throws URISyntaxException { ArrayList tosave = new ArrayList(); for (Iterator iterator = statementList.iterator(); iterator.hasNext();) { Statement statement = (Statement) iterator.next(); UUID currentid = statement.getId(); if (currentid == null) { currentid = UUID.randomUUID(); } String uuid = currentid.toString(); Data data = (Data) searcher.searchById(uuid); if (data == null) { data = searcher.createNewData(); data.setId(uuid); //could be null } DateTime timestamp = statement.getTimestamp(); if (timestamp == null) { timestamp = new DateTime(new Date()); } Date date = new Date(timestamp.getMillis()); data.setValue("date", date); data.setValue("UUID", uuid); getActorData(inBench, statement.getActor(), data, ""); //grab tin can Actor information from the LRS getVerbData(inBench, statement.getVerb(), data); //grab tin can verb info from LRS getObjectData((Activity) statement.getObject(), data); //get tin can object activity info from LRS getContextData((Context) statement.getContext(), data, inBench); //get tin can context info from LRS, which will be testbench specific data getResultsData(inBench, statement, data); //grab results and duration //grab entire statement JSON code String source = statement.toJSON().toString(); data.setValue("src", source); if (statement.getContext() != null && statement.getContext().getRegistration() != null) { String registrationid = statement.getContext().getRegistration().toString(); log.info("Reg ID WAS " + registrationid); Data registration = inBench.getSearcher("registration").query().match("registrationuuid", registrationid).searchOne(); if (registration != null) { extradetails.put("learningGroup", registration.getValue("class")); extradetails.put("testplan", registration.getValue("testplan")); extradetails.put("course", registration.getValue("course")); extradetails.put("registrationuuid", registration.getValue("registrationid")); // log.info("Reg ID WAS FOUND"); } else { // log.info("No registration data was found"); } } for (Iterator iterator2 = extradetails.keySet().iterator(); iterator2.hasNext();) { String key = (String) iterator2.next(); Object val = extradetails.get(key); data.setValue(key, val); } tosave.add(data); // searcher.saveData(data); } log.info(tosave.size()); searcher.saveAllData(tosave, null); } public void getActorData(TestBench bench, Agent inActor, Data data, String inPrefix) { String mbox = inActor.getMbox(); String email = mbox.substring(mbox.indexOf(":") + 1, mbox.length()); UserSearcher searcher = (UserSearcher) bench.getSearcher("user"); String actorname = inActor.getName(); User user = bench.getUserManager().getUserByEmail(email); if (user != null) { data.setValue(inPrefix + "userid", user.getId()); } else { user = (User) searcher.createNewData(); user.setEmail(email); user.setName(actorname); //bench.getUserManager().saveUser(user); searcher.saveData(user); } data.setValue(inPrefix + "email", email); String actortype = inActor.getObjectType(); //would normally expect type to be Agent. Could also be Group... //TODO handle when Actor type is Group data.setValue(inPrefix + "actortype", actortype); data.setValue(inPrefix + "actorname", actorname); } public void getVerbData(TestBench inBench, Verb inVerb, Data data) { String verbid = inVerb.getId().toString(); String verbdisplay = null; if (inVerb.getDisplay() != null) { verbdisplay = extractValueInfo(inVerb.getDisplay()); } if (verbdisplay == null) { verbdisplay = "no verb display"; } data.setValue("verb", verbdisplay); data.setValue("verbid", verbid); Searcher verbsearcher = inBench.getSearcher("tincanverb"); Data verbdata = (Data) verbsearcher.searchById(verbid); if (verbdata == null) { verbdata = verbsearcher.createNewData(); verbdata.setId(verbid); verbdata.setName(verbdisplay); verbsearcher.saveData(verbdata); } } public void getObjectData(Activity inActivity, Data data) { String objecttype = inActivity.getObjectType(); //this expects object type to be Activity. Could also be Agent/Group, StatementRef, or SubStatement... data.setValue("objecttype", objecttype); //TODO handle other types of Objects other than Activity... https://xapi.com/blog/deep-dive-object/ String activitydefinition = null; String activitydescription = null; if (inActivity != null && inActivity.getDefinition() != null) { activitydefinition = extractValueInfo(inActivity.getDefinition().getName()); activitydescription = extractValueInfo(inActivity.getDefinition().getDescription()); Extensions activityextensions = inActivity.getDefinition().getExtensions(); if (activityextensions != null) { try { handleExtensions(data, activityextensions); } catch (URISyntaxException e) { log.info("Invalid URI for Activity Extensions"); } } } data.setValue("activity", inActivity.getId().toString()); data.setValue("activitydefinition", activitydefinition); data.setValue("activitydescription", activitydescription); } public void getContextData(Context inContext, Data data, TestBench inBench) throws URISyntaxException { if (inContext != null) { Extensions extensions = inContext.getExtensions(); if (extensions != null) { handleExtensions(data, extensions); } Agent instructor = inContext.getInstructor(); if (instructor != null) { getActorData(inBench, instructor, data, "instructor "); //grab tin can Actor information about the instructor from the LRS } ContextActivities contextactivities = inContext.getContextActivities(); if (contextactivities != null) { List parents = contextactivities.getParent(); if (parents != null) { List parentids = new ArrayList(); for (Iterator iterator = parents.iterator(); iterator.hasNext();) { Activity activity = (Activity) iterator.next(); parentids.add(activity.getId().toString()); } data.setValue("parentids", parentids); } List groupings = contextactivities.getGrouping(); if (groupings != null) { List groupingids = new ArrayList(); for (Iterator iterator = groupings.iterator(); iterator.hasNext();) { Activity activity = (Activity) iterator.next(); groupingids.add(activity.getId().toString()); } data.setValue("groupingids", groupingids); } //TODO handle more contextactivity properties if necessary, ie category, other... https://xapi.com/blog/deep-dive-activity/ and https://xapi.com/blog/deep-dive-context/ } } } public void getResultsData(TestBench inBench, Statement statement, Data data) throws URISyntaxException { Result result = statement.getResult(); if (result != null) { Boolean success = result.getSuccess(); if (success != null) { data.setValue("success", success); } Boolean completion = result.getCompletion(); if (completion != null) { data.setValue("completion", completion); } Period period = result.getDuration(); if (period != null) { data.setValue("period", period.toStandardSeconds().getSeconds()); } String response = result.getResponse(); if (response != null) { data.setValue("response", response); } Score score = result.getScore(); if (score != null) { data.setValue("scaled", score.getScaled()); //If the ‘raw’ value can be calculated as a percentage, then it is expected that the ‘scaled’ value is populated with a value between -1 and 1. data.setValue("raw", score.getRaw()); //The ‘raw’ value corresponds to a nominal value and when they are provided it must be between the values of ‘min’ and ‘max.’ data.setValue("min", score.getMin()); //‘min’ and ‘max,’ correspond to nominal values marking the starting and ending point of a range of values. data.setValue("max", score.getMax()); //For example, on a 25 question quiz a user may get 20 questions correct, corresponding to a ‘raw’ value of ‘20’ and a scaled value of ‘0.8’ (or 80%), ‘min’ could be included as ‘0’ and ‘max’ as ‘25.’ } Extensions extensions = statement.getResult().getExtensions(); if (extensions != null) { handleExtensions(data, extensions); } //Extensions on a result could be used for determining things like progress and ending position or rank. } } //grab testbench specific information from the LRS public void handleExtensions(Data data, Extensions extensions) throws URISyntaxException { JsonNode node = extensions.get("http://netevolved.com/testbench/tincan/extensions/"); ObjectMapper mapper = new ObjectMapper(); Map result = mapper.convertValue(node, new TypeReference>() { }); if (result != null) { for (Iterator iterator2 = result.keySet().iterator(); iterator2.hasNext();) { String key = (String) iterator2.next(); String value = (String) result.get(key); key = key.substring(key.lastIndexOf("/") + 1, key.length()); //strips URI field required by tin can from our data data.setValue(key, value); } } } public String extractValueInfo(LanguageMap map) { if (map == null) { return null; } String value = map.get("en"); if (value == null) { value = map.get("en-US"); } if (value == null) { value = map.get("und"); } if (value == null) { for (Iterator iterator = map.iterator(); iterator.hasNext();) { Entry entry = (Entry) iterator.next(); value = entry.getValue(); break; } } //TODO look for any other value that map could have return value; } public JSONObject getState(User inUser, String inActivityId, String statetype) throws Exception { Activity activity = new Activity(); activity.setId(inActivityId); Agent agent = new Agent(); agent.setMbox("mailto:" + inUser.getEmail()); agent.setName(inUser.getName()); StateLRSResponse stateResponse = getLrs().retrieveState(statetype, activity, agent, null); StateDocument statedoc = stateResponse.getContent(); if (statedoc == null) { return null; } String statedata = new String(statedoc.getContent()); JSONParser parser = new JSONParser(); JSONObject obj = (JSONObject) parser.parse(statedata); return obj; } public void restoreState(String inStateId) throws Exception { Data oldstate = getTestBench().getData("lrsstate", inStateId); User user = getTestBench().getUserManager().getUser(oldstate.get("user")); log.info("Found oldstate" + oldstate); Activity activity = new Activity(); activity.setId(oldstate.get("activityid")); Agent agent = new Agent(); agent.setMbox("mailto:" + user.getEmail()); agent.setName(user.getName()); StateLRSResponse stateResponse = getLrs().retrieveState("suspend_data", activity, agent, null); StateDocument statedoc = stateResponse.getContent(); if (statedoc == null) { log.info("No suspend data was found, trying to create a new one"); statedoc = new StateDocument(); statedoc.setActivity(activity); statedoc.setAgent(agent); statedoc.setContentType("suspend_data"); } String statedata = oldstate.get("statedata"); statedoc.setContent(statedata.getBytes()); getLrs().saveState(statedoc); } public void clearState(User inUser, String inActivityId) throws Exception { Activity activity = new Activity(); activity.setId(inActivityId); Agent agent = new Agent(); agent.setMbox("mailto:" + inUser.getEmail()); agent.setName(inUser.getName()); LRSResponse stateResponse = getLrs().clearState(activity, agent, null); } public JSONObject getLessonData(JSONObject object) throws Exception { JSONParser parser = new JSONParser(); JSONArray array = (JSONArray) object.get("d"); ArrayList ints = new ArrayList(); for (Iterator iterator = array.iterator(); iterator.hasNext();) { Long l = (Long) iterator.next(); ints.add(Math.toIntExact(l)); } String json = Lzw.decompress(ints); JSONObject progressmap = (JSONObject) parser.parse(json); //log.info(json); return progressmap; } public int getTotalCompletedLessons(User inUser, String inActivityId) throws Exception { Activity activity = new Activity(); activity.setId(inActivityId); Agent agent = new Agent(); agent.setMbox("mailto:" + inUser.getEmail()); agent.setName(inUser.getName()); StateLRSResponse stateResponse = getLrs().retrieveState("suspend_data", activity, agent, null); StateDocument statedoc = stateResponse.getContent(); if (statedoc == null) { return 0; } String statedata = new String(statedoc.getContent()); JSONParser parser = new JSONParser(); JSONObject obj = (JSONObject) parser.parse(statedata); JSONArray array = (JSONArray) obj.get("d"); ArrayList ints = new ArrayList(); for (Iterator iterator = array.iterator(); iterator.hasNext();) { Long l = (Long) iterator.next(); ints.add(Math.toIntExact(l)); } String json = Lzw.decompress(ints); JSONObject progressmap = (JSONObject) parser.parse(json); //log.info(json); JSONObject progress = (JSONObject) progressmap.get("progress"); JSONObject lessons = (JSONObject) progress.get("lessons"); //log.info(lessons); Set keySet = lessons.keySet(); int size = keySet.size(); return size; } public void updateBookmark(User inUser, String inActivityId, int jump) throws Exception { JSONObject inode = createInode(); Activity activity = new Activity(); activity.setId(inActivityId); Agent agent = new Agent(); agent.setMbox("mailto:" + inUser.getEmail()); agent.setName(inUser.getName()); StateLRSResponse stateResponse = getLrs().retrieveState("suspend_data", activity, agent, null); StateDocument statedoc = stateResponse.getContent(); String statedata = new String(statedoc.getContent()); JSONParser parser = new JSONParser(); JSONObject obj = (JSONObject) parser.parse(statedata); JSONArray array = (JSONArray) obj.get("d"); ArrayList ints = new ArrayList(); for (Iterator iterator = array.iterator(); iterator.hasNext();) { Long l = (Long) iterator.next(); ints.add(Math.toIntExact(l)); } String json = Lzw.decompress(ints); JSONObject progressmap = (JSONObject) parser.parse(json); log.info(json); JSONObject progress = (JSONObject) progressmap.get("progress"); JSONObject lessons = (JSONObject) progress.get("lessons"); ArrayList toremove = new ArrayList(); for (Iterator iterator = lessons.keySet().iterator(); iterator.hasNext();) { String key = (String) iterator.next(); int keyint = new Integer(key); if (keyint >= jump) { toremove.add(key); } } for (Iterator iterator = toremove.iterator(); iterator.hasNext();) { String key = (String) iterator.next(); lessons.remove(key); } for (int i = 0; i < 100; i++) { JSONObject extra = new JSONObject(); extra.put("c", 1); extra.put("p", 100); extra.put("i", inode); lessons.put(i, extra); } String toencode = progressmap.toJSONString(); List encoded = Lzw.compress(toencode); obj.put("d", encoded); statedoc.setContent(obj.toJSONString().getBytes()); agent.setMbox("mailto:brekke@netevolved.com"); agent.setName("Brekke Beyer"); statedoc.setAgent(agent); //getLrs().saveState(statedoc); } private JSONObject createInode() { JSONObject object = new JSONObject(); for (int i = 0; i < 40; i++) { JSONObject node = new JSONObject(); node.put("c", 1); object.put(i, node);//0, 1, 2... top level JSONObject sub = new JSONObject(); node.put("i", sub); //sub.put("c", 1); for (int j = 0; j < 40; j++) { JSONObject lowest = new JSONObject(); lowest.put("c", 1); sub.put(j, lowest); } } return object; // { // "0": { // "c": 1, // "i": { // "0": { // "c": 1 // } // } // }, // "1": { // "c": 1, // "i": { // "0": { // "c": 1 // } // } // }, } public String getProgress(String userid, String inGroupId, String inCourseId, String inPlanId) { String uri = getActivityUri(inGroupId, inCourseId, inPlanId); Searcher statesearcher = getTestBench().getSearcher("lrsstate"); Data oldstate = statesearcher.query().exact("testplan", inPlanId).exact("user", userid).sort("dateDown").searchOne(); log.info("Search found" + oldstate); log.info("planid: " + inPlanId); if (oldstate != null) { return oldstate.get("progress"); } else { return "0"; } } public String getActivityUri(String inGroupId, String inCourseId, String inPlanId) { return "http://classes/" + inGroupId + "/courses/" + inCourseId + "/" + inPlanId; } }