I am working on the SCORM 2004 4th edition based LMS, where i am at the initial stage.
Hence, i am reading SCORM based documents.
In the SCORM 2004 4th edition CAM document, i was stuck at the page CAM-3-37, where the element adlcp:data is defined as the container used to define sets of data shared associated with an
activity.
and the child element of adlcp:data i.e; map is defined as
The element is the container used to describe how an activity will utilize a specific
set of shared data.
I thought, I may understand it as I will move forward in the said book.
But, I completed the CAM book and yet i am unable to get it how those two tags work.
And also, let's take an example into the consideration, which is as follows:
<adlcp:data>
<adlcp:map targetID="com.scorm.golfsamples.sequencing.forcedsequential.notesStorage" readSharedData="true" writeSharedData="true"/>
</adlcp:data>
where, readSharedData attribute indicates that
currently available shared data will be utilized by the activity while it is active.
and writeSharedData attribute indicates that
shared data should be persisted (true or false) upon termination ( Terminate(“”)
) of the attempt on the activity.
Here in this case,
i didn't get what this targetID= com.scorm.golfsamples.sequencing.forcedsequential.notesStorage indicates.
i didn't get what is this shared data? and where is it located? what is it actually?
Can anyone help me in understanding the above described elements?
adlcp:data is a way to define space on the LMS to store information that doesn't fit in the CMI data model, or that you want to make accessible across SCOs.
There are 3 pieces to defining this space.
1. adlcp:sharedDataGlobalToSystem attribute on the element, which says whether shared data is available for one attempt or for every attempt (aka is it wiped out each time the learner takes the course).. See CAM-3-27
2. adlcp:data & adlcp:map elements list the space(s) you want made available for that SCO. You define an ID for each storage space, and then add access controls meaning whether or not the SCO can read or write to the storage space. (See CAM-3-37)
Those two set up the LMS storage and behaviors for each SCO in the content package.
The final piece is described in section 4.3 of the RTE book. To access the data storage spaces, you use the SCORM API GetValue and SetValue requests and the data model element adl.data.n.store.
One additional note, since the id order is not necessarily maintained, you will need to loop through the adl.data stores in the SCO and determine which index goes to which ID.
Tom Creightons answer is a very good explanation of the shared buckets implementation.
I am just adding a few pointers we found in our implentation.
The data saved is for the "learner", and can be accessed and set across different SCO's or courses assigned to the learner. Beware though, if you are using SCORM Cloud, Clear GLobals button will clear the data for all courses assigned to the user.
While Tom mentions that adlcp:sharedDataGlobalToSystem is attempt specific, SCORM Cloud support says that it is restricted to course/SCO. I have yet to get clarity on that.
There might be a limit on the number of buckets being saved. I have yet to confirm this and will update this reply shortly.
For those looking for more info on implementation:
Add this to your item (organisation > item) in the manifest:
<adlcp:data>
<adlcp:map targetID="mybucketname" readSharedData="true" writeSharedData="true"/>
</adlcp:data>
JS part (Use your API calls in place of LMSGetValue and LMSSetValue)
var dataBucketsCount = LMSGetValue("adl.data._count");
dataBucketsCount = parseInt(dataBucketsCount);
for (var i=0; i < dataBucketsCount; i++){
if (LMSGetValue("adl.data." + i + ".id") == "mybucketname"){
//do your processing with the data
}
}
I had to search a lot for this and try and fail multiple times until we got this right. So I have added this here, so in future it might help someone.
Related
I have a set of events that have been refactored to another package. This works as is until I execute the event replay. Digging deeper I noticed a payloadtype in the domainevententry table and figure changing this would be sufficient but alas it seems the xml root element of the event needs to be changed as well. I am hoping there is a simple way to do this.
I cannot find any examples on upcasting to different packages or using XStream aliasing so any help would be greatly appreciated.
Thanks
As you noticed, the default payload type stored in events is the fully qualified class name. This ensures that out of the box serialization and deserialization work as intended. However, moving classes around thus means the payload type can no longer be found, requiring some adjustment to be made.
You could have used the EventTypeUpcaster, as referred to in the Reference Guide. The EventTypeUpcaster is dedicated to adjusting the payload type, and thus can also be used to deal with changing package names.
When using (the default) XStreamSerializer, aliasing the tags would indeed also work. How to set aliases can be seen here for example. AS noticed in that sample, the alias is added to the XStream instance. The XStreamSerializer uses an XStream instance to support de-/serialization from/to XML. To adjust the XStream instance, you can simply use the builder paradigm on the XStreamSerializer. The JavaDoc of the builder should be specific enough to help you out how to use it.
Went the long way round with this but it seems to work. As always, backup your database before executing large volume changes. I also restarted the service using the database on completion. Needless to say I will make sure the events are in logical packages before deploying next time :)
Database Engine: Postgres 10
Table: domainevententry
update domainevententry
set
payloadtype = '<new.package.Classname>',
payload = lo_from_bytea(0, decode(REPLACE(
subquery.output,
'<old.package.Classname>',
'<new.package.Classname>'
), 'escape'))
from (
SELECT eventidentifier, payloadtype, encode(lo_get(payload::oid), 'escape') as output FROM domainevententry
WHERE eventidentifier in (
'<event guid 1>',
'<event guid 2>'
)
AND payloadtype = '<old.package.Classname>'
) as subquery
where domainevententry.eventidentifier = subquery.eventidentifier;
Once that is completed I needed to update the OWNER of the large object:
ALTER LARGE OBJECT <LargeObjectId> OWNER TO database_role;
Probably not the most elegant solution but based on the time constraints I have, it did the job. There are probably encoding issues with this solution for the large object but it all worked out for me in the end. Feel free to share any optimizations that would make the above more suitable.
Firing off the Axon Framework replays rebuilt the projections and everything lined up.
Why it uses d->eventFilters.prepend(obj) not append(obj) in function(QObject::installEventFilter),i want to know why design it in such way.I just curious about it.
void QObject::installEventFilter(QObject *obj)
{
Q_D(QObject);
if (!obj)
return;
if (d->threadData != obj->d_func()->threadData) {
qWarning("QObject::installEventFilter(): Cannot filter events for objects in a different thread.");
return;
}
// clean up unused items in the list
d->eventFilters.removeAll((QObject*)0);
d->eventFilters.removeAll(obj);
d->eventFilters.prepend(obj);
}
It's done that way because the most recently installed event filter is to be processed first, i.e. it needs to be at the beginning of the filter list. The filters are invoked by traversing the list in sequential order from begin() to end().
The most recently installed filter is to be processed first because the only two simple choices are to either process it first or last. And the second choice is not useful: when you filter events, you want to decide what happens before anyone else does. Well, but then some new user's filter will go before yours, so how that can be? As follows: event filters are used to amend functionality - functionality that already exists. If you added a filter somewhere inside the existing functionality, you'd effectively be interfacing to a partially defined system, with unknown behavior. After all, even Qt's implementation uses event filters. They provide the documented behavior. By inserting your event filter last, you couldn't be sure at all what events it will see - it'd all depend on implementation details of every layer of functionality above your filter.
A system with some event filter installed is like a layer of skin on the onion - the user of that system only sees the skin, not what's inside, not the implementation. But they can add their own skin on top if they wish so, and implement new functionality that way. They can't dig into the onion, because they don't know what's in it. Of course that's a generalization: they don't know because it doesn't form an API, a contract between them and the implementation of the system. They are free to read the source code and/or reverse engineer the system, and then insert the event filter anywhere in the list they wish. After all, once you get access to QObjectPrivate, you can modify the event filter list as you wish. But then you're responsible for the behavior of not only what you added on top of the public API, but of many of the underlying layers too - and your responsibility broadens. Updating the toolkit becomes next to impossible, because you'd have to audit the code and/or verify test coverage to make sure that something somewhere in the internals didn't get broken.
On my meteor project users can post events and they have to choose (via an autocomplete) in which city it will take place. I have a full list of french cities and it will never be updated.
I want to use a collection and publish-subscribes based on the input of the autocomplete because I don't want the client to download the full database (5MB). Is there a way, for performance, to tell meteor that this collection is "static"? Or does it make no difference?
Could anyone suggest a different approach?
When you "want to tell the server that a collection is static", I am aware of two potential optimizations:
Don't observe the database using a live query because the data will never change
Don't store the results of this query in the merge box because it doesn't need to be tracked and compared with other data (saving memory and CPU)
(1) is something you can do rather easily by constructing your own publish cursor. However, if any client is observing the same query, I believe Meteor will (at least in the future) optimize for that so it's still just one live query for any number of clients. As for (2), I am not aware of any straightforward way to do this because it could potentially mess up the data merging over multiple publications and subscriptions.
To avoid using a live query, you can manually add data to the publish function instead of returning a cursor, which causes the .observe() function to be called to hook up data to the subscription. Here's a simple example:
Meteor.publish(function() {
var sub = this;
var args = {}; // what you're find()ing
Foo.find(args).forEach(function(document) {
sub.added("client_collection_name", document._id, document);
});
sub.ready();
});
This will cause the data to be added to client_collection_name on the client side, which could have the same name as the collection referenced by Foo, or something different. Be aware that you can do many other things with publications (also, see the link above.)
UPDATE: To resolve issues from (2), which can be potentially very problematic depending on the size of the collection, it's necessary to bypass Meteor altogether. See https://stackoverflow.com/a/21835534/586086 for one way to do it. Another way is to just return the collection fetch()ed as a method call, although this doesn't have the benefits of compression.
From Meteor doc :
"Any change to the collection that changes the documents in a cursor will trigger a recomputation. To disable this behavior, pass {reactive: false} as an option to find."
I think this simple option is the best answer
You don't need to publish your whole collection.
1.Show autocomplete options only after user has inputted first 3 letters - this will narrow your search significantly.
2.Provide no more than 5-10 cities as options - this will keep your recordset really small - thus no need to push 5mb of data to each user.
Your publication should look like this:
Meteor.publish('pub-name', function(userInput){
var firstLetters = new RegExp('^' + userInput);
return Cities.find({name:firstLetters},{limit:10,sort:{name:1}});
});
I've been doing some reading recently and have encountered the Law of Demeter. Now some of what I've read makes perfect sense e.g. the paperboy should never be able to rifle through a customers pocket, grab the wallet and take the money out. The wallet is something the customer should have control of, not the paperboy.
What throws me about the law, maybe I'm just misunderstanding the whole thing, is that stringing properties together with a heirarchy of functionality/information can be so useful. e.g. .NETs HTTPContext class.
Wouldn't code such as :
If DataTable.Columns.Count >= 0 Then
DataTable.Columns(0).Caption = "Something"
End If
Or
Dim strUserPlatform as string = HttpContext.Current.Request.Browser.Platform.ToString()
Or
If NewTerm.StartDate >= NewTerm.AcademicYear.StartDate And
NewTerm.EndDate <= NewTerm.AcademicYear.EndDate Then
' Valid, subject to further tests.
Else
' Not valid.
End If
be breaking this law? I thought (perhaps mistakenly) the point of OOP was in part to provide access to related classes in a nice heirarchical structure.
I like, for example, the idea of referencing a utility toolkit that can be used by page classes to avoid repetitive tasks, such as sending emails and encapsulating useful string methods:
Dim strUserInput As String = "London, Paris, New York"
For Each strSearchTerm In Tools.StringManipulation.GetListOfString(strUserInput, ",")
Dim ThisItem As New SearchTerm
ThisItem.Text = strSearchTerm
Next
Any clarity would be great...at the moment I can't reconcile how the law seems to banish stringing properties and methods together...it seems strange to me that so much power should be disregarded? I'm pretty new to OOP as some of you might have guessed, so please go easy :)
What the Law of Demeter (also "Law of Demeter for Functions/Methods") wants to reduce with saying "only use one dot" is that in a method you shouldn't have to assume such a lot of context from the provided arguments. This increases the dependency of the class and makes it less testable.
It doesn't mean that you can't use all of the above examples but it suggests that instead of giving your method the customer which then accesses the wallet and retrieves the money from it:
function getPayment(Customer customer)
{
Money payment = customer.leftpocket.getWallet().getPayment(100);
...
// do stuff with the payment
}
that you instead only pass what the paperboy needs to the method and as such reduce the dependency for the method if it's possible:
function getPayment(Money money)
{
// do stuff with the payment
}
Your benefit from it will be that you dont depend on the customer to have the wallet in the left pocket but instead just process the money the customer gives you. It's a decision you have to base on your individual case though. Less dependencies allow you to test easier.
I think applying the Law of Demeter to individual classes is taking it a bit too far. I think a better application is to apply it to layers in your code. For example, your business logic layer shouldn't need to access anything about the HTTP context, and your data access layer shouldn't need to access anything in the presentation layer.
Yes, it's usually good practice to design the interface of your object so that you don't have to do tons of property chaining, but imagine the hideously complex interface you'd have if you tried to do that to the DataTable and HttpContext classes you gave as examples.
The law doesn't say that you shouldn't have access to any information at all in a class, but that you should only have access to information in a way that you can't easily misuse it.
You can for example not add columns in a data table by assigning anything to the Count property:
DataTable.Columns.Count = 42;
Instead you use the Add method of the Columns object that lets you add a column in a way that all the needed information about the column is there, and also so that the data table get set up with data for that column.
I would like pragmatically to control individual objects' add-menu list of allowed content types.
I am building a collection of content types with archgenxml. In one case, I have a simulation class composed of a RangeBase class which has three realizations, valueRange, vectorRange and uniformRange. A simulation can contain exactly one range, i.e., RangeBase's multiplicity is one, so a simulation's add-menu should offer either all three range types or none at all.
To achieve this, I thought to subscribed to the IObjectInitializedEvent and IObjectRemovedEvent events; placing their respective handlers, initializedHook and removedHook, in the RangeBase class. The handlers would solicit an object's list of locally allowed types and remove or add the three ranges accordingly. After perusing the Plone's 'Community Developer Documentation', I thought the initializedHook code might look something like this:
# Set allowed content types
from Products.ATContentTypes.lib import constraintypes
def initializedHook(obj, event):
# Get this range's parent simulation
parent = obj.aq_parent
# Enable constraining
parent.setConstrainTypesMode(constraintypes.ENABLED)
# Remove the three ranges
allowedTypes = parent.getLocallyAllowedTypes()
ranges = ('valueRange','vectorRange','uniformRange')
for range in ranges:
allowedTypes.remove(range)
# Tweak the menu
parent.setLocallyAllowedTypes(allowedTypes)
parent.setImmediatelyAddableTypes(allowedTypes)
Unfortunately, my simulation class has none of these functions.
Is there an adaptor that will provide my simulation class with this functionality, or are there other altogether different approaches to achieve the desired menu behaviour? Any suggestions would be appreciated.
It is possible.
I believe you need to override getLocallyAllowedType()
http://svn.plone.org/svn/collective/Products.ATContentTypes/trunk/Products/ATContentTypes/lib/constraintypes.py
AT was written time before adapters, so AT is not using it.
I suggest you could also update the documentation regarding this... it is pretty common use case.
http://web.archive.org/web/20101010142032/http://collective-docs.plone.org/content/creating.html
After several unsuccessful attempts at tweaking _allowedTypes(), I followed the last suggestion at http://plone.org/documentation/kb/restrict-addable-types and customized getNotAddableTypes.py. My customization merely lists a folder's contents filtering for the three ranges. If the resulting array is not empty, I add the three range types to the list:
# customize this script to filter addable portal types based on
# context, the current user or other criteria
ranges = []
ranges = context.listFolderContents(contentFilter={'portal_type':
('VectorRange','ValueRange','UniformRange')})
return {True: ('Favorite', 'VectorRange', 'ValueRange', 'UniformRange'),
False: ('Favorite')}[len(ranges)]
See the last post here for two possibilities: http://plone.293351.n2.nabble.com/Folder-constraints-not-applicable-to-custom-content-types-td6073100.html
The method
foo.getLocallyAllowedTypes()
gives back a tuple, that you just have to copy / filter into another tuple / list, because it's immutable.
allowed_types = parent.getLocallyAllowedTypes()
filtered_types = []
for v in allowed_types:
if not v in ranges:
filtered_types.append(v)
Then you can just give that tuple to the setter method
parent.setLocallyAllowedTypes(filtered_types)
and your're done. But if you want to access the parent during object creation to restrict content types of the folder, you creating the object in, you can hook up in at_post_create_script() and manage_beforeDelete() from BaseObject. This works great for me, restricting the number of specific content types to a folder and also corrects the AllowedTypes when the object gets deleted.