/// <summary> /// Creates a new <Typ>ExecuteNavigator</Typ> object in memory and its representation in the database. /// </summary> /// <param name="store">A <Typ>LearningStore</Typ> object that references the database to use.</param> /// <param name="rootActivityId">The database identifier for the root activity (i.e. organization) of the activity tree to attempt.</param> /// <param name="learnerId">The database identifier for the learner information.</param> /// <param name="learnerAssignmentId">The database identifier for the learner assignment information. Only used in SLK.</param> /// <param name="loggingFlags">Flags specifying which actions to log.</param> /// <returns>A new <Typ>ExecuteNavigator</Typ> object.</returns> /// <exception cref="LearningStoreItemNotFoundException">Thrown if the learner ID or root activity ID is invalid, or if the package information cannot be found.</exception> static public ExecuteNavigator CreateExecuteNavigator(LearningStore store, long rootActivityId, long learnerId, long learnerAssignmentId, LoggingOptions loggingFlags) { ExecuteNavigator eNav = new ExecuteNavigator(); LearningStoreJob job = store.CreateJob(); // first add security Dictionary <string, object> securityParameters = new Dictionary <string, object>(); securityParameters[Schema.CreateAttemptRight.RootActivityId] = new LearningStoreItemIdentifier(Schema.ActivityPackageItem.ItemTypeName, rootActivityId); securityParameters[Schema.CreateAttemptRight.LearnerId] = new LearningStoreItemIdentifier(Schema.UserItem.ItemTypeName, learnerId); job.DemandRight(Schema.CreateAttemptRight.RightName, securityParameters); job.DisableFollowingSecurityChecks(); // first query for all information about the learner LearningStoreQuery query = store.CreateQuery(Schema.UserItem.ItemTypeName); query.AddColumn(Schema.UserItem.Id); query.AddColumn(Schema.UserItem.Name); query.AddColumn(Schema.UserItem.Language); query.AddColumn(Schema.UserItem.AudioCaptioning); query.AddColumn(Schema.UserItem.AudioLevel); query.AddColumn(Schema.UserItem.DeliverySpeed); query.AddCondition(Schema.UserItem.Id, LearningStoreConditionOperator.Equal, new LearningStoreItemIdentifier(Schema.UserItem.ItemTypeName, learnerId)); job.PerformQuery(query); // then query the package information query = store.CreateQuery(Schema.SeqNavActivityPackageView.ViewName); LearningStoreItemIdentifier rootId = new LearningStoreItemIdentifier(Schema.ActivityPackageItem.ItemTypeName, rootActivityId); query.AddColumn(Schema.SeqNavActivityPackageView.PackageId); query.AddColumn(Schema.SeqNavActivityPackageView.PackageFormat); query.AddColumn(Schema.SeqNavActivityPackageView.PackagePath); query.AddCondition(Schema.SeqNavActivityPackageView.Id, LearningStoreConditionOperator.Equal, rootId); job.PerformQuery(query); // then query for the activity tree query = store.CreateQuery(Schema.SeqNavActivityTreeView.ViewName); query.AddColumn(Schema.SeqNavActivityTreeView.DataModelCache); query.AddColumn(Schema.SeqNavActivityTreeView.ParentActivityId); query.AddColumn(Schema.SeqNavActivityTreeView.Id); query.AddColumn(Schema.SeqNavActivityTreeView.ObjectivesGlobalToSystem); query.AddCondition(Schema.SeqNavActivityTreeView.RootActivityId, LearningStoreConditionOperator.Equal, new LearningStoreItemIdentifier(Schema.ActivityPackageItem.ItemTypeName, rootActivityId)); job.PerformQuery(query); DataTable d; // used to store results of query ReadOnlyCollection <object> ids; // for this transaction we need to read from the activitypackageitem table and write new records to // the activityattemptitem and attemptitem tables TransactionOptions options = new TransactionOptions(); options.IsolationLevel = System.Transactions.IsolationLevel.RepeatableRead; using (LearningStoreTransactionScope scope = new LearningStoreTransactionScope(options)) { // execute the query ReadOnlyCollection <object> c = job.Execute(); Utilities.Assert(c.Count == 3); if (((DataTable)c[0]).Rows.Count < 1) { throw new LearningStoreItemNotFoundException(Resources.InvalidLearnerId); } if (((DataTable)c[1]).Rows.Count < 1) { throw new LearningStoreItemNotFoundException(Resources.InvalidRootActivityId); } if (((DataTable)c[2]).Rows.Count < 1) { throw new LearningStoreItemNotFoundException(Resources.InvalidRootActivityId); } d = (DataTable)c[0]; // save learner information string learnerName = (string)d.Rows[0][Schema.UserItem.Name]; AudioCaptioning learnerCaption = (AudioCaptioning)d.Rows[0][Schema.UserItem.AudioCaptioning]; float learnerAudioLevel = (float)d.Rows[0][Schema.UserItem.AudioLevel]; float learnerDeliverySpeed = (float)d.Rows[0][Schema.UserItem.DeliverySpeed]; string learnerLanguage = (string)d.Rows[0][Schema.UserItem.Language]; d = (DataTable)c[1]; // save package information // we need to create the activity tree within the transaction because it may affect the data written // by means of the selection and randomization processes. eNav.m_packageFormat = (PackageFormat)d.Rows[0][Schema.SeqNavActivityPackageView.PackageFormat]; eNav.m_packageId = ((LearningStoreItemIdentifier)d.Rows[0][Schema.SeqNavActivityPackageView.PackageId]).GetKey(); eNav.m_packageLocation = (string)d.Rows[0][Schema.SeqNavActivityPackageView.PackagePath]; eNav.m_store = store; eNav.m_learnerId = learnerId; eNav.m_loggingFlags = loggingFlags; // we must set this here so that the Activity constructor doesn't try and load missing info eNav.m_attemptItemInformationIsValid = true; d = (DataTable)c[2]; // save data to create activity tree later, when we are done with sql processing eNav.AddActivities(d, rootActivityId, learnerName, learnerLanguage, learnerCaption, learnerAudioLevel, learnerDeliverySpeed); /* * // create activity objects and store them in a big dictionary * foreach(DataRow row in d.Rows) * { * Activity act = new Activity(eNav, ((LearningStoreItemIdentifier)row[Schema.SeqNavActivityTreeView.Id]).GetKey(), * ConvertLearningStoreXmlToXPathNavigator(row[Schema.SeqNavActivityTreeView.DataModelCache] as LearningStoreXml, true), * null, null, null, eNav.WrapAttachment, eNav.WrapAttachmentGuid, -1, * (bool)row[Schema.SeqNavActivityTreeView.ObjectivesGlobalToSystem], eNav.m_learnerId.ToString(System.Globalization.CultureInfo.InvariantCulture), * learnerName, learnerLanguage, learnerCaption, learnerAudioLevel, learnerDeliverySpeed); * eNav.m_activities[act.ActivityId] = act; * if(act.ActivityId == rootActivityId) * { * eNav.RootActivity = act; * if(!(row[Schema.SeqNavActivityTreeView.ParentActivityId] is DBNull)) * { * throw new LearningStoreItemNotFoundException(Resources.InvalidRootActivityId); * } * } * } * * // now that we have all the activities in a big dictionary, find all the parents to build the tree * // in theory this could be done in the first loop if I sort the query by parentid, but that's making a lot * // of assumptions about the structure of the database that I don't think are safe to make * foreach(DataRow row in d.Rows) * { * LearningStoreItemIdentifier parentId = row[Schema.SeqNavActivityTreeView.ParentActivityId] as LearningStoreItemIdentifier; * if (parentId != null) * { * long id = ((LearningStoreItemIdentifier)row[Schema.SeqNavActivityTreeView.Id]).GetKey(); * eNav.m_activities[id].Parent = eNav.m_activities[parentId.GetKey()]; * eNav.m_activities[parentId.GetKey()].AddChild(eNav.m_activities[id]); * } * } * * // make sure children of each parent are in the right order, and set Previous and Next pointer correctly. * eNav.SortActivityTree(); * * // step through the activity tree searching for selection and randomization instructions * // this must be done before the ActivityAttemptItems are saved because they may reorder * // and potentially even remove children. * Random rand = new Random(); * foreach(Activity a in eNav.Traverse) * { * if(!a.IsLeaf) * { * // if there's a valid selection instruction, remove excess * // child activities randomly * if(a.Sequencing.SelectionTiming == RandomizationTiming.Once && * a.Sequencing.RandomizationSelectCount > 0 && * a.Sequencing.RandomizationSelectCount < a.Children.Count) * { * int selCount = a.Sequencing.RandomizationSelectCount; * for(int i = 0 ; i < a.Children.Count ; ++i) * { * if(rand.Next(a.Children.Count) < selCount) * { * --selCount; * } * else * { * a.RemoveChild(i); * } * } * a.SortChildren(); * } * * // if there's a valid randomization instruction, randomize order of the children * // of this activity * if((a.Sequencing.RandomizationTiming == RandomizationTiming.Once || * a.Sequencing.RandomizationTiming == RandomizationTiming.OnEachNewAttempt) && * a.Sequencing.ReorderChildren) * { * List<Activity> randlist = new List<Activity>(); * while(a.Children.Count > 0) * { * int i = rand.Next(a.Children.Count); * randlist.Add((Activity)a.Children[i]); * a.RemoveChild(i); * } * for(int i = 0 ; i < randlist.Count ; ++i) * { * randlist[i].RandomPlacement = i; * a.AddChild(randlist[i]); * } * a.SortChildren(); * } * } * } * */ // create the attemptitem // fill in fields with no defaults job = store.CreateJob(); job.DisableFollowingSecurityChecks(); Dictionary <string, object> attempt = new Dictionary <string, object>(); attempt[Schema.AttemptItem.RootActivityId] = new LearningStoreItemIdentifier(Schema.ActivityPackageItem.ItemTypeName, rootActivityId); attempt[Schema.AttemptItem.LearnerId] = new LearningStoreItemIdentifier(Schema.UserItem.ItemTypeName, learnerId); if (learnerAssignmentId != 0) { attempt[Schema.AttemptItem.LearnerAssignmentId] = new LearningStoreItemIdentifier("LearnerAssignmentItem", learnerAssignmentId); } attempt[Schema.AttemptItem.PackageId] = new LearningStoreItemIdentifier(Schema.PackageItem.ItemTypeName, eNav.m_packageId); eNav.m_attemptStartTime = DateTime.UtcNow; attempt[Schema.AttemptItem.StartedTimestamp] = eNav.m_attemptStartTime; attempt[Schema.AttemptItem.LogDetailSequencing] = ((loggingFlags & LoggingOptions.LogDetailSequencing) == LoggingOptions.LogDetailSequencing); attempt[Schema.AttemptItem.LogFinalSequencing] = ((loggingFlags & LoggingOptions.LogFinalSequencing) == LoggingOptions.LogFinalSequencing); attempt[Schema.AttemptItem.LogRollup] = ((loggingFlags & LoggingOptions.LogRollup) == LoggingOptions.LogRollup); LearningStoreItemIdentifier attemptId = job.AddItem(Schema.AttemptItem.ItemTypeName, attempt, true); // create activityattemptitems for each activity in the tree foreach (Activity a in eNav.Traverse) { Dictionary <string, object> activity = new Dictionary <string, object>(); // fill in everything that has no default activity[Schema.ActivityAttemptItem.AttemptId] = attemptId; activity[Schema.ActivityAttemptItem.ActivityPackageId] = new LearningStoreItemIdentifier(Schema.ActivityPackageItem.ItemTypeName, a.ActivityId); if (a.RandomPlacement >= 0) { activity[Schema.ActivityAttemptItem.RandomPlacement] = a.RandomPlacement; // sibling activities are ordered by RandomPlacement first, // then by OriginalPlacment; RandomPlacement is -1 for all siblings if not randomization occurs } job.AddItem(Schema.ActivityAttemptItem.ItemTypeName, activity, true); } ids = job.Execute(); scope.Complete(); } // fill in some vital data for the navigator eNav.m_attemptId = ((LearningStoreItemIdentifier)ids[0]).GetKey(); int index = 1; foreach (Activity a in eNav.Traverse) { a.InternalActivityId = ((LearningStoreItemIdentifier)ids[index++]).GetKey(); } return(eNav); }