|
From: <lo...@us...> - 2010-03-19 12:56:38
|
Revision: 2389
http://qtitools.svn.sourceforge.net/qtitools/?rev=2389&view=rev
Author: loccy
Date: 2010-03-19 12:56:29 +0000 (Fri, 19 Mar 2010)
Log Message:
-----------
Mathqurate: Implemented new content package handling in line with FETLAR/Jorum standard metadata. Metadata handling now integrated with content package, plus searchable taxon on metadata view.
Modified Paths:
--------------
Mathqurate/trunk/mathqurate/pom.xml
Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/controller/DefaultController.java
Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/MQContentPackage.java
Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/MQMetadata.java
Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/MQModel.java
Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/MQObjectFactory.java
Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/view/ContentPackageExportView.java
Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/view/ContentPackageImportView.java
Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/view/Init.java
Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/view/MQMain.java
Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/view/MetadataView.java
Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/view/ResponseTemplateProcessingView.java
Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/view/SnuggleTeXView.java
Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/view/Splash.java
Added Paths:
-----------
Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/AttribValue.java
Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/utilities/CPBuildException.java
Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/utilities/ZipHelper.java
Mathqurate/trunk/mathqurate/src/main/resources/org/qtitools/mathqurate/resources/md-template.xml
Removed Paths:
-------------
Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/utilities/ContentPackageBuilder.java
Modified: Mathqurate/trunk/mathqurate/pom.xml
===================================================================
--- Mathqurate/trunk/mathqurate/pom.xml 2010-03-19 09:55:12 UTC (rev 2388)
+++ Mathqurate/trunk/mathqurate/pom.xml 2010-03-19 12:56:29 UTC (rev 2389)
@@ -297,7 +297,9 @@
<version>1.5.8</version>
</dependency>
<!--minibix -->
- <dependency>
+ <!-- CARET libraries are no longer needed since
+ MQ version 0.9.7. -->
+ <!--<dependency>
<groupId>uk.ac.cam.caret.minibix</groupId>
<artifactId>imscp-pgs</artifactId>
<version>1.0</version>
@@ -351,7 +353,7 @@
<groupId>uk.ac.cam.caret.minibix</groupId>
<artifactId>qticp-pgs</artifactId>
<version>1.0</version>
- </dependency>
+ </dependency>-->
<!-- JQTI rendering -->
<dependency>
<groupId>xalan</groupId>
@@ -525,7 +527,7 @@
<dependency>
<groupId>org.qtitools</groupId>
<artifactId>validatr</artifactId>
- <version>2.2mq</version>
+ <version>2.3mq</version>
</dependency>
<!-- mozilla xulrunner -->
<dependency>
Modified: Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/controller/DefaultController.java
===================================================================
--- Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/controller/DefaultController.java 2010-03-19 09:55:12 UTC (rev 2388)
+++ Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/controller/DefaultController.java 2010-03-19 12:56:29 UTC (rev 2389)
@@ -33,6 +33,7 @@
package org.qtitools.mathqurate.controller;
+import java.io.File;
import java.util.ArrayList;
import org.imsglobal.xsd.imsqti_v2p1.ChoiceInteractionType;
@@ -516,15 +517,14 @@
}
/**
- * Import mqims metadata.
- * initialises the model's metadata.
- * The metadata structure is then set and the views triggered to update.
- *
- * @param newValue the new value
+ * Import content package file
+ * Bits need to be:
+ * Array element 0 - file to be imported
+ * Array element 1 - string containing Minibix ticket number
+ * Array element 2 - string containing Minibix URL
*/
-// public void importContentPackage(MQContentPackage newValue){
- public void importContentPackage(MQContentPackage newValue){
- setModelProperty(IMPORT_CONTENTPACKAGE_PROPERTY, newValue);
+ public void importContentPackage(Object[] bits) {
+ setModelProperty(IMPORT_CONTENTPACKAGE_PROPERTY, bits);
}
/**
Added: Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/AttribValue.java
===================================================================
--- Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/AttribValue.java (rev 0)
+++ Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/AttribValue.java 2010-03-19 12:56:29 UTC (rev 2389)
@@ -0,0 +1,62 @@
+package org.qtitools.mathqurate.model;
+
+
+/**
+ * The Class AttribValue.
+ * Used to store Attribute value pairs. Used for in the Meta-data structure
+ */
+public class AttribValue {
+
+ /** The Attribute */
+ String Attrib = "";
+
+ /** The Value. */
+ String Value = "";
+
+ /**
+ * Instantiates a new attrib value.
+ *
+ * @param attrib the attrib
+ * @param value the value
+ */
+ public AttribValue(String attrib, String value) {
+ Attrib = attrib;
+ if (value != null) Value = value; else value = "";
+ }
+
+ /**
+ * Gets the attrib.
+ *
+ * @return the attrib
+ */
+ public String getAttrib() {
+ return Attrib;
+ }
+
+ /**
+ * Gets the value.
+ *
+ * @return the value
+ */
+ public String getValue() {
+ return Value;
+ }
+
+ /**
+ * Sets the attrib.
+ *
+ * @param attrib the new attrib
+ */
+ public void setAttrib(String attrib) {
+ Attrib = attrib;
+ }
+
+ /**
+ * Sets the value.
+ *
+ * @param value the new value
+ */
+ public void setValue(String value) {
+ Value = value;
+ }
+}
\ No newline at end of file
Modified: Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/MQContentPackage.java
===================================================================
--- Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/MQContentPackage.java 2010-03-19 09:55:12 UTC (rev 2388)
+++ Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/MQContentPackage.java 2010-03-19 12:56:29 UTC (rev 2389)
@@ -1,13 +1,46 @@
package org.qtitools.mathqurate.model;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Date;
+import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.UUID;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathFactory;
+
import org.imsglobal.xsd.imsqti_v2p1.AssessmentItemType;
import org.qtitools.mathqurate.utilities.PrefsHelper;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import com.sun.org.apache.xml.internal.serialize.OutputFormat;
+import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
+
/**
* The Class MQAssessmentItem.
* Creates a model of the assessment item
@@ -20,16 +53,14 @@
/** The assessmentItem. */
private AssessmentItemType assessmentItemType;
- /** extended metadata */
+ /** metadata */
private LinkedHashMap<String,String> extendedMetadata =
- new LinkedHashMap<String,String>();
-
-
+ new LinkedHashMap<String,String>();
/**
- * Instantiates a new mQ content package.
+ * Instantiates a new mQ content package, given a QTI assessmentItem
*
- * @param assessmentItemType the assessment item type
+ * @param assessmentItemType the assessment item
*
*/
public MQContentPackage(AssessmentItemType assessmentItemType) {
@@ -39,15 +70,24 @@
this.contentXML = MQModel.mathqurateObjectFactory.getTypeAsXML(assessmentItemType);
- this.set("url",PrefsHelper.getMinibixUrl());
+ this.set("url",PrefsHelper.getMinibixUrl()+":"+PrefsHelper.getMinibixPort());
this.assessmentItemType = MQModel.mathqurateObjectFactory.createAssessmentType(contentXML);
+ populateMdFields();
}
+
+ @Override
+ public String getContentXML()
+ {
+ this.contentXML = MQModel.mathqurateObjectFactory.getTypeAsXML(assessmentItemType);
+ return this.contentXML;
+ }
/**
* Instantiates a new mQ assessment item and populate its metadata with explicitly
- * passed strings.
+ * passed strings. (PN: not quite sure where this is used actually? Seems obsolete
+ * given the new metadata approach...)
*
* @param assessmentItemType the assessment item type
* @param contentXML the content xml or html
@@ -62,25 +102,27 @@
public MQContentPackage(String contentXML,
String author, String datetime, String description, String taxon,
String ticket, String url) {
+
this.contentXML = contentXML;
- this.set("author",author);
+ this.set(MQMetadata.AUTHOR[0],author);
//this.setDatetime(datetime);
- this.set("description",description);
- this.set("taxon",taxon);
- this.set("ticket",ticket);
- this.set("url",url);
+ this.set(MQMetadata.DESCRIPTION[0],description);
+ this.set(MQMetadata.TAXON[0],taxon);
+ this.set(MQMetadata.TICKET[0],ticket);
+ this.set(MQMetadata.URL[0],url);
this.assessmentItemType = MQModel.mathqurateObjectFactory.createAssessmentType(contentXML);
+ populateMdFields();
}
/**
- * Instantiates a new mQ assessment item from an existing one.
+ * Instantiates a new mQ content package from an existing one.
*
* @param assessmentItem the Mathqurate assessment item
*
*/
- public MQContentPackage(MQContentPackage assessmentItem) {
+ public MQContentPackage(MQContentPackage cp) {
/*
this.set("author",assessmentItem.get("author"));
@@ -90,10 +132,11 @@
this.set("ticket",assessmentItem.get("ticket"));
this.set("url",assessmentItem.get("url")); */
- this.extendedMetadata = assessmentItem.getMetadataMap();
+ this.extendedMetadata = cp.getMetadataMap();
- this.contentXML = MQModel.mathqurateObjectFactory.getTypeAsXML(assessmentItem.getAssessmentItemType());
+ this.contentXML = MQModel.mathqurateObjectFactory.getTypeAsXML(cp.getAssessmentItemType());
this.assessmentItemType = MQModel.mathqurateObjectFactory.createAssessmentType(contentXML);
+ populateMdFields();
}
@@ -106,6 +149,37 @@
}
/**
+ * Populates what metadata fields can be populated from the QTI attributes.
+ * Fill others with blanks if they're not already populated
+ */
+ private void populateMdFields()
+ {
+ this.set(MQMetadata.IDENTIFIER[0], assessmentItemType.getIdentifier());
+ this.set(MQMetadata.ADAPTIVE[0],String.valueOf(assessmentItemType.isAdaptive()));
+ this.set(MQMetadata.TIMEDEPENDENT[0], String.valueOf(assessmentItemType.isTimeDependent()));
+ this.set(MQMetadata.TITLE[0],assessmentItemType.getTitle());
+ this.set(MQMetadata.AUTHORINGTOOL[0],assessmentItemType.getToolName());
+ this.set(MQMetadata.LABEL[0],assessmentItemType.getLabel());
+ this.set(MQMetadata.LANG[0],assessmentItemType.getLang());
+ this.set(MQMetadata.TOOLVERSION[0],assessmentItemType.getToolVersion());
+
+ // fill the rest with blanks
+ for (String[] pair : MQMetadata.MQFIELDNAMES)
+ {
+ if (!extendedMetadata.containsKey(pair[0])) this.set(pair[0],"");
+ }
+
+/* if (!extendedMetadata.containsKey(MQMetadata.AUTHOR[0])) this.set("author","");
+ if (!extendedMetadata.containsKey(MQMetadata.DESCRIPTION[0])) this.set("description","");
+ if (!extendedMetadata.containsKey(MQMetadata.KEYWORDS[0])) this.set("keywords","");
+ if (!extendedMetadata.containsKey(MQMetadata)) this.set("location","");
+ if (!extendedMetadata.containsKey("software")) this.set("software","");
+ if (!extendedMetadata.containsKey("ticketno")) this.set("ticketno",""); */
+ //if (!extendedMetadata.containsKey("url")) this.set("url","");
+
+ }
+
+ /**
* Gets the assessment item type.
*
* @return the assessment item type
@@ -123,20 +197,47 @@
this.assessmentItemType = assessmentItemType;
}
-
- public void setTitle(String title) {
- this.assessmentItemType.setTitle(title);
- }
-
/**
- * Sets a generic metadata value
- * @param key
- * @param value
+ * Sets a metadata value
+ * @param key metadata to set
+ * @param value value to set
*/
public void set(String key, String value)
{
+// System.out.println("METADATA: setting "+key+" as "+value);
key = key.toUpperCase();
extendedMetadata.put(key, value);
+
+ // also update those fields which need to go in the QTI itself:
+ if (key.equals(MQMetadata.IDENTIFIER[0]))
+ assessmentItemType.setIdentifier(value);
+
+ if (key.equals(MQMetadata.ADAPTIVE[0]))
+ {
+ if (value.toLowerCase().equals("true")) assessmentItemType.setAdaptive(true);
+ else assessmentItemType.setAdaptive(false);
+ }
+
+ if (key.equals(MQMetadata.TIMEDEPENDENT[0]))
+ {
+ if (value.toLowerCase().equals("true")) assessmentItemType.setTimeDependent(true);
+ else assessmentItemType.setTimeDependent(false);
+ }
+
+ if (key.equals(MQMetadata.TITLE[0]))
+ {
+ assessmentItemType.setTitle(value);
+ }
+
+ if (key.equals(MQMetadata.AUTHORINGTOOL[0]))
+ assessmentItemType.setToolName(value);
+
+ if (key.equals(MQMetadata.LANG[0]))
+ assessmentItemType.setLang(value);
+
+ if (key.equals(MQMetadata.TOOLVERSION[0]))
+ assessmentItemType.setToolVersion(value);
+
}
/**
@@ -147,7 +248,7 @@
public String get(String key)
{
key = key.toUpperCase();
- if (key.equals("DATETIME"))
+ if (key.equals(MQMetadata.DATETIME[0]))
{
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
Date date = new Date();
@@ -157,6 +258,77 @@
else return extendedMetadata.get(key);
}
+ /**
+ * Returns the metadata as an MQMetadata.
+ * The MQMetadata is still used in the view class for populating the
+ * metadata table, so this is a convenience method for that purpose.
+ * @return
+ */
+ public MQMetadata getMetadata()
+ {
+ MQMetadata metadata = new MQMetadata();
+ ArrayList<AttribValue> metadataArray = new ArrayList<AttribValue>();
+ metadata.setTaxon(this.get(MQMetadata.TAXON[0]));
+
+ // pull things out of our hashmap
+ Iterator it = extendedMetadata.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry pairs = (Map.Entry)it.next();
+ String keyString = (String)pairs.getKey();
+ String value = (String)pairs.getValue();
+
+ // prettify fields we know about
+ for (String[] mdfield : MQMetadata.MQFIELDNAMES)
+ {
+ if (keyString.toUpperCase().equals(mdfield[0]))
+ {
+ keyString = mdfield[1];
+ }
+ }
+
+ String[] oneEntryArray = { keyString, value };
+ // fields with @ are suppressed
+ if (!keyString.startsWith("@"))
+ metadataArray.add(new AttribValue(keyString,value));
+ }
+
+ metadata.setMetadataArray(metadataArray);
+ return metadata;
+ }
+
+ /**
+ * Sets the metadata hashmap from an MQMetadata object. As above,
+ * a convenience method used by the metadata UI view.
+ * @param metadata
+ */
+ public void setMetadata(MQMetadata metadata)
+ {
+ this.set(MQMetadata.TAXON[0], metadata.getTaxon());
+ ArrayList<AttribValue> metadataArray = metadata.getMetadataArray();
+ for (AttribValue pair : metadataArray)
+ {
+ String keyString = pair.getAttrib();
+ String value = pair.getValue();
+ //System.out.println("METADATA: setting "+keyString+" as "+value);
+
+ // de-prettify prettified fields
+ for (String[] mdfield : MQMetadata.MQFIELDNAMES)
+ {
+ if (keyString.equals(mdfield[1]))
+ {
+ keyString = mdfield[0];
+ }
+ }
+
+ if (!keyString.startsWith("@"))
+ this.set(keyString, value);
+ }
+ }
+
+ /**
+ * Returns the metadata as a linked hashmap
+ * @return
+ */
public LinkedHashMap<String,String> getMetadataMap()
{
return extendedMetadata;
@@ -169,9 +341,37 @@
public void setMetadataMap(LinkedHashMap<String,String> extendedMetadata)
{
this.extendedMetadata = extendedMetadata;
+ populateMdFields();
}
/**
+ * Gets metadata as a 2 x Y array
+ * @return
+ */
+ public String[][] getMDAsArray()
+ {
+ ArrayList<String[]> al = new ArrayList();
+ Iterator it = extendedMetadata.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry pairs = (Map.Entry)it.next();
+ String keyString = (String)pairs.getKey();
+ String value = (String)pairs.getValue();
+ String[] oneEntryArray = { keyString, value };
+ al.add(oneEntryArray);
+ }
+ String[][] array = new String[2][al.size()];
+ int i = 0;
+ for (String[] oneEntryArray : al)
+ {
+ array[0][i] = oneEntryArray[0];
+ array[1][i] = oneEntryArray[1];
+ i++;
+ }
+ return array;
+ }
+
+
+ /**
* Set the metadata map from an existing CP's metadata
* @param cp
*/
@@ -180,76 +380,420 @@
this.extendedMetadata = cp.getMetadataMap();
}
- /** Updates the contentXML. Kludgey, but needed in the ContentPackageBuilder. Search
- * for KLUDGE over there for the appropriate comment.
+ /**
+ * Returns the CP's metadata as an IMS CP manifest.
+ * @return
*/
- public void updateContentXML() {
- this.contentXML = MQModel.mathqurateObjectFactory.getTypeAsXML(assessmentItemType);
+ public String metadataToXml()
+ {
+ return metadataToXml(this);
}
- // TODO: LEGACY STUFF
- // a whole raft of legacy bumpf follows below. This is from the old metadata model.
- // Will ultimately be removed.
-
- /** The author. */
- //private String author="";
-
- /** The Minibix item bank ticket. */
- //private String ticket="";
-
- /** The assessment item description. */
- //private String description="";
-
- /** The Minibix item bank url. */
- //private String url = PrefsHelper.getMinibixUrl();//MQModel.URL;
-
- /** The date and time. */
- //private String datetime="";
-
- /** The MathAssess taxon. */
- //private String taxon="";
-
- /* legacy "getters and setters" below */
-
-/* public void setURL(String url) {
- extendedMetadata.put("URL", url);
- //this.url = url;
- }public String getTicket() {
- return extendedMetadata.get("TICKET");
- //return ticket;
- }public String getTaxon() {
- return extendedMetadata.get("TAXON");
- //return taxon;
- }public String getDescription() {
- return extendedMetadata.get("DESCRIPTION");
- //return description;
- }public void setDatetime(String datetime) {
- // this is pointless
- //this.datetime = datetime;
- }public void setAuthor(String author) {
- extendedMetadata.put("AUTHOR", author);
- //this.author = author;
- }public void setTaxon(String taxon) {
- extendedMetadata.put("TAXON", taxon);
- //this.taxon = taxon;
- }public void setTicket(String ticket) {
- extendedMetadata.put("TICKET",ticket);
- //this.ticket = ticket;
- }public void setDescription(String description) {
- extendedMetadata.put("DESCRIPTION",description);
- //this.description = description;
- }public String getURL() {
- return extendedMetadata.get("URL");
- //return url;
- }public String getAuthor() {
- return extendedMetadata.get("AUTHOR");
- //return author;
- }public String getDatetime() {
+ /**
+ * Returns an IMS manifest for a ContentPackage as a string
+ * Convenience static method - probably now won't ever be used in a static
+ * context, but there you go.
+ * @param md
+ * @return
+ */
+ public static String metadataToXml(MQContentPackage cp)
+ {
+ // pull in metadata template from resource
+ InputStream is = MQContentPackage.class.
+ getClassLoader().getResourceAsStream("org/qtitools/mathqurate/resources/md-template.xml");
+ StringBuilder sb = new StringBuilder();
+ String line;
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+ while ((line = reader.readLine()) != null) {
+ sb.append(line).append("\n");
+ }
+ } catch (UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } finally {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ String template = sb.toString();
+
+ // get metadata
+ LinkedHashMap<String,String> mdmap = cp.getMetadataMap();
+
+ // push our easy fields into metadata
+ template = template.replace("{{IDENTIFIER}}", mdmap.get(MQMetadata.IDENTIFIER[0]));
+ template = template.replace("{{TITLE}}", mdmap.get(MQMetadata.TITLE[0]));
+ template = template.replace("{{DESCRIPTION}}", mdmap.get(MQMetadata.DESCRIPTION[0]));
+ template = template.replace("{{AUTHOR}}", mdmap.get(MQMetadata.AUTHOR[0]));
+ template = template.replace("{{DESCRIPTION}}", mdmap.get(MQMetadata.DESCRIPTION[0]));
+ template = template.replace("{{LOCATION-URL}}", mdmap.get(MQMetadata.LOCATION[0]));
+ template = template.replace("{{SOFTWARE}}", mdmap.get(MQMetadata.SOFTWARE[0]));
+ template = template.replace("{{CC-URL}}", mdmap.get(MQMetadata.LICENCEURL[0]));
+ //template = template.replace("{{TAXON}}", mdmap.get(MQMetadata.TAXON[0]));
+ template = template.replace("{{FETLAR}}", mdmap.get(MQMetadata.REPODOMAIN[0]));
+
+ // now do the harder fields!
+
+ /* KEYWORDS: each keyword neeeds to go in the following code block
+ * <imsmd:keyword>
+ * <imsmd:langstring xml:lang="en">{{KEYWORD}}</imsmd:langstring>
+ * </imsmd:keyword>
+ * Split at spaces and then build a block of XML.
+ * Then substitute placeholder comment with our built block.
+ */
+ String keywordsString = mdmap.get(MQMetadata.KEYWORDS[0]);
+ if (keywordsString != null) // shouldn't happen, but just in case
+ {
+ String[] keywords = keywordsString.split("\\s+");
+ String xmlString = "";
+ for (String keyword : keywords)
+ {
+ xmlString += "<imsmd:keyword><imsmd:langstring xml:lang=\"en\">"+keyword+"</imsmd:langstring></imsmd:keyword>\n";
+ }
+ template = template.replace("<!-- KEYWORDSHERE -->",xmlString);
+ }
+
+ // DATE/DATETIME
+ /* There is no way I'm letting a user type this in. We'll stamp it at point of
+ * creation.
+ */
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
Date date = new Date();
String dateStr = df.format(date);
- return dateStr;
- //return datetime;
- } */
+ template = template.replace("{{DATE}}", dateStr);
+
+ // UNIQUE-ID
+ // derive an ID in the form FETLAR-2-X where X is derived from epoch time
+ long epoch = System.currentTimeMillis() / 1000;
+ template = template.replace("{{UNIQUE-ID}}","FETLAR-2-"+String.valueOf(epoch));
+
+ // create {{RANDOM-HASH}}, derived from the epoch md5-ed
+ try {
+ MessageDigest m = MessageDigest.getInstance("MD5");
+ byte[] data = String.valueOf(epoch).getBytes();
+ m.update(data,0,data.length);
+ BigInteger i = new BigInteger(1,m.digest());
+ String md5 = String.format("%1$032X", i);
+ template = template.replace("{{RANDOM-HASH}}",md5);
+ } catch (NoSuchAlgorithmException e) {} // can't happen
+
+ // add taxon
+ // This needs to be added in a new classification element
+ String xmlString = "<imsmd:classification>"+
+ "<imsmd:purpose>"+
+ "<imsmd:source>"+
+ "<imsmd:langstring xml:lang=\"x-none\">imsmdv1.0</imsmd:langstring>"+
+ "</imsmd:source>"+
+ "<imsmd:value>"+
+ "<imsmd:langstring xml:lang=\"x-none\">Discipline</imsmd:langstring>"+
+ "</imsmd:value>"+
+ "</imsmd:purpose>"+
+ "<imsmd:taxonpath>"+
+ "<imsmd:source>"+
+ "<imsmd:langstring xml:lang=\"en\"><!-- TAXONNAMEHERE --></imsmd:langstring>"+
+ "</imsmd:source>"+
+ "<imsmd:taxon>"+
+ "<imsmd:entry>"+
+ "<imsmd:langstring xml:lang=\"en\">"+mdmap.get(MQMetadata.TAXON[0])+"</imsmd:langstring>"+
+ "</imsmd:entry>"+
+ "</imsmd:taxon>"+
+ "</imsmd:taxonpath>"+
+ "</imsmd:classification>";
+ template = template.replace("<!-- MATAXONHERE -->",xmlString);
+ // TODO: Get taxonomy name
+ template = template.replace("<!-- TAXONNAMEHERE -->","MathAssess Taxonomy");
+
+ // resource
+ /*String resourceXml =
+ "<resource identifier=\"{{RES-ID}}\""+
+ " type=\"imsqti_item_xmlv2p1\" href=\"{{FILENAME}}\">"+
+ "<file href=\"{{FILENAME}}\"/>"+
+ "</resource>";*/
+
+ // resource stuff
+ template = template.replace("{{RES-ID}}",String.valueOf(UUID.randomUUID()));
+
+ // TODO: may need to sort out filename
+ template = template.replace("{{FILENAME}}", mdmap.get(MQMetadata.FILENAME[0]));
+
+ // time dependent
+ template = template.replace("{{TIMEDEPENDENT}}", mdmap.get(MQMetadata.TIMEDEPENDENT[0]));
+
+ // solution available
+ template = template.replace("{{SOLUTIONAVAILABLE}}", mdmap.get(MQMetadata.SOLUTIONAVAILABLE[0]));
+
+ // tool version
+ template = template.replace("{{MQVERSION}}", mdmap.get(MQMetadata.TOOLVERSION[0]));
+
+ // that should be everything in {{xxx}} notation, but wipe out anything we missed
+ template = template.replaceAll("\\{\\{.+\\}", "");
+
+ // grab everything between <!-- MDSTART --> and <!-- MDEND -->
+ int startpoint = template.indexOf("<!-- MDSTART -->");
+ int endpoint = template.indexOf("<!-- MDEND -->");
+ String metaelement = template.substring(startpoint,endpoint);
+ metaelement = metaelement.replace("<!-- MDSTART -->", "");
+ //System.out.println(metaelement);
+
+ // duplicate metadata within question resource
+ template = template.replace("<!-- METADATA -->", metaelement);
+
+ // clear up any remaining comments
+ template = template.replaceAll("\\<!--.+--\\>", "");
+
+ // remove Dan's additional metadata bumpf
+// mdTemplate = mdTemplate.replaceAll("(?s)\\<!-- FETLAR-CLASS-START --\\>.*<!-- FETLARCLASSEND -->","");
+
+
+ // return a pretty printed version
+ return format(template);
+ }
+ /**
+ * Returns a hashmap of metadata, given an IMS manifest XML file.
+ * @param filename a string containing the filename of the XML file.
+ * @return
+ */
+ public static LinkedHashMap<String,String> metadataFromFile(String filename)
+ {
+ return metadataFromFile(new File(filename));
+ }
+
+ /**
+ * Returns a hashmap of metadata, given an IMS manifest XML file.
+ * @param xmlFile the XML file
+ * @return
+ */
+ public static LinkedHashMap<String,String> metadataFromFile(File xmlFile)
+ {
+ try
+ {
+ String filename = xmlFile.getAbsolutePath();
+ // create document from our string
+ DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+ docFactory.setNamespaceAware(true);
+ DocumentBuilder docBuilder = null;
+ try {
+ docBuilder = docFactory.newDocumentBuilder();
+ } catch (ParserConfigurationException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace(); // shouldn't happen
+ return null;
+ }
+ Document doc = docBuilder.parse(filename);
+ Element element = doc.getDocumentElement();
+
+ XPathFactory xpfactory = XPathFactory.newInstance();
+ XPath xpath = xpfactory.newXPath();
+
+ // much of the below skanked straight out of Spectatus and adapted.
+ // Probably should get round to genericising this at some point and
+ // rolling it into a library.
+
+ // Have to do this if namespaceAware = true, otherwise XPATH simply doesn't work.
+ // namespace for general metadata
+ NamespaceContext ncImsMd = new NamespaceContext()
+ {
+
+ public String getNamespaceURI(String prefix) {
+ return("http://www.imsglobal.org/xsd/imsmd_v1p2");
+ }
+
+ public String getPrefix(String namespaceURI) {
+ return "lom";
+ }
+
+ public Iterator getPrefixes(String namespaceURI) {
+ return null;
+ }
+ };
+
+ // QTI namespace - used for QTI-specific metadata, e.g. tool version
+ NamespaceContext ncImsQti = new NamespaceContext()
+ {
+ public String getNamespaceURI(String prefix) {
+ return("http://www.imsglobal.org/xsd/imsqti_v2p1");
+ }
+
+ public String getPrefix(String namespaceURI) {
+ return "imsqti";
+ }
+
+ public Iterator getPrefixes(String namespaceURI) {
+ return null;
+ }
+ };
+
+ // CP namespace - for stuff like resource, file, etc
+ NamespaceContext ncImsCp = new NamespaceContext()
+ {
+
+ public String getNamespaceURI(String prefix) {
+ return("http://www.imsglobal.org/xsd/imscp_v1p1");
+ }
+
+ public String getPrefix(String namespaceURI) {
+ return "cp";
+ }
+
+ public Iterator getPrefixes(String namespaceURI) {
+ return null;
+ }
+ };
+
+
+ // kick off using the general metadata namespace
+ xpath.setNamespaceContext(ncImsMd);
+
+ // identifier
+ XPathExpression expr = xpath.compile("//lom:general/lom:identifier");
+ String identifier = (String)expr.evaluate(element, XPathConstants.STRING);
+
+ // title
+ expr = xpath.compile("//lom:general/lom:title");
+ String title = (String)expr.evaluate(element, XPathConstants.STRING);
+ title = title.trim();
+
+ // description
+ expr = xpath.compile("//lom:general/lom:description/lom:langstring");
+ String description = (String)expr.evaluate(element, XPathConstants.STRING);
+ description = description.trim();
+
+ // keywords
+ expr = xpath.compile("//lom:general/lom:keyword");
+ NodeList keywordsList = (NodeList)expr.evaluate(element, XPathConstants.NODESET);
+ String keywords = "";
+ for (int i = 0; i < keywordsList.getLength(); i++)
+ {
+ keywords += keywordsList.item(i).getTextContent().trim()+" ";
+ }
+
+ // author, author!
+ expr = xpath.compile("//lom:centity/lom:vcard");
+ String author = (String)expr.evaluate(element, XPathConstants.STRING);
+ //System.out.println("Author before replace "+author);
+ author = author.replaceAll("(?i)BEGIN:VCARD FN:","");
+ author = author.replaceAll("(?i)END:VCARD","");
+ author = author.trim();
+ //System.out.println("Author after replace "+author);
+
+ // location, location, location!
+ expr = xpath.compile("//lom:technical/lom:location");
+ String location = (String)expr.evaluate(element, XPathConstants.STRING);
+ location = location.trim();
+
+ // software
+ expr = xpath.compile("//lom:otherplatformrequirements/lom:langstring");
+ String software = (String)expr.evaluate(element, XPathConstants.STRING);
+ software = software.trim();
+
+ // taxon
+ // xpath string - find a classification node that has purpose/source/langstring contains imsmd
+ // then get taxonpath/taxon/entry/langstring
+ expr = xpath.compile("//lom:classification/lom:purpose/lom:source/lom:langstring[contains(.,'imsmd')]"+
+ "/ancestor::lom:classification/lom:taxonpath/lom:taxon/lom:entry/lom:langstring");
+ String taxon = (String)expr.evaluate(element,XPathConstants.STRING);
+ taxon = taxon.trim();
+ if (taxon.equals(""))
+ {
+ // failing finding it in the *right* place, we'll just grab the first taxon we can find
+ expr = xpath.compile("//lom:taxon/lom:entry/lom:langstring");
+ taxon = (String)expr.evaluate(element,XPathConstants.STRING);
+ taxon = taxon.trim();
+ }
+
+ // switch namespaces - need QTI for these three
+ xpath.setNamespaceContext(ncImsQti);
+ expr = xpath.compile("//imsqti:timeDependent");
+ String timedep = (String)expr.evaluate(element,XPathConstants.STRING);
+
+ expr = xpath.compile("//imsqti:solutionAvailable");
+ String solutionAvailable = (String)expr.evaluate(element,XPathConstants.STRING);
+
+ expr = xpath.compile("//imsqti:toolVersion");
+ String toolVersion = (String)expr.evaluate(element,XPathConstants.STRING);
+
+ // now need CP for the file element
+ xpath.setNamespaceContext(ncImsCp);
+ expr = xpath.compile("//imsqti:file/@href");
+ String fileHref = (String)expr.evaluate(element,XPathConstants.STRING);
+
+
+ /*
+ System.out.println("Identifier: "+identifier);
+ System.out.println("Title "+title);
+ System.out.println("Description "+description);
+ System.out.println("Keywords "+keywords);
+ System.out.println("Author "+author);
+ System.out.println("Location "+location);
+ System.out.println("Software "+software);
+ System.out.println("Taxon "+taxon);
+ System.out.println("Time dep "+timedep);
+ System.out.println("Solution available "+solutionAvailable); */
+
+ // should be done - can now add them to a hashmap
+ LinkedHashMap<String,String> mdresults = new LinkedHashMap<String,String>();
+
+ mdresults.put(MQMetadata.IDENTIFIER[0],identifier);
+ mdresults.put(MQMetadata.TITLE[0],title);
+ mdresults.put(MQMetadata.DESCRIPTION[0],description);
+ mdresults.put(MQMetadata.KEYWORDS[0],keywords);
+ mdresults.put(MQMetadata.AUTHOR[0],author);
+ mdresults.put(MQMetadata.LOCATION[0],location);
+ mdresults.put(MQMetadata.SOFTWARE[0],software);
+ mdresults.put(MQMetadata.TAXON[0],taxon);
+ mdresults.put(MQMetadata.TIMEDEPENDENT[0],timedep);
+ mdresults.put(MQMetadata.SOLUTIONAVAILABLE[0],solutionAvailable);
+ mdresults.put(MQMetadata.TOOLVERSION[0],toolVersion);
+ mdresults.put(MQMetadata.FILENAME[0],fileHref);
+
+ return mdresults;
+ }
+ catch (Exception e)
+ {
+ // TODO: Needs more cowbell. And error trapping.
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ // private methods for quick and dirty pretty printing of XML from a string. Used
+ // so that what we get of the metadata to XML methods aren't a horrible mess.
+ private static String format(String unformattedXml) {
+ try {
+ final Document document = parseXmlFile(unformattedXml);
+
+ OutputFormat format = new OutputFormat(document);
+ format.setLineWidth(65);
+ format.setIndenting(true);
+ format.setIndent(4);
+ Writer out = new StringWriter();
+ XMLSerializer serializer = new XMLSerializer(out, format);
+ serializer.serialize(document);
+
+ return out.toString();
+ } catch (IOException e) {}
+ // should be impossible - file is bundled in resource
+ return null;
+ }
+
+ private static Document parseXmlFile(String in) {
+ try {
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ DocumentBuilder db = dbf.newDocumentBuilder();
+ InputSource is = new InputSource(new StringReader(in));
+ return db.parse(is);
+ } catch (ParserConfigurationException e) {} catch (SAXException e) {} catch (IOException e) {}
+ // exceptions should be impossible - the XML being passed is fixed within a resource and
+ // thus won't ever deviate from a valid XML.
+ return null;
+ }
}
Modified: Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/MQMetadata.java
===================================================================
--- Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/MQMetadata.java 2010-03-19 09:55:12 UTC (rev 2388)
+++ Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/MQMetadata.java 2010-03-19 12:56:29 UTC (rev 2389)
@@ -35,16 +35,61 @@
import java.util.ArrayList;
-import org.qtitools.mathqurate.model.MQModel.AttribValue;
-
-
/**
* The Class MQMetadata. Contains the meta-data for an assessment item and the Mathqurate meta-data.
*
- * @author James Annesley <j.a...@ki...>
+ * PN 17/3/10: Most of the metadata handling has been shifted to MQContentPackage itself in the form
+ * of a hashmap attribute, which makes it much easier to add support for additional metadata fields.
+ * Apart from the constant declarations below, MQMetadata is now something of a vestigial compatibility
+ * class for parts of the code that still expect metadata to be passed around in the form of an
+ * arraylist (which ultimately I'll clean up).
+ *
+ * @author James Annesley
+ * @author Paul Neve
*/
public class MQMetadata {
-
+
+ /**
+ * Constants for supported metadata
+ * These are used throughout the app when handling metadata
+ */
+
+ public static final String[] IDENTIFIER = {"IDENTIFIER","Question identifier"};
+ public static final String[] ADAPTIVE = {"ADAPTIVE","Is question adaptive?"};
+ public static final String[] TIMEDEPENDENT = {"TIMEDEPENDENT","Is question time dependent?"};
+ public static final String[] SOLUTIONAVAILABLE = {"SOLUTIONAVAILABLE","Is solution available?"};
+ public static final String[] TITLE = {"TITLE","Question title"};
+ public static final String[] AUTHORINGTOOL = {"AUTHORINGTOOL","Authoring tool"};
+ public static final String[] LABEL = {"LABEL","Question label"};
+ public static final String[] LANG = {"LANG","Question language"};
+ public static final String[] TOOLVERSION = {"TOOLVERSION","Authoring tool version"};
+ public static final String[] AUTHOR = {"AUTHOR","Question author"};
+ public static final String[] DESCRIPTION = {"DESCRIPTION","Description"};
+ public static final String[] KEYWORDS = {"KEYWORDS","Keywords"};
+ public static final String[] LOCATION = {"LOCATION","Location in repository"};
+ public static final String[] SOFTWARE = {"SOFTWARE","Software required"};
+ public static final String[] TICKET = {"TICKET","Minibix ticket no"};
+ public static final String[] DATETIME = {"DATETIME","Time/date stamp"};
+ public static final String[] URL = {"URL","Minibix URL"};
+ public static final String[] LICENCEURL = {"LICENCEURL","Licence URL"};
+ public static final String[] TAXON = {"@TAXON","@Taxonomy Entry"};
+ public static final String[] REPODOMAIN = { "REPODOMAIN","Repository Domain"};
+ public static final String[] FILENAME = { "@FILENAME","@Filename"};
+ // ones prefixed @ do not appear in metadata window and are not user settable
+
+ // composite metadata constant. Used to enumerate a complete list of metadata
+ // fieldnames/fields
+ public static final String[][] MQFIELDNAMES = {
+ IDENTIFIER, ADAPTIVE, TIMEDEPENDENT, SOLUTIONAVAILABLE, TITLE, AUTHORINGTOOL,
+ LABEL, LANG, TOOLVERSION, AUTHOR, DESCRIPTION, KEYWORDS, LOCATION, SOFTWARE, TICKET, URL, LICENCEURL,
+ TAXON, REPODOMAIN, FILENAME
+ };
+
+ ///// TO ADD A NEW METADATA FIELD, YOU WILL NEED TO ADD IT TO BOTH THE LIST OF CONSTANTS AND
+ ///// THE COMPOSITE CONSTANT ABOVE, AS WELL AS ADDING IT TO THE md-template.xml IN RESOURCES.
+ ///// YOU WILL ALSO NEED TO ADD SUPPORT FOR IT IN MQContentPackage FOR BOTH FROM-XML AND TO-XML
+ ///// SCENARIOS.
+
/** The Assessment Item meta-data (mainly). */
private ArrayList<AttribValue> metadataArray = new ArrayList<AttribValue>();
@@ -63,6 +108,13 @@
this.taxon = taxon;
}
+ /**
+ * default constructor
+ */
+ public MQMetadata()
+ {
+ }
+
public ArrayList<AttribValue> getMetadataArray() {
return metadataArray;
}
Modified: Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/MQModel.java
===================================================================
--- Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/MQModel.java 2010-03-19 09:55:12 UTC (rev 2388)
+++ Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/MQModel.java 2010-03-19 12:56:29 UTC (rev 2389)
@@ -105,6 +105,7 @@
import org.qtitools.mathassess.xsd.mathassess.CasType;
import org.qtitools.mathqurate.controller.DefaultController;
+import org.qtitools.mathqurate.utilities.CPBuildException;
import org.qtitools.mathqurate.utilities.IMSQTINamespacePrefixMapper;
import org.qtitools.mathqurate.utilities.JAXBCommentFactory;
import org.qtitools.mathqurate.utilities.MathMLNamespacePrefixMapper;
@@ -198,66 +199,7 @@
return null;
}
-
- /**
- * The Class AttribValue.
- * Used to store Attribute value pairs. Used for in the Meta-data structure
- */
- public class AttribValue {
-
- /** The Attribute */
- String Attrib = "";
-
- /** The Value. */
- String Value = "";
-
- /**
- * Instantiates a new attrib value.
- *
- * @param attrib the attrib
- * @param value the value
- */
- public AttribValue(String attrib, String value) {
- Attrib = attrib;
- if (value != null) Value = value; else value = "";
- }
-
- /**
- * Gets the attrib.
- *
- * @return the attrib
- */
- public String getAttrib() {
- return Attrib;
- }
-
- /**
- * Gets the value.
- *
- * @return the value
- */
- public String getValue() {
- return Value;
- }
-
- /**
- * Sets the attrib.
- *
- * @param attrib the new attrib
- */
- public void setAttrib(String attrib) {
- Attrib = attrib;
- }
-
- /**
- * Sets the value.
- *
- * @param value the new value
- */
- public void setValue(String value) {
- Value = value;
- }
- }
+
/**
* The Class NodeHelper.
@@ -338,7 +280,7 @@
/** The mathqurate object factory.
* This is a class of functions useful for marshalling and unmarshalling */
- static MQObjectFactory mathqurateObjectFactory;
+ public static MQObjectFactory mathqurateObjectFactory;
/**
* The taxon array list. Contains the taxonomy values. Will be pulled from
@@ -1090,9 +1032,9 @@
MQMain.logger.debug("MQModel: callGetMQMetadata");
AssessmentItemType assessmentItemType = assessmentItemHelper.getAssessmentItemType();
-
+
ArrayList<AttribValue> metadataArray = new ArrayList<AttribValue>();
-
+/*
AttribValue identifier = new AttribValue("Question identifier",
assessmentItemType.getIdentifier());
AttribValue adaptive = new AttribValue("Is question adaptive?", String
@@ -1111,14 +1053,13 @@
AttribValue avauthor = new AttribValue("Question Author", assessmentItemHelper.get("author"));
//AttribValue avdatetime = new AttribValue("Date time", assessmentItemHelper.getDatetime());
- AttribValue avdescription = new AttribValue("Minibix item description", assessmentItemHelper.get("description"));
- AttribValue avticket = new AttribValue("Minibix item bank ticket", assessmentItemHelper.get("ticket"));
- AttribValue avurl = new AttribValue("Minibix item bank URL", assessmentItemHelper.get("url"));
-
-// AttribValue keywords = new AttribValue("Keywords", assessmentItemHelper.getKeywords());
-// AttribValue location = new AttribValue("Location",assessmentItemHelper.getLocation());
-// AttribValue software = new AttribValue("Software",assessmentItemHelper.getSoftware());
-
+ AttribValue avdescription = new AttribValue("Description", assessmentItemHelper.get("description"));
+ AttribValue avkeywords = new AttribValue("Keywords",assessmentItemHelper.get("keywords"));
+ AttribValue avlocation = new AttribValue("Repository location",assessmentItemHelper.get("location"));
+ AttribValue avsoftware = new AttribValue("Software Required",assessmentItemHelper.get("software"));
+ AttribValue avticket = new AttribValue("Minibix ticket no", assessmentItemHelper.get("ticket"));
+ AttribValue avurl = new AttribValue("Minibix URL", assessmentItemHelper.get("url"));
+
metadataArray.add(avauthor);
//metadataArray.add(avdatetime);
metadataArray.add(toolname);
@@ -1130,15 +1071,19 @@
metadataArray.add(label);
metadataArray.add(lang);
metadataArray.add(avdescription);
-// metadataArray.add(keywords);
-// metadataArray.add(location);
-// metadataArray.add(software);
+
+ metadataArray.add(avkeywords);
+ metadataArray.add(avlocation);
+ metadataArray.add(avsoftware);
//newArray.add(avtaxon);
metadataArray.add(avticket);
- metadataArray.add(avurl);
+ metadataArray.add(avurl); */
- MQMetadata metadata = new MQMetadata(metadataArray, assessmentItemHelper.get("taxon"));
-
+ //metadataArray = assessmentItemHelper.getMetadata().getMetadataArray();
+
+ //MQMetadata metadata = new MQMetadata(metadataArray, assessmentItemHelper.get("taxon"));
+ MQMetadata metadata = assessmentItemHelper.getMetadata();
+
firePropertyChange(DefaultController.GET_MQMETADATA_PROPERTY,
"", metadata);
}
@@ -1764,6 +1709,7 @@
assessmentItemType = mathqurateObjectFactory.getAssessmentItemType(filename);
assessmentItemHelper = new MQContentPackage(assessmentItemType);
+ assessmentItemHelper.set(MQMetadata.FILENAME[0], filename);
setMQAssessmentItem();
@@ -1789,37 +1735,37 @@
}
/**
- * Calls the import content package.
- * Sets the member variables for the model not contained in the
- * assessment item. Then sets the internal meta-data structure, which
- * triggers the views to update.
- *
- * @param metadata the metadata
+ * Imports a content package from a zip file, and, if appropriate, sets the Minibix-specific
+ * metadata fields. Needs an object array containing
+ * [0] - zip file
+ * [1] - Minibix ticket number
+ * [2] - Minibix URL
+ * @param bits
+ * @throws CPBuildException
*/
- public void callImportContentPackage(MQContentPackage metadata) {
+ public void callImportContentPackage(Object[] bits) throws CPBuildException {
+ File zipfile = (File)bits[0];
+
MQMain.logger.debug("MQModel: callImportContentPackage");
-
+ /*
AssessmentItemType assessmentItemType;
assessmentItemType = mathqurateObjectFactory.getAssessmentItemType(metadata.get("@FILENAME"));
assessmentItemHelper = new MQContentPackage(assessmentItemType);
- /*
- assessmentItemHelper.setAuthor(metadata.getAuthor());
- assessmentItemHelper.setDatetime(metadata.getDatetime());
-
- assessmentItemHelper.setTaxon(metadata.getTaxon());
-
- assessmentItemHelper.setTicket(metadata.getTicket());
-
- assessmentItemHelper.setDescription(metadata.getDescription());
- */
-
assessmentItemHelper.setMetadataMap(metadata);
+ setMQAssessmentItem(); */
+
+ assessmentItemHelper = mathqurateObjectFactory.getCPfromZip(zipfile);
+ if (bits.length > 1)
+ {
+ assessmentItemHelper.set(MQMetadata.TICKET[0],(String)bits[1]);
+ assessmentItemHelper.set(MQMetadata.URL[0],(String)bits[2]);
+ }
setMQAssessmentItem();
}
@@ -2719,8 +2665,10 @@
MQMain.logger.debug("MQModel: callSetMQMetadata");
- AssessmentItemType assessmentItemType = assessmentItemHelper.getAssessmentItemType();
+ //AssessmentItemType assessmentItemType = assessmentItemHelper.getAssessmentItemType();
+ /*
+
Iterator<AttribValue> it = metadata.getMetadataArray().iterator();
while (it.hasNext()) {
@@ -2755,7 +2703,7 @@
} /*else if (attribvalue.getAttrib().equals("Date time")) {
assessmentItemHelper.setDatetime(attribvalue.getValue());
} */
- // else add it with spaces removed in upper case
+ /* // else add it with spaces removed in upper case
else // catch all for attribs we don't "know" about
{
String key = attribvalue.getAttrib().toUpperCase().replace(" ","");
@@ -2763,15 +2711,18 @@
String value = attribvalue.getValue();
assessmentItemHelper.set(key, value);
}
- }
+ } */
- String taxon = metadata.getTaxon();
+ /*String taxon = metadata.getTaxon();
if (taxon!=null){
assessmentItemHelper.set("taxon",metadata.getTaxon());
- }
+ } */
+
+ assessmentItemHelper.setMetadata(metadata);
+ //System.out.println(assessmentItemHelper.getAssessmentItemType().getTitle());
- assessmentItemHelper.setAssessmentItemType(assessmentItemType);
+ //assessmentItemHelper.setAssessmentItemType(assessmentItemType);
setMQAssessmentItem();
}
Modified: Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/MQObjectFactory.java
===================================================================
--- Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/MQObjectFactory.java 2010-03-19 09:55:12 UTC (rev 2388)
+++ Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/model/MQObjectFactory.java 2010-03-19 12:56:29 UTC (rev 2389)
@@ -33,35 +33,36 @@
package org.qtitools.mathqurate.model;
-import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
-import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
-import javax.xml.XMLConstants;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
+import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.Source;
import javax.xml.transform.sax.SAXSource;
-import javax.xml.transform.stream.StreamSource;
-import javax.xml.validation.Schema;
-import javax.xml.validation.SchemaFactory;
-import javax.xml.validation.Validator;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
import org.imsglobal.xsd.imsqti_v2p1.AssessmentItemType;
import org.imsglobal.xsd.imsqti_v2p1.BaseTypeType;
@@ -84,10 +85,14 @@
import org.imsglobal.xsd.imsqti_v2p1.TemplateProcessingType;
import org.imsglobal.xsd.imsqti_v2p1.TextEntryInteractionType;
import org.imsglobal.xsd.imsqti_v2p1.ValueType;
+import org.qtitools.mathqurate.utilities.CPBuildException;
+import org.qtitools.mathqurate.utilities.ZipHelper;
import org.qtitools.mathqurate.view.MQMain;
import org.w3._1998.math.mathml.MathType;
import org.w3c.dom.Document;
+import org.w3c.dom.Element;
import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
@@ -2078,7 +2083,7 @@
}
/**
- * Gets the assessmentItem from an XML string.
+ * Gets the assessmentItem from a file, in the form of a string filename.
*
* @param filename the filename
*
@@ -2212,7 +2217,7 @@
} */
/**
- * Gets the AssessmentItemType as a File.
+ * Saves the AssessmentItemType as a File.
*
* @param type the assessment Item Type
* @param filename the filename
@@ -2354,4 +2359,176 @@
}
}
+ public MQContentPackage getCPfromZip(File zipfile) throws CPBuildException
+ {
+ String systemTemp = System.getProperty("java.io.tmpdir");
+ File unzipLocation = new File(systemTemp+"/"+"tempunzip");
+ try {
+ ZipHelper.unzip(zipfile,unzipLocation);
+ } catch (IOException e) {
+ ZipHelper.deleteDirectory(unzipLocation);
+ throw new CPBuildException(CPBuildException.ErrorType.IOEXCEPTION);
+ }
+
+ // we should now have our unpacked zip in TEMP/tempunzip.
+ // We do at least know the CP manifest will ALWAYS be in the root,
+ // in imsmanifest.xml. If it isn't, this is not an IMS CP.
+ File manifest = new File(unzipLocation+"/imsmanifest.xml");
+ if (!manifest.exists())
+ {
+ ZipHelper.deleteDirectory(unzipLocation);
+ throw new CPBuildException(CPBuildException.ErrorType.NOMANIFEST);
+ }
+
+ // now check how many resources there are in the content package.
+ // MQ inherently only deals with single questions with no subsidiary files, e.g.
+ // graphics etc. If more than one resource in the file, fergeddaboutit!
+
+ // create document from our string
+ DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+ docFactory.setNamespaceAware(true);
+ DocumentBuilder docBuilder = null;
+ Document doc = null;
+ try {
+ docBuilder = docFactory.newDocumentBuilder();
+ doc = docBuilder.parse(manifest);
+ } catch (Exception e) {
+ ZipHelper.deleteDirectory(unzipLocation);
+ throw new CPBuildException(CPBuildException.ErrorType.BADMANIFEST);
+ }
+
+ Element element = doc.getDocumentElement();
+
+ XPathFactory xpfactory = XPathFactory.newInstance();
+ XPath xpath = xpfactory.newXPath();
+
+ // Have to do this if namespaceAware = true, otherwise XPATH simply doesn't work.
+ NamespaceContext ncImsCp = new NamespaceContext()
+ {
+
+ public String getNamespaceURI(String prefix) {
+ return("http://www.imsglobal.org/xsd/imscp_v1p1");
+ }
+
+ public String getPrefix(String namespaceURI) {
+ return "cp";
+ }
+
+ public Iterator getPrefixes(String namespaceURI) {
+ return null;
+ }
+ };
+ xpath.setNamespaceContext(ncImsCp);
+
+ // now we can do an xpath to retrieve resource elements
+ XPathExpression expr = null;
+ NodeList resources = null;
+ try {
+ expr = xpath.compile("//cp:resource");
+ resources = (NodeList)expr.evaluate(element, XPathConstants.NODESET);
+ } catch (XPathExpressionException e) {} // can't happen
+
+ if (resources.getLength() != 1)
+ {
+ ZipHelper.deleteDirectory(unzipLocation);
+ //System.out.println(resources.getLength());
+ throw new CPBuildException(CPBuildException.ErrorType.RESNOWRONG);
+ }
+
+ // Right. After that lot we can assume that we have something that MQ can
+ // make sense of. Let's get the metadata
+ LinkedHashMap<String,String> metadata =
+ MQContentPackage.metadataFromFile(unzipLocation+"/imsmanifest.xml");
+
+ // we should be able to pull the actual QTI from the filname
+ String qtiFilename = metadata.get(MQMetadata.FILENAME[0]);
+ qtiFilename = unzipLocation+"/"+qtiFilename;
+ //System.out.println(qtiFilename);
+
+ // create an assessment item from our XML
+ AssessmentItemType assItem = getAssessmentItemType(qtiFilename);
+
+ MQContentPackage mqCp = new MQContentPackage(assItem);
+ mqCp.setMetadataMap(metadata);
+
+ // clean up work dir
+ ZipHelper.deleteDirectory(unzipLocation);
+
+ return mqCp;
+
+ }
+
+ public void makeZipFromCP(MQContentPackage cp, String zipFilename)
+ {
+ makeZipFromCP(cp,new File(zipFilename));
+ }
+
+ /**
+ * Makes a zipfile from the content package
+ * @param cp content package
+ * @param zipfile zipfile
+ */
+
+ public void makeZipFromCP(MQContentPackage cp, File zipfile)
+ {
+ // first, make a temporary directory to work in
+ String systemTemp = System.getProperty("java.io.tmpdir");
+ String zipLocation = systemTemp+"tempzip";
+ new File(zipLocation).mkdir();
+
+ // MQ's CPs will flatten out the directory structure - seems little point
+ // having a ZIP file with a folder in that contains one file - so drop any
+ // directories from the filename
+ String filename = cp.get(MQMetadata.FILENAME[0]);
+ String[] x = filename.split("/");
+ filename = x[x.length-1];
+ filename = filename.trim();
+ if (filename.equals("")) filename = "qtiQuestion.xml";
+ cp.set(MQMetadata.FILENAME[0], filename);
+
+ // right. We can now save our QTI XML into our directory under the name filename
+ try {
+ getTypeAsFile(cp.getAssessmentItemType(), zipLocation+"/"+filename);
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (JAXBException e) {
+ e.printStackTrace();
+ }
+
+ // now stash our manifest in there
+ String manifest = cp.metadataToXml();
+ FileOutputStream fos = null;
+ OutputStreamWriter out = null;
+ try
+ {
+ fos = new FileOutputStream(zipLocation+"/"+"imsmanifest.xml");
+ out = new OutputStreamWriter(fos, "UTF-8");
+ out.write(manifest);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ finally
+ {
+ try {out.close();} catch (IOException e) {}
+ }
+
+ // finally, zip up our two files
+ try {
+ //System.out.println(zipLocation+"/"+filename);
+ ZipHelper.zipDirectory(new File(zipLocation), zipfile);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ // finally, finally, delete the work dir
+ ZipHelper.deleteDirectory(zipLocation);
+
+ // TODO: needs more cowbell. And error trapping.
+
+ }
+
}
Added: Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/utilities/CPBuildException.java
===================================================================
--- Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/utilities/CPBuildException.java (rev 0)
+++ Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/utilities/CPBuildException.java 2010-03-19 12:56:29 UTC (rev 2389)
@@ -0,0 +1,27 @@
+package org.qtitools.mathqurate.utilities;
+
+public class CPBuildException extends Exception {
+
+ private String exceptionDetails;
+
+ public enum ErrorType
+ {
+ IOEXCEPTION, NOMANIFEST, RESNOWRONG, BADMANIFEST,
+ }
+
+ public ErrorType errorType;
+
+ public CPBuildException(ErrorType errorType)
+ {
+ this.errorType = errorType;
+ }
+
+ public void setExceptionDetails(String exceptionDetails) {
+ this.exceptionDetails = exceptionDetails;
+ }
+
+ public String getExceptionDetails() {
+ return exceptionDetails;
+ }
+
+}
Deleted: Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/utilities/ContentPackageBuilder.java
===================================================================
--- Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/utilities/ContentPackageBuilder.java 2010-03-19 09:55:12 UTC (rev 2388)
+++ Mathqurate/trunk/mathqurate/src/main/java/org/qtitools/mathqurate/utilities/ContentPackageBuilder.java 2010-03-19 12:56:29 UTC (rev 2389)
@@ -1,459 +0,0 @@
-/*
- *
- Open Source License
-
- Copyright (c) 2009, Kingston University.
-
- All rights reserved.
-
- Redistribution and use of this software in source and binary forms
- (where applicable), with or without modification, are permitted provided
- that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions, and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions, and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- * Neither the name of the Kingston University, nor the names of any other
- contributors to the software, may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
- OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
- OR OTHER DEALINGS IN THE SOFTWARE.
- *
- */
-
-package org.qtitools.mathqurate.utilities;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.UUID;
-
-import org.apache.commons.io.IOUtils;
-import org.qtitools.mathqurate.model.MQContentPackage;
-import org.qtitools.mathqurate.view.MQMain;
-
-
-import uk.ac.cam.caret.imscp.api.BadParseException;
-import uk.ac.cam.caret.imscp.api.ContentPackage;
-import uk.ac.cam.caret.imscp.api.Manifest;
-import uk.ac.cam.caret.imscp.api.Metadata;
-import uk.ac.cam.caret.imscp.api.PackageDirectory;
-import uk.ac.cam.caret.imscp.api.Resource;
-import uk.ac.cam.caret.imscp.impl.ZipFilePackageFacto...
[truncated message content] |