1.1. IMS Content Packaging (version 1.2)¶
The IMS Content Packaging specification defines methods for packaging and organizing resources and their associated metadata for transmission between systems. There is a small amount of information on Wikipedia about content packaging in general, see http://en.wikipedia.org/wiki/Content_package. The main use of IMS Content Packaging in the market place is through the SCORM profile. Content Packaging is also used as the basis for the new IMS Common Cartridge, and a method of packaging assessment materials using the speicifcation is also described by IMS QTI version 2.1.
Official information about the specification is available from the IMS GLC: http://www.imsglobal.org/content/packaging/index.html
1.1.1. Example¶
The following example script illustrates the use of this module. The script takes two arguments, a resource file to be packaged (such as an index.html file) and the path to save the zipped package to. The script creates a new package containing a single resource with the entry point set to point to the resource file. It also adds any other files in the same directory as the resource file, using the python os.walk function to include files in sub-directories too. The ContentPackage.IgnoreFilePath() method is used to ensure that hidden files are not added:
#! /usr/bin/env python
import sys, os, os.path, shutil
from pyslet.imscpv1p2 import ContentPackage, PathInPath
from pyslet.rfc2396 import URIFactory
def main():
if len(sys.argv)!=3:
print "Usage: makecp <resource file> <package file>"
return
resFile=sys.argv[1]
pkgFile=sys.argv[2]
pkg=ContentPackage()
try:
if os.path.isdir(resFile):
print "Resource entry point must be a file, not a directory."
return
resHREF=URIFactory.URLFromPathname(resFile)
srcDir,srcFile=os.path.split(resFile)
r=pkg.manifest.root.Resources.ChildElement(pkg.manifest.root.Resources.ResourceClass)
r.href=str(resHREF.Relative(URIFactory.URLFromPathname(os.path.join(srcDir,'imsmanifest.xml'))))
r.type=='webcontent'
for dirpath,dirnames,filenames in os.walk(srcDir):
for f in filenames:
srcPath=os.path.join(dirpath,f)
if pkg.IgnoreFilePath(srcPath):
print "Skipping: %s"%srcPath
continue
dstPath=os.path.join(pkg.dPath,PathInPath(srcPath,srcDir))
# copy the file
dName,fName=os.path.split(dstPath)
if not os.path.isdir(dName):
os.makedirs(dName)
print "Copying: %s"%srcPath
shutil.copy(srcPath,dstPath)
pkg.File(r,URIFactory.URLFromPathname(dstPath))
if os.path.exists(pkgFile):
if raw_input("Are you sure you want to overwrite %s? (y/n) "%pkgFile).lower()!='y':
return
pkg.manifest.Update()
pkg.ExportToPIF(pkgFile)
finally:
pkg.Close()
if __name__ == "__main__":
main()
Note the use of the try:... finally: construct to ensure that the ContentPackage object is properly closed when it is finished with. Note also the correct way to create elements within the manifest, using the dependency safe *Class attributes:
r=pkg.manifest.root.Resources.ChildElement(pkg.manifest.root.Resources.ResourceClass)
This line creates a new resource element as a child of the (required) Resources element.
At the end of the script the ManifestDocument is updated on the disk using the inherited Update() method. The package can then be exported to the zip file format.
1.1.2. Reference¶
- class pyslet.imscpv1p2.ContentPackage(dPath=None)¶
Represents a content package.
When constructed with no arguments a new package is created. A temporary folder to hold the contents of the package is created and will not be cleaned up until the Close() method is called.
Alternatively, you can pass an operating system or virtual file path to a content package directory, to an imsmanifest.xml file or to a Package Interchange Format file. In the latter case, the file is unzipped into a temporary folder to facilitate manipulation of the package contents.
A new manifest file is created and written to the file system when creating a new package, or if it is missing from an existing package or directory.
- ManifestDocumentClass¶
the default class for representing the Manifest file
alias of ManifestDocument
- dPath = None¶
the VirtualFilePath to the package’s directory
- manifest = None¶
The ManifestDocument object representing the imsmanifest.xml file.
The file is read (or created) on construction.
- fileTable = None¶
The fileTable is a dictionary that maps package relative file paths to the File objects that represent them in the manifest.
It is possible for a file to be referenced multiple times (although dependencies were designed to take care of most cases it is still possible for two resources to share a physical file, or even for a resource to contain multiple references to the same file.) Therefore, the dictionary values are lists of File objects.
If a file path maps to an empty list then a file exists in the package which is not referenced by any resource. In some packages it is commone for auxiliary files such as supporting schemas to be included in packages without a corresponding File object so an empty list does not indicate that the file can be removed safely. These files are still included when packaging the content package for interchange.
Finally, if a file referred to by a File object in the manifest is missing an entry is still created in the fileTable. You can walk the keys of the fileTable testing if each file exists to determine if some expected files are missing from the package.
The keys in fileTable are VirtualFilePath instances. To convert a string to an appropriate instance use the FilePath() method.
- FilePath(*path)¶
Converts a string into a pyslet.vfs.VirtualFilePath instance suitable for using as a key into the fileTable. The conversion is done using the file system of the content package’s directory, dPath.
- SetIgnoreFiles(ignoreFiles)¶
Sets the regular expression used to determine if a file should be ignored.
Some operating systems and utilities create hidden files or other spurious data inside the content package directory. For example, Apple’s OS X creates .DS_Store files and the svn source control utility creates .svn directories. The files shouldn’t generally be included in exported packages as they may confuse the recipient (who may be using a system on which these files and directories are not hidden) and be deemed to violate the specification, not to mention adding unnecessarily to the size of the package and perhaps even leaking information unintentionally.
To help avoid this type of problem the class uses a regular expression to determine if a file should be considered part of the package. When listing directories, the names of the files found are compared against this regular expression and are ignored if they match.
By default, the pattern is set to match all directories and files with names beginning ‘.’ so you will not normally need to call this method.
- IgnoreFile(f)¶
Compares a file or directory name against the pattern set by SetIgnoreFiles().
f is a unicode string.
- IgnoreFilePath(fPath)¶
Compares a file path against the pattern set by SetIgnoreFiles()
The path is normalised before comparison and any segments consisting of the string ‘..’ are skipped. The method returns True if any of the remaining path components matches the ignore pattern. In other words, if the path describes a file that is is in a directory that should be ignored it will also be ignored.
The path can be relative or absolute. Relative paths are not made absolute prior to comparison so this method is not affected by the current directory, even if the current diretory would itself be ignored.
- PackagePath(fPath)¶
Converts an absolute file path into a canonical package-relative path
Returns None if fPath is not inside the package.
- ExportToPIF(zPath)¶
Exports the content package, saving the zipped package in zPath
zPath is overwritten by this operation.
In order to make content packages more interoperable this method goes beyond the basic zip specification and ensures that pathnames are always UTF-8 encoded when added to the archive. When creating instances of ContentPackage from an existing archive the reverse transformation is performed. When exchanging PIF files between systems with different native file path encodings, encoding erros may occurr.
- GetUniqueFile(suggestedPath)¶
Returns a unique file path suitable for creating a new file in the package.
suggestedPath is used to provide a suggested path for the file. This may be relative (to the root and manifest) or absolute but it must resolve to a file (potentially) in the package. The suggestedPath should either be a VirtualFilePath (of the same type as the content package’s dPath) or a string suitable for conversion to a VirtualFilePath.
When suggestedPath is relative, it is forced to lower-case. This is consistent with the behaviour of normcase on systems that are case insensitive. The trouble with case insensitive file systems is that it may be impossible to unpack a content package created on a case sensitive system and store it on a case insenstive one. By channelling all file storage through this method (and constructing any URIs after the file has been stored) the resulting packages will be more portable.
If suggestedPath already corresponds to a file already in the package, or to a file already referred to in the manifest, then a random string is added to it while preserving the suggested extension in order to make it unique.
The return result is always normalized and returned relative to the package root.
- File(resource, href)¶
Returns a new File object attached to resource
href is the URI of the file expressed relative to the resource element in the manifest. Although this is normally the same as the URI expressed relative to the package, a resource may have an xml:base attribute that alters the base for resolving relative URIs.
href may of course be an absolute URI to an external resource. If an absolute URI is given to a local file it must be located inside the package.
Attempting to add a File object representing the manifest file iteself will raise CPFilePathError.
The fileTable is updated automatically by this method.
- FileCopy(resource, srcURL)¶
Returns a new File object copied into the package from srcURL, attached to resource.
The file is copied to the same directory as the resource’s entry point or to the main package directory if the resource has no entry point.
The File object is actually created with the File() method.
Note that if srcURL points to a missing file then no file is copied to the package but the associated File is still created. It will point to a missing file.
- DeleteFile(href)¶
Removes the file at href from the file system
This method also removes any file references to it from resources in the manifest. href may be given relative to the package root directory. The entry in fileTable is also removed.
CPFileTypeError is raised if the file is not a regular file
CPFilePathError is raised if the file is an IgnoreFile(), the manifest itself or outside of the content package.
CPProtocolError is raised if the indicated file is not in the local file system.
- GetPackageName()¶
Returns a human readable name for the package
The name is determined by the method used to create the object. The purpose is to return a name that would be intuitive to the user if it were to be used as the name of the package directory or the stem of a file name when exporting to a PIF file.
Note that the name is returned as a unicode string suitable for showing to the user and may need to be encoded before being used in file path operations.
- Close()¶
Closes the content package, removing any temporary files.
This method must be called to clean up any temporary files created when processing the content package. Temporary files are created inside a special temporary directory created using the builtin python tempdir.mkdtemp function. They are not automatically cleaned up when the process exits or when the garbage collector disposes of the object. Use of try:... finally: to clean up the package is recommended. For example:
pkg=ContentPackage("MyPackage.zip") try: # do stuff with the content package here finally: pkg.Close()
- class pyslet.imscpv1p2.ManifestDocument(**args)¶
Bases: pyslet.xmlnames20091208.XMLNSDocument
Represents the imsmanifest.xml file itself.
Buildong on pyslet.xmlnames20091208.XMLNSDocument this class is used for parsing and writing manifest files.
The constructor defines three additional prefixes using MakePrefix(), mapping xsi onto XML schema, imsmd onto the IMS LRM namespace and imsqti onto the IMS QTI 2.1 namespace. It also adds a schemaLocation attribute. The elements defined by the pyslet.imsmdv1p2p1 and pyslet.imsqtiv2p1 modules are added to the classMap to ensure that metadata from those schemas are bound to the special classes defined there.
- defaultNS = None¶
the default namespace is set to IMSCP_NAMESPACE
- GetElementClass(name)¶
Overrides pyslet.xmlnames20091208.XMLNSDocument.GetElementClass() to look up name.
The class contains a mapping from (namespace,element name) pairs to class objects representing the elements. Any element not in the class map returns XMLNSElement() instead.
1.1.2.1. Constants¶
The following constants are used for setting and interpreting XML documents that conform to the Content Packaging specification
- pyslet.imscpv1p2.IMSCP_NAMESPACE = 'http://www.imsglobal.org/xsd/imscp_v1p1'¶
str(object=’‘) -> string
Return a nice string representation of the object. If the argument is a string, the return value is the same object.
- pyslet.imscpv1p2.IMSCP_SCHEMALOCATION = 'http://www.imsglobal.org/xsd/imscp_v1p1.xsd'¶
str(object=’‘) -> string
Return a nice string representation of the object. If the argument is a string, the return value is the same object.
- pyslet.imscpv1p2.IMSCPX_NAMESPACE = 'http://www.imsglobal.org/xsd/imscp_extensionv1p2'¶
str(object=’‘) -> string
Return a nice string representation of the object. If the argument is a string, the return value is the same object.
1.1.2.2. Elements¶
- class pyslet.imscpv1p2.CPElement(parent, name=None)¶
Bases: pyslet.xmlnames20091208.XMLNSElement
Base class for all elements defined by the Content Packaging specification.
- class pyslet.imscpv1p2.Manifest(parent)¶
Bases: pyslet.imscpv1p2.CPElement
Represents the manifest element, the root element of the imsmanifest file.
- OrganizationsClass¶
the default class to represent the organizations element
alias of Organizations
- Metadata = None¶
the manifest’s metadata element
- Organizations = None¶
the organizations element
- Resources = None¶
the resources element
- Manifest = None¶
a list of child manifest elements
- class pyslet.imscpv1p2.Metadata(parent)¶
Bases: pyslet.imscpv1p2.CPElement
Represents the Metadata element.
- SchemaVersionClass¶
alias of SchemaVersion
- Schema = None¶
the optional schema element
- SchemaVersion = None¶
the optional schemaversion element
- class pyslet.imscpv1p2.Schema(parent, name=None)¶
Bases: pyslet.imscpv1p2.CPElement
Represents the schema element.
- class pyslet.imscpv1p2.SchemaVersion(parent, name=None)¶
Bases: pyslet.imscpv1p2.CPElement
Represents the schemaversion element.
- class pyslet.imscpv1p2.Organizations(parent)¶
Bases: pyslet.imscpv1p2.CPElement
Represents the organizations element.
- OrganizationClass¶
the default class to represent the organization element
alias of Organization
- Organization = None¶
a list of organization elements
- class pyslet.imscpv1p2.Organization(parent, name=None)¶
Bases: pyslet.imscpv1p2.CPElement
Represents the organization element.
- class pyslet.imscpv1p2.Resources(parent)¶
Bases: pyslet.imscpv1p2.CPElement
Represents the resources element.
- Resource = None¶
the list of resources in the manifest
- class pyslet.imscpv1p2.Resource(parent)¶
Bases: pyslet.imscpv1p2.CPElement
Represents the resource element.
- DependencyClass¶
the default class to represent the dependency element
alias of Dependency
- type = None¶
the type of the resource
- href = None¶
the href pointing at the resource’s entry point
- Metadata = None¶
the resource’s optional metadata element
- File = None¶
a list of file elements associated with the resource
- Dependency = None¶
a list of dependencies of this resource
- class pyslet.imscpv1p2.File(parent)¶
Bases: pyslet.imscpv1p2.CPElement
Represents the file element.
- href = None¶
the href used to locate the file object
- PackagePath(cp)¶
Returns the normalized file path relative to the root of the content package, cp.
If the href does not point to a local file then None is returned. Otherwise, this function calculates an absolute path to the file and then calls the content package’s ContentPackage.PackagePath() method.
- class pyslet.imscpv1p2.Dependency(parent)¶
Bases: pyslet.imscpv1p2.CPElement
Represents the dependency element.
- identifierref = None¶
the identifier of the resource in this dependency
1.1.2.3. Utilities¶
- pyslet.imscpv1p2.PathInPath(childPath, parentPath)¶
Utility function that returns childPath expressed relative to parentPath
This function processes file system paths, not the path components of URI.
Both paths are normalized to remove any redundant navigational segments before any processing, the resulting path will not contain these either.
If childPath is not contained in parentPath then None is returned.
If childPath and parentPath are equal an empty string is returned.