Skip to content
This repository
Browse code

Implemented the special export for SVGDraw and Mysystem2 steps that

generates a zip file that contains all the student work for that step.
  • Loading branch information...
commit 7eecba16f72bf2c840aae0287cf2d2736cccc54b 1 parent 21b220a
Geoffrey Kwan authored May 25, 2012
1,133  src/main/java/vle/web/VLEGetSpecialExport.java
... ...
@@ -0,0 +1,1133 @@
  1
+package vle.web;
  2
+
  3
+import java.io.BufferedOutputStream;
  4
+import java.io.File;
  5
+import java.io.FileInputStream;
  6
+import java.io.IOException;
  7
+import java.sql.Timestamp;
  8
+import java.text.DateFormat;
  9
+import java.util.ArrayList;
  10
+import java.util.Date;
  11
+import java.util.HashMap;
  12
+import java.util.Iterator;
  13
+import java.util.List;
  14
+import java.util.Vector;
  15
+import java.util.zip.ZipEntry;
  16
+import java.util.zip.ZipOutputStream;
  17
+
  18
+import javax.servlet.ServletException;
  19
+import javax.servlet.ServletOutputStream;
  20
+import javax.servlet.http.HttpServletRequest;
  21
+import javax.servlet.http.HttpServletResponse;
  22
+
  23
+import org.apache.commons.io.FileUtils;
  24
+import org.apache.commons.io.IOUtils;
  25
+import org.apache.poi.ss.usermodel.Workbook;
  26
+import org.json.JSONArray;
  27
+import org.json.JSONException;
  28
+import org.json.JSONObject;
  29
+
  30
+import utils.FileManager;
  31
+import vle.VLEServlet;
  32
+import vle.domain.node.Node;
  33
+import vle.domain.user.UserInfo;
  34
+import vle.domain.work.StepWork;
  35
+
  36
+public class VLEGetSpecialExport extends VLEServlet {
  37
+
  38
+	private static final long serialVersionUID = 1L;
  39
+	
  40
+	//the max number of step work columns we need, only used for "allStudentWork"
  41
+	private int maxNumberOfStepWorkParts = 1;
  42
+	
  43
+	private HashMap<String, JSONObject> nodeIdToNodeContent = new HashMap<String, JSONObject>();
  44
+	
  45
+	private HashMap<String, JSONObject> nodeIdToNode = new HashMap<String, JSONObject>();
  46
+
  47
+	private HashMap<String, String> nodeIdToNodeTitles = new HashMap<String, String>();
  48
+	
  49
+	private HashMap<String, String> nodeIdToNodeTitlesWithPosition = new HashMap<String, String>();
  50
+	
  51
+	private HashMap<Integer, String> workgroupIdToPeriodName = new HashMap<Integer, String>();
  52
+	
  53
+	private HashMap<Integer, String> workgroupIdToStudentLogins = new HashMap<Integer, String>();
  54
+	
  55
+	private HashMap<Long, JSONArray> workgroupIdToStudentAttendance = new HashMap<Long, JSONArray>();
  56
+	
  57
+	private List<String> nodeIdList = new Vector<String>();
  58
+	
  59
+	//the start time of the run (when the run was created)
  60
+	private String startTime = "N/A";
  61
+	
  62
+	//the end time of the run (when the run was archived)
  63
+	private String endTime = "N/A";
  64
+	
  65
+	//holds the teacher's username and workgroupid
  66
+	private JSONObject teacherUserInfoJSONObject;
  67
+	
  68
+	//run and project attributes
  69
+	private String runId = "";
  70
+	private String runName = "";
  71
+	private String projectId = "";
  72
+	private String parentProjectId = "";
  73
+	private String projectName = "";
  74
+	private String nodeId = "";
  75
+	
  76
+	//holds all the teacher workgroup ids
  77
+	private List<String> teacherWorkgroupIds = null;
  78
+	
  79
+	//the custom steps to export
  80
+	List<String> customSteps = null;
  81
+	
  82
+	//the number of columns to width auto size
  83
+	int numColumnsToAutoSize = 0;
  84
+	
  85
+	//the project meta data
  86
+	private JSONObject projectMetaData = null;
  87
+	
  88
+	private static long debugStartTime = 0;
  89
+	
  90
+	//the type of export "latestStudentWork" or "allStudentWork"
  91
+	private String exportType = "";
  92
+	
  93
+	/**
  94
+	 * Clear the instance variables because only one instance of a servlet
  95
+	 * is ever created
  96
+	 */
  97
+	private void clearVariables() {
  98
+		//the max number of step work columns we need, only used for "allStudentWork"
  99
+		maxNumberOfStepWorkParts = 1;
  100
+		
  101
+		//mappings for the project and user
  102
+		nodeIdToNodeContent = new HashMap<String, JSONObject>();
  103
+		nodeIdToNode = new HashMap<String, JSONObject>();
  104
+		nodeIdToNodeTitles = new HashMap<String, String>();
  105
+		nodeIdToNodeTitlesWithPosition = new HashMap<String, String>();
  106
+		workgroupIdToPeriodName = new HashMap<Integer, String>();
  107
+		workgroupIdToStudentLogins = new HashMap<Integer, String>();
  108
+		workgroupIdToStudentAttendance = new HashMap<Long, JSONArray>();
  109
+		
  110
+		//the list of node ids in the project
  111
+		nodeIdList = new Vector<String>();
  112
+		
  113
+		//the start time of the run (when the run was created)
  114
+		startTime = "N/A";
  115
+		
  116
+		//the end time of the run (when the run was archived)
  117
+		endTime = "N/A";
  118
+		
  119
+		//holds the teacher's username and workgroupid
  120
+		teacherUserInfoJSONObject = null;
  121
+		
  122
+		//run and project attributes
  123
+		runId = "";
  124
+		runName = "";
  125
+		projectId = "";
  126
+		parentProjectId = "";
  127
+		projectName = "";
  128
+		nodeId = "";
  129
+		
  130
+		//holds all the teacher workgroup ids
  131
+		teacherWorkgroupIds = null;
  132
+		
  133
+		//holds the custom steps to export data for
  134
+		customSteps = new Vector<String>();
  135
+		
  136
+		//reset the number of columns to width auto size
  137
+		numColumnsToAutoSize = 0;
  138
+		
  139
+		//reset the project meta data
  140
+		projectMetaData = null;
  141
+		
  142
+		//holds the export type
  143
+		exportType = "";
  144
+	}
  145
+	
  146
+	/**
  147
+	 * Compare two different millisecond times
  148
+	 * @param time1 the earlier time (smaller)
  149
+	 * @param time2 the later time (larger)
  150
+	 * @return the difference between the times in seconds
  151
+	 */
  152
+	private long getDifferenceInSeconds(long time1, long time2) {
  153
+		return (time2 - time1) / 1000;
  154
+	}
  155
+	
  156
+	/**
  157
+	 * Display the difference between the current time and the
  158
+	 * start time
  159
+	 * @param label the label to display with the time difference
  160
+	 */
  161
+	@SuppressWarnings("unused")
  162
+	private void displayCurrentTimeDifference(String label) {
  163
+		long currentTime = new Date().getTime();
  164
+		System.out.println(label + ": " + getDifferenceInSeconds(debugStartTime, currentTime));
  165
+	}
  166
+	
  167
+	/**
  168
+	 * Generates and returns an excel xls of exported student data.
  169
+	 */
  170
+	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  171
+		/*
  172
+		 * clear the instance variables because only one instance of a servlet
  173
+		 * is ever created
  174
+		 */
  175
+		clearVariables();
  176
+		
  177
+		//obtain the start time for debugging purposes
  178
+		debugStartTime = new Date().getTime();
  179
+
  180
+		//holds the workgroup ids and period ids of the students
  181
+		String classmateUserInfos = (String) request.getAttribute("classmateUserInfos");
  182
+
  183
+		//holds the workgroup id of the teacher
  184
+		String teacherUserInfo = (String) request.getAttribute("teacherUserInfo");
  185
+		
  186
+		//holds the workgroup ids of the shared teachers
  187
+		String sharedTeacherUserInfos = (String) request.getAttribute("sharedTeacherUserInfos");
  188
+		
  189
+		//get the path of the project file on the server
  190
+		String projectPath = (String) request.getAttribute("projectPath");
  191
+		
  192
+		//get the path of the project meta data
  193
+		String projectMetaDataJSONString = (String) request.getAttribute("projectMetaData");
  194
+
  195
+		//get the path of the vlewrapper base dir
  196
+		String vlewrapperBaseDir = (String) request.getAttribute("vlewrapperBaseDir");
  197
+
  198
+		try {
  199
+			//get the project meta data JSON object
  200
+			projectMetaData = new JSONObject(projectMetaDataJSONString);
  201
+		} catch (JSONException e2) {
  202
+			e2.printStackTrace();
  203
+		}
  204
+		
  205
+		//holds the run info
  206
+		String runInfo = (String) request.getAttribute("runInfo");
  207
+		
  208
+		try {
  209
+			JSONObject runInfoJSONObject = new JSONObject(runInfo);
  210
+			
  211
+			if(runInfoJSONObject.has("startTime")) {
  212
+				//get the start time as a string
  213
+				String startTimeString = runInfoJSONObject.getString("startTime");
  214
+				
  215
+				if(startTimeString != null && !startTimeString.equals("null") && !startTimeString.equals("")) {
  216
+					long startTimeLong = Long.parseLong(startTimeString);
  217
+					
  218
+					Timestamp startTimeTimestamp = new Timestamp(startTimeLong);
  219
+					
  220
+					//get the date the run was created
  221
+					startTime = timestampToFormattedString(startTimeTimestamp);					
  222
+				}
  223
+			}
  224
+			
  225
+			if(runInfoJSONObject.has("endTime")) {
  226
+				//get the end time as a string
  227
+				String endTimeString = runInfoJSONObject.getString("endTime");
  228
+				
  229
+				if(endTimeString != null && !endTimeString.equals("null") && !endTimeString.equals("")) {
  230
+					long endTimeLong = Long.parseLong(endTimeString);
  231
+					
  232
+					Timestamp endTimeTimestamp = new Timestamp(endTimeLong);
  233
+					
  234
+					//get the date the run was archived
  235
+					endTime = timestampToFormattedString(endTimeTimestamp);
  236
+				}
  237
+			}
  238
+		} catch (JSONException e1) {
  239
+			e1.printStackTrace();
  240
+		}
  241
+		
  242
+		//get the student attendance data which is a JSONArray string
  243
+		String studentAttendanceString = (String) request.getAttribute("studentAttendance");
  244
+		
  245
+		JSONArray studentAttendanceArray = new JSONArray();
  246
+		try {
  247
+			//create the JSONArray from the student attendance data
  248
+			studentAttendanceArray = new JSONArray(studentAttendanceString);
  249
+		} catch (JSONException e1) {
  250
+			e1.printStackTrace();
  251
+		}
  252
+		
  253
+		//parse the student attendance data so we can query it later
  254
+		parseStudentAttendance(studentAttendanceArray);
  255
+		
  256
+		//the List that will hold all the workgroup ids
  257
+		Vector<String> workgroupIds = new Vector<String>();
  258
+		
  259
+		//get the run and project attributes
  260
+		runId = request.getParameter("runId");
  261
+		runName = request.getParameter("runName");
  262
+		projectId = request.getParameter("projectId");
  263
+		projectName = request.getParameter("projectName");
  264
+		parentProjectId = request.getParameter("parentProjectId");
  265
+		nodeId = request.getParameter("nodeId");
  266
+		
  267
+		//the export type "latestStudentWork" or "allStudentWork"
  268
+		exportType = request.getParameter("exportType");
  269
+		
  270
+		JSONArray customStepsArray = new JSONArray();
  271
+		
  272
+		//gather the custom steps if the teacher is requesting a custom export
  273
+		if(exportType.equals("customLatestStudentWork") || exportType.equals("customAllStudentWork")) {
  274
+			String customStepsArrayJSONString = request.getParameter("customStepsArray");
  275
+			
  276
+			try {
  277
+				customStepsArray = new JSONArray(customStepsArrayJSONString);
  278
+				
  279
+				//loop through all the node ids
  280
+				for(int x=0; x<customStepsArray.length(); x++) {
  281
+					//add the node id to our list of custom steps
  282
+					String nodeId = customStepsArray.getString(x);
  283
+					customSteps.add(nodeId);
  284
+				}
  285
+			} catch (JSONException e) {
  286
+				e.printStackTrace();
  287
+			}
  288
+		}
  289
+		
  290
+		//create a file handle to the project file
  291
+		File projectFile = new File(projectPath);
  292
+		FileManager fileManager = new FileManager();
  293
+		
  294
+		//the hash map to store workgroup id to period id
  295
+		HashMap<Integer, Integer> workgroupIdToPeriodId = new HashMap<Integer, Integer>();
  296
+		
  297
+		String teacherWorkgroupId = "";
  298
+		
  299
+		//create an array to hold all the teacher workgroup ids
  300
+		teacherWorkgroupIds = new ArrayList<String>();
  301
+		
  302
+		JSONObject project = null;
  303
+		
  304
+		try {
  305
+			//get the project JSON object
  306
+			project = new JSONObject(fileManager.getFileText(projectFile));
  307
+			
  308
+			//create the map of node ids to node titles
  309
+			makeNodeIdToNodeTitleAndNodeMap(project);
  310
+			
  311
+			/*
  312
+			 * create the list of node ids in the order they appear in the project.
  313
+			 * this also creates the map of node ides to node titles with positions
  314
+			 */
  315
+			makeNodeIdList(project);
  316
+			
  317
+			//get the nodes
  318
+			JSONArray nodes = project.getJSONArray("nodes");
  319
+			
  320
+			//loop through all the nodes
  321
+			for(int x = 0; x < nodes.length(); x++){
  322
+				//get a node
  323
+				JSONObject node = nodes.getJSONObject(x);
  324
+				
  325
+				try {
  326
+					//get the text from the file
  327
+					String fileText = fileManager.getFileText(new File(projectFile.getParentFile(), node.getString("ref")));
  328
+					
  329
+					//get the content for the node
  330
+					JSONObject nodeContent = new JSONObject(fileText);
  331
+					
  332
+					//put an entry into the hashmap with key as node id and value as JSON node content
  333
+					nodeIdToNodeContent.put(node.getString("identifier"), nodeContent);
  334
+				} catch(IOException e) {
  335
+					e.printStackTrace();
  336
+				} catch(JSONException e) {
  337
+					e.printStackTrace();
  338
+				}
  339
+			}
  340
+			
  341
+			//get the array of classmates
  342
+			JSONArray classmateUserInfosJSONArray = new JSONArray(classmateUserInfos);
  343
+			
  344
+			//get the teacher user info
  345
+			teacherUserInfoJSONObject = new JSONObject(teacherUserInfo);
  346
+			
  347
+			//get the owner teacher workgroup id
  348
+			teacherWorkgroupId = teacherUserInfoJSONObject.getString("workgroupId");
  349
+			
  350
+			//add the owner teacher
  351
+			teacherWorkgroupIds.add(teacherWorkgroupId);
  352
+			
  353
+			//get the shared teacher user infos
  354
+			JSONArray sharedTeacherUserInfosJSONArray = new JSONArray(sharedTeacherUserInfos);
  355
+			
  356
+			//loop through all the shared teacher user infos
  357
+			for(int z=0; z<sharedTeacherUserInfosJSONArray.length(); z++) {
  358
+				//get a shared teacher
  359
+				JSONObject sharedTeacherJSONObject = (JSONObject) sharedTeacherUserInfosJSONArray.get(z);
  360
+				
  361
+				if(sharedTeacherJSONObject != null) {
  362
+					if(sharedTeacherJSONObject.has("workgroupId")) {
  363
+						//get the shared teacher workgroup id
  364
+						String sharedTeacherWorkgroupId = sharedTeacherJSONObject.getString("workgroupId");
  365
+						
  366
+						//add the shared teacher workgroup id to the array
  367
+						teacherWorkgroupIds.add(sharedTeacherWorkgroupId);
  368
+					}
  369
+				}
  370
+			}
  371
+			
  372
+			
  373
+			//loop through all the classmates
  374
+			for(int y=0; y<classmateUserInfosJSONArray.length(); y++) {
  375
+				//get a classmate
  376
+				JSONObject classmate = classmateUserInfosJSONArray.getJSONObject(y);
  377
+				
  378
+				//make sure workgroupId and periodId exist and are not null
  379
+				if(classmate.has("workgroupId") && !classmate.isNull("workgroupId")) {
  380
+					//get the workgroup id for the classmate
  381
+					int workgroupId = classmate.getInt("workgroupId");
  382
+					
  383
+					if(classmate.has("periodId") && !classmate.isNull("periodId")) {
  384
+						//get the period id for the classmate
  385
+						int periodId = classmate.getInt("periodId");
  386
+						
  387
+						//put an entry into the hashmap with key as workgroup id and value as period id
  388
+						workgroupIdToPeriodId.put(workgroupId, periodId);
  389
+					}
  390
+					
  391
+					if(classmate.has("periodName") && !classmate.isNull("periodName")) {
  392
+						//get the period name such as 1, 2, 3, or 4, etc.
  393
+						String periodName = classmate.getString("periodName");
  394
+						workgroupIdToPeriodName.put(workgroupId, periodName);
  395
+					}
  396
+					
  397
+					if(classmate.has("studentLogins") && !classmate.isNull("studentLogins")) {
  398
+						/*
  399
+						 * get the student logins, this is a singls string with the logins
  400
+						 * separated by ':'
  401
+						 */
  402
+						String studentLogins = classmate.getString("studentLogins");
  403
+						workgroupIdToStudentLogins.put(workgroupId, studentLogins);
  404
+					}
  405
+					
  406
+					if(classmate.has("periodId") && !classmate.isNull("periodId") &&
  407
+							classmate.has("periodName") && !classmate.isNull("periodName") &&
  408
+							classmate.has("studentLogins") && !classmate.isNull("studentLogins")) {
  409
+
  410
+						//add the workgroup id string to the List of workgroup ids
  411
+						workgroupIds.add(workgroupId + "");
  412
+					}
  413
+				}
  414
+			}
  415
+		} catch (JSONException e) {
  416
+			e.printStackTrace();
  417
+		}
  418
+	    
  419
+	    if(exportType.equals("specialExport")) {
  420
+
  421
+	    	String nodeTitleWithPosition = nodeIdToNodeTitlesWithPosition.get(nodeId);
  422
+	    	String fileName = projectName + "-" + runId + "-" + nodeTitleWithPosition;
  423
+	    	fileName = fileName.replaceAll(" ", "_");
  424
+	    	
  425
+	    	/*
  426
+	    	 * we will return a zipped folder that contains all the necessary
  427
+	    	 * files to view the student work
  428
+	    	 */
  429
+	    	response.setContentType("application/zip");
  430
+			response.addHeader("Content-Disposition", "attachment;filename=\"" + fileName + ".zip" + "\"");
  431
+			
  432
+			//create a folder that will contain all the files and will then be zipped
  433
+			File zipFolder = new File(fileName);
  434
+			zipFolder.mkdir();
  435
+			
  436
+			//create the file that will contain all the student data in a JSON array
  437
+			File studentDataFile = new File(zipFolder, "studentData.js");
  438
+			JSONArray studentDataArray = new JSONArray();
  439
+			
  440
+			String nodeType = "";
  441
+			
  442
+			//get the step content
  443
+			JSONObject nodeContent = nodeIdToNodeContent.get(nodeId);
  444
+			
  445
+			if(nodeContent != null) {
  446
+				try {
  447
+					//get the node type e.g. SVGDraw or mysystem2
  448
+					nodeType = nodeContent.getString("type");
  449
+					
  450
+					if(nodeType != null) {
  451
+						//make the node type lower case for easy comparison later
  452
+						nodeType = nodeType.toLowerCase();
  453
+					}
  454
+				} catch (JSONException e) {
  455
+					e.printStackTrace();
  456
+				}
  457
+			}
  458
+			
  459
+			if(nodeType == null) {
  460
+				
  461
+			} else if(nodeType.equals("svgdraw")) {
  462
+				//get the lz77.js file from the server
  463
+				File sourcelz77File = new File(vlewrapperBaseDir + "/vle/node/draw/svg-edit/lz77.js");
  464
+				
  465
+				//create a lz77.js file in the folder we are creating
  466
+				File newlz77File = new File(zipFolder, "lz77.js");
  467
+				
  468
+				//copy the contents of the lz77.js file into our new file
  469
+				FileUtils.copyFile(sourcelz77File, newlz77File);
  470
+			} else if(nodeType.equals("mysystem2")) {
  471
+				//get the lz77.js file from the server
  472
+				File sourcelz77File = new File(vlewrapperBaseDir + "/vle/node/mysystem2/authoring/js/libs/lz77.js");
  473
+
  474
+				//create a lz77.js file in the folder we are creating
  475
+				File newlz77File = new File(zipFolder, "lz77.js");
  476
+				
  477
+				//copy the contents of the lz77.js file into our new file
  478
+				FileUtils.copyFile(sourcelz77File, newlz77File);
  479
+			}
  480
+			
  481
+			//loop through the workgroup ids
  482
+			for(int x=0; x<workgroupIds.size(); x++) {
  483
+				//get a workgroup id
  484
+				String userId = workgroupIds.get(x);
  485
+				Long userIdLong = Long.parseLong(userId);
  486
+				
  487
+				//get the UserInfo object for the workgroup id
  488
+				UserInfo userInfo = UserInfo.getByWorkgroupId(userIdLong);
  489
+
  490
+				//get all the work for a workgroup id
  491
+				List<StepWork> stepWorksForWorkgroupId = StepWork.getByUserInfo(userInfo);
  492
+				
  493
+				//get all the step works for this node id
  494
+				List<StepWork> stepWorksForNodeId = getStepWorksForNodeId(stepWorksForWorkgroupId, nodeId);
  495
+				
  496
+				//get the latest step work that contains a response
  497
+				StepWork latestStepWorkWithResponse = getLatestStepWorkWithResponse(stepWorksForNodeId);
  498
+				
  499
+				Long stepWorkId = null;
  500
+				String studentData = "";
  501
+				
  502
+				if(latestStepWorkWithResponse != null) {
  503
+					//get the step work id
  504
+					stepWorkId = latestStepWorkWithResponse.getId();
  505
+					
  506
+					//get the student data
  507
+					studentData = getStudentData(latestStepWorkWithResponse);
  508
+				}
  509
+				
  510
+				JSONObject studentObject = new JSONObject();
  511
+				try {
  512
+					//put the workgroup id into the JSON object
  513
+					studentObject.put("workgroupId", userIdLong);
  514
+					
  515
+					//put the student data into the JSON object
  516
+					studentObject.put("data", studentData);
  517
+					
  518
+					//put the step work id into the JSON Object
  519
+					if(stepWorkId == null) {
  520
+						studentObject.put("stepWorkId", "");
  521
+					} else {
  522
+						studentObject.put("stepWorkId", stepWorkId);						
  523
+					}
  524
+				} catch (JSONException e) {
  525
+					e.printStackTrace();
  526
+				}
  527
+				
  528
+				//add the student object into the array
  529
+				studentDataArray.put(studentObject);
  530
+			}
  531
+			
  532
+			/*
  533
+			 * turn the student data array into a javascript declaration
  534
+			 * e.g.
  535
+			 * from
  536
+			 * []
  537
+			 * to
  538
+			 * var studentData = [];
  539
+			 */
  540
+			String javascriptArrayString = getJavascriptArrayText("studentData", studentDataArray);
  541
+			
  542
+			//write the student data to the studentData.js file
  543
+			FileUtils.writeStringToFile(studentDataFile, javascriptArrayString);
  544
+			
  545
+			//create an html file that users will use to view the student data
  546
+			File htmlFile = new File(zipFolder, "viewStudentWork.html");
  547
+			
  548
+			//write the html to the display.html file
  549
+			FileUtils.writeStringToFile(htmlFile, getHtmlDisplayFileText(nodeType));
  550
+			
  551
+			//get the response output stream
  552
+			ServletOutputStream outputStream = response.getOutputStream();
  553
+			
  554
+			//create ZipOutputStream object
  555
+			ZipOutputStream out = new ZipOutputStream(
  556
+					new BufferedOutputStream(outputStream));
  557
+			
  558
+			//get path prefix so that the zip file does not contain the whole path
  559
+			// eg. if folder to be zipped is /home/lalit/test
  560
+			// the zip file when opened will have test folder and not home/lalit/test folder
  561
+			int len = zipFolder.getAbsolutePath().lastIndexOf(File.separator);
  562
+			String baseName = zipFolder.getAbsolutePath().substring(0,len+1);
  563
+			
  564
+			//add the folder to the zip file
  565
+			addFolderToZip(zipFolder, out, baseName);
  566
+			
  567
+			//close the zip output stream
  568
+			out.close();
  569
+			
  570
+			//delete the folder that has been created on the server
  571
+			FileUtils.deleteDirectory(zipFolder);
  572
+	    }
  573
+		
  574
+	    //perform cleanup
  575
+		clearVariables();
  576
+	}
  577
+	
  578
+	/**
  579
+	 * Get the html that we will put into the display.html file
  580
+	 * @param nodeType the step type (make sure it is all lowercase) e.g. svgdraw or mysystem2
  581
+	 * @return the html text that we will put into the display.html file
  582
+	 */
  583
+	private String getHtmlDisplayFileText(String nodeType) {
  584
+		//used to accumulate the html text
  585
+		StringBuffer html = new StringBuffer();
  586
+		
  587
+		html.append("<html>\n");
  588
+		
  589
+		html.append("<head>\n");
  590
+		
  591
+		//add the import of studentData.js
  592
+		html.append("<script type='text/javascript' src='studentData.js'></script>\n");
  593
+		
  594
+		//import any other necessary .js files
  595
+		if(nodeType == null) {
  596
+			
  597
+		} else if(nodeType.equals("svgdraw")) {
  598
+			html.append("<script type='text/javascript' src='lz77.js'></script>\n");
  599
+		} else if(nodeType.equals("mysystem2")) {
  600
+			html.append("<script type='text/javascript' src='lz77.js'></script>\n");
  601
+		}
  602
+		
  603
+		html.append("<script>\n");
  604
+		
  605
+		//create any necessary global variables
  606
+		if(nodeType == null) {
  607
+			
  608
+		} else if(nodeType.equals("svgdraw")) {
  609
+			html.append("var lz77 = new LZ77();\n\n");
  610
+		} else if(nodeType.equals("mysystem2")) {
  611
+			html.append("var lz77 = new LZ77();\n\n");
  612
+		}
  613
+		
  614
+		//create the function that will load the student data
  615
+		html.append("function loadStudentData() {\n");
  616
+		html.append("	for(var x=0; x<studentData.length; x++) {\n");
  617
+		html.append("		var tempStudentData = studentData[x];\n");
  618
+		
  619
+		if(nodeType == null) {
  620
+			
  621
+		} else if(nodeType.equals("svgdraw")) {
  622
+			/*
  623
+			 * perform the necessary processing to retrieve the SVG string.
  624
+			 * the student data from svgdraw steps must be lz77 decompressed.
  625
+			 */
  626
+			html.append("		document.getElementById('studentDataDiv').innerHTML += 'Workgroup Id:' + tempStudentData.workgroupId + '<br>';\n");
  627
+			html.append("		document.getElementById('studentDataDiv').innerHTML += 'Step Work Id:' + tempStudentData.stepWorkId + '<br>';\n");
  628
+			html.append("\n");
  629
+			html.append("		var data = tempStudentData.data;\n");
  630
+			html.append("		var svgString = '';\n\n");
  631
+			html.append("		if(data != null && data != '') {\n");
  632
+			html.append("			data = data.replace(/^--lz77--/,'');\n");
  633
+			html.append("			data = JSON.parse(lz77.decompress(data));\n");
  634
+			html.append("			svgString = data.svgString;\n");
  635
+			html.append("		}\n\n");
  636
+			html.append("		document.getElementById('studentDataDiv').innerHTML += 'Student Data:<br>' + svgString + '<br>';\n");
  637
+		} else if(nodeType.equals("mysystem2")) {
  638
+			/*
  639
+			 * perform the necessary processing to retrieve the SVG string.
  640
+			 * the student data from mysystem2 steps must be unescaped and
  641
+			 * then lz77 decompressed.
  642
+			 */
  643
+			html.append("		document.getElementById('studentDataDiv').innerHTML += 'Workgroup Id:' + tempStudentData.workgroupId + '<br>';\n");
  644
+			html.append("		document.getElementById('studentDataDiv').innerHTML += 'Step Work Id:' + tempStudentData.stepWorkId + '<br>';\n");
  645
+			html.append("\n");
  646
+			html.append("		var data = tempStudentData.data;\n");
  647
+			html.append("		var svgString = '';\n\n");
  648
+			html.append("		if(data != null && data != '') {\n");
  649
+			html.append("			data = unescape(data);\n");
  650
+			html.append("			svgString = lz77.decompress(data);\n");
  651
+			html.append("		}\n\n");
  652
+			html.append("		document.getElementById('studentDataDiv').innerHTML += 'Student Data:<br>' + svgString + '<br>';\n");
  653
+		}
  654
+		
  655
+		html.append("		document.getElementById('studentDataDiv').innerHTML += '<br>';\n");
  656
+		html.append("	}\n");
  657
+		html.append("}\n");
  658
+		
  659
+		html.append("</script>\n");
  660
+		html.append("</head>\n");
  661
+		
  662
+		//set the body onload to load the student data
  663
+		html.append("<body onload='loadStudentData()'>\n");
  664
+		
  665
+		//the div that will display all the student data
  666
+		html.append("<div id='studentDataDiv'></div>\n");
  667
+		
  668
+		html.append("</body>\n");
  669
+		
  670
+		html.append("</html>\n");
  671
+		
  672
+		return html.toString();
  673
+	}
  674
+	
  675
+	/**
  676
+	 * Turn the JSONArray into a javascript array declaration in
  677
+	 * the form of a string.
  678
+	 * e.g.
  679
+	 * before
  680
+	 * []
  681
+	 * after
  682
+	 * var studentData = [];
  683
+	 * @param jsVariableName the javascript variable name that we will use
  684
+	 * @param jsonArray a JSONArray
  685
+	 * @return a string containing the javascript array declaration
  686
+	 */
  687
+	private String getJavascriptArrayText(String jsVariableName, JSONArray jsonArray) {
  688
+		StringBuffer result = new StringBuffer();
  689
+		
  690
+		if(jsonArray != null) {
  691
+			String jsonArrayString = "";
  692
+			try {
  693
+				/*
  694
+				 * get the string representation of the JSONArray with
  695
+				 * proper indentation (using 3 spaces per tab) so it 
  696
+				 * will be easy for a human to read 
  697
+				 */
  698
+				jsonArrayString = jsonArray.toString(3);
  699
+			} catch (JSONException e) {
  700
+				e.printStackTrace();
  701
+			}
  702
+			
  703
+			if(jsonArrayString != null && !jsonArrayString.equals("")) {
  704
+				//make the array declaration string
  705
+				result.append("var " + jsVariableName + " = ");
  706
+				result.append(jsonArrayString);
  707
+				result.append(";");			
  708
+			}
  709
+		}
  710
+
  711
+		return result.toString();
  712
+	}
  713
+	
  714
+	/**
  715
+	 * Get the student data from the step work
  716
+	 * @param stepWork the step work object
  717
+	 * @return a string containing the student data. the
  718
+	 * contents of the string depend on the step type
  719
+	 * that the work was for.
  720
+	 */
  721
+	private String getStudentData(StepWork stepWork) {
  722
+		String studentData = "";
  723
+		
  724
+		String nodeType = "";
  725
+		Node node = null;
  726
+
  727
+		//get the node type
  728
+		if(stepWork != null) {
  729
+			node = stepWork.getNode();
  730
+			
  731
+			if(node != null && node.getNodeType() != null) {
  732
+				//get the node type from the node object e.g. "OpenResponseNode"
  733
+				nodeType = node.getNodeType();
  734
+			}
  735
+		}
  736
+		
  737
+		if(stepWork != null) {
  738
+			//get the step work data
  739
+			String stepWorkData = stepWork.getData();
  740
+			
  741
+			if(stepWorkData != null) {
  742
+				try {
  743
+					//create a JSONObject from the data
  744
+					JSONObject dataJSONObject = new JSONObject(stepWorkData);
  745
+					
  746
+					if(dataJSONObject.has("nodeStates")) {
  747
+						//get the node states
  748
+						JSONArray nodeStates = dataJSONObject.getJSONArray("nodeStates");
  749
+						
  750
+						if(nodeStates != null) {
  751
+							if(nodeStates.length() > 0) {
  752
+								//get the latest node state
  753
+								JSONObject latestNodeState = nodeStates.getJSONObject(nodeStates.length() - 1);
  754
+								
  755
+								if(latestNodeState != null) {
  756
+									if(nodeType == null || nodeType.equals("")) {
  757
+										
  758
+									} else if(nodeType.equals("SVGDrawNode")) {
  759
+										
  760
+										if(latestNodeState.has("data")) {
  761
+											//get the data
  762
+											studentData = latestNodeState.getString("data");											
  763
+										}
  764
+									} else if(nodeType.equals("Mysystem2Node")) {
  765
+										//get the svg string that is embedded inside the response
  766
+										
  767
+										if(latestNodeState.has("response")) {
  768
+											//get the response
  769
+											String response = latestNodeState.getString("response");
  770
+											
  771
+											if(response != null && !response.equals("")) {
  772
+												//get the response as a JSONObject
  773
+												JSONObject responseJSONObject = new JSONObject(response);
  774
+												
  775
+												if(responseJSONObject != null && responseJSONObject.has("MySystem.GraphicPreview")) {
  776
+													//get the MySystem.GraphicPreview JSONObject
  777
+													JSONObject graphicPreview = responseJSONObject.getJSONObject("MySystem.GraphicPreview");
  778
+													
  779
+													if(graphicPreview != null && graphicPreview.has("LAST_GRAPHIC_PREVIEW")) {
  780
+														//get the LAST_GRAPHIC_PREVIEW JSONObject
  781
+														JSONObject lastGraphicPreview = graphicPreview.getJSONObject("LAST_GRAPHIC_PREVIEW");
  782
+														
  783
+														if(lastGraphicPreview != null && lastGraphicPreview.has("svg")) {
  784
+															//get the svg string
  785
+															String svg = lastGraphicPreview.getString("svg");
  786
+															studentData = svg;
  787
+														}
  788
+													}
  789
+												}
  790
+											}
  791
+										}
  792
+									}
  793
+								}								
  794
+							}
  795
+						}
  796
+					}
  797
+				} catch (JSONException e) {
  798
+					e.printStackTrace();
  799
+				}
  800
+			}
  801
+		}
  802
+		
  803
+		return studentData;
  804
+	}
  805
+	
  806
+	/**
  807
+	 * Get the student work from the step work
  808
+	 * @param stepWork the step work object
  809
+	 * @return the student work as a string
  810
+	 */
  811
+	private String getStepWorkResponse(StepWork stepWork) {
  812
+		String stepWorkResponse = "";
  813
+		
  814
+		//get the student work
  815
+		stepWorkResponse = getStudentData(stepWork);
  816
+		
  817
+		return stepWorkResponse;
  818
+	}
  819
+	
  820
+	/**
  821
+	 * Get the latest StepWork that has a non-empty response
  822
+	 * @param stepWorks a list of StepWork objects
  823
+	 * @return a String containing the latest response
  824
+	 */
  825
+	private StepWork getLatestStepWorkWithResponse(List<StepWork> stepWorks) {
  826
+		String stepWorkResponse = "";
  827
+		StepWork stepWork = null;
  828
+		
  829
+		/*
  830
+		 * loop through all the stepworks for the node id and find
  831
+		 * the latest work
  832
+		 */
  833
+		for(int z=stepWorks.size() - 1; z>=0; z--) {
  834
+			//get a step work
  835
+			StepWork tempStepWork = stepWorks.get(z);
  836
+			
  837
+			//retrieve the student work from the step work, if any
  838
+			stepWorkResponse = getStepWorkResponse(tempStepWork);
  839
+			
  840
+			/*
  841
+			 * if the step work is not empty, we are done looking
  842
+			 * for the latest work
  843
+			 */
  844
+			if(!stepWorkResponse.equals("")) {
  845
+				stepWork = tempStepWork;
  846
+				break;
  847
+			}
  848
+		}
  849
+		
  850
+		return stepWork;
  851
+	}
  852
+	/**
  853
+	 * Get the step works only for a specific node id
  854
+	 * @param stepWorks a list of StepWork objects
  855
+	 * @param nodeId the node id we want student work for
  856
+	 * @return a list of StepWork objects that are filtered
  857
+	 * for a node id
  858
+	 */
  859
+	private List<StepWork> getStepWorksForNodeId(List<StepWork> stepWorks, String nodeId) {
  860
+		//the list of StepWorks that will contain the StepWorks we want
  861
+		List<StepWork> filteredStepWorks = new Vector<StepWork>();
  862
+		
  863
+		//iterator for the list of StepWorks we will filter
  864
+		Iterator<StepWork> stepWorksIterator = stepWorks.iterator();
  865
+		
  866
+		//loop through all the StepWorks
  867
+		while(stepWorksIterator.hasNext()) {
  868
+			//get a StepWork
  869
+			StepWork stepWork = stepWorksIterator.next();
  870
+			
  871
+			//get the node id for the StepWork
  872
+			String stepWorkNodeId = stepWork.getNode().getNodeId();
  873
+			
  874
+			//see if the node id matches the node id we are looking for
  875
+			if(stepWorkNodeId != null && stepWorkNodeId.equals(nodeId)) {
  876
+				/*
  877
+				 * add the StepWork to our list of StepWorks that have the
  878
+				 * node id we want
  879
+				 */
  880
+				filteredStepWorks.add(stepWork);
  881
+			}
  882
+		}
  883
+		
  884
+		//return the list of StepWorks that are for the node id we want
  885
+		return filteredStepWorks;
  886
+	}
  887
+	
  888
+	private static void addFolderToZip(File folder, ZipOutputStream zip, String baseName) throws IOException {
  889
+		File[] files = folder.listFiles();
  890
+		for (File file : files) {
  891
+			if (file.isDirectory()) {
  892
+				// add folder to zip
  893
+				String name = file.getAbsolutePath().substring(baseName.length());
  894
+				ZipEntry zipEntry = new ZipEntry(name+"/");
  895
+				zip.putNextEntry(zipEntry);
  896
+				zip.closeEntry();
  897
+				addFolderToZip(file, zip, baseName);
  898
+			} else {
  899
+				// it's a file.				
  900
+				String name = file.getAbsolutePath().substring(baseName.length());
  901
+				ZipEntry zipEntry = new ZipEntry(name);
  902
+				zip.putNextEntry(zipEntry);
  903
+				IOUtils.copy(new FileInputStream(file), zip);
  904
+				zip.closeEntry();
  905
+			}
  906
+		}
  907
+	}
  908
+	
  909
+	/**
  910
+	 * Get the timestamp as a string
  911
+	 * @param timestamp the timestamp object
  912
+	 * @return the timstamp as a string
  913
+	 * e.g.
  914
+	 * Mar 9, 2011 8:50:47 PM
  915
+	 */
  916
+	private String timestampToFormattedString(Timestamp timestamp) {
  917
+		String timestampString = "";
  918
+		
  919
+		if(timestamp != null) {
  920
+			//get the object to format timestamps
  921
+			DateFormat dateTimeInstance = DateFormat.getDateTimeInstance();
  922
+			
  923
+			//get the timestamp for the annotation
  924
+			long time = timestamp.getTime();
  925
+			Date timestampDate = new Date(time);
  926
+			timestampString = dateTimeInstance.format(timestampDate);			
  927
+		}
  928
+		
  929
+		return timestampString;
  930
+	}
  931
+	
  932
+	/**
  933
+	 * Parse the student attendance data and put it into the workgroupIdToStudentAttendance HashMap
  934
+	 * @param studentAttendanceArray a JSONArray containing all the student attendance rows
  935
+	 */
  936
+	private void parseStudentAttendance(JSONArray studentAttendanceArray) {
  937
+		//loop through all the stuent attendance rows
  938
+		for(int x=0; x<studentAttendanceArray.length(); x++) {
  939
+			try {
  940
+				//get a student attendence row
  941
+				JSONObject studentAttendanceEntry = studentAttendanceArray.getJSONObject(x);
  942
+				
  943
+				//get the workgroup id
  944
+				long workgroupId = studentAttendanceEntry.getLong("workgroupId");
  945
+				
  946
+				//get the JSONArray that holds all the student attendence entries for this workgroup id
  947
+				JSONArray workgroupIdStudentAttendance = workgroupIdToStudentAttendance.get(workgroupId);
  948
+				
  949
+				if(workgroupIdStudentAttendance == null) {
  950
+					//the JSONArray does not exist so we will create it
  951
+					workgroupIdStudentAttendance = new JSONArray();
  952
+					workgroupIdToStudentAttendance.put(workgroupId, workgroupIdStudentAttendance);
  953
+				}
  954
+				
  955
+				//add the student attendence entry to the JSONArray
  956
+				workgroupIdStudentAttendance.put(studentAttendanceEntry);
  957
+			} catch (JSONException e) {
  958
+				e.printStackTrace();
  959
+			}
  960
+		}
  961
+	}
  962
+	
  963
+	/**
  964
+	 * Create a map of node id to node titles by looping through the array
  965
+	 * of nodes in the project file and creating an entry in the map
  966
+	 * for each node
  967
+	 * @param project the project JSON object
  968
+	 * @return a map of node id to node titles
  969
+	 */
  970
+	private void makeNodeIdToNodeTitleAndNodeMap(JSONObject project) {
  971
+		nodeIdToNodeTitles = new HashMap<String, String>();
  972
+		nodeIdToNode = new HashMap<String, JSONObject>();
  973
+		
  974
+		try {
  975
+			//get the array of nodes defined in the project
  976
+			JSONArray nodesJSONArray = project.getJSONArray("nodes");
  977
+			
  978
+			//loop through all the nodes
  979
+			for(int x=0; x<nodesJSONArray.length(); x++) {
  980
+				//get a node
  981
+				JSONObject node = nodesJSONArray.getJSONObject(x);
  982
+				
  983
+				if(node != null) {
  984
+					//obtain the id and title
  985
+					String nodeId = node.getString("identifier");
  986
+					String title = node.getString("title");
  987
+					
  988
+					if(nodeId != null && title != null) {
  989
+						//put the id and title into the map
  990
+						nodeIdToNodeTitles.put(nodeId, title);
  991
+					}
  992
+					
  993
+					if(nodeId != null) {
  994
+						nodeIdToNode.put(nodeId, node);
  995
+					}
  996
+				}
  997
+			}
  998
+		} catch (JSONException e) {
  999
+			e.printStackTrace();
  1000
+		}
  1001
+	}
  1002
+	
  1003
+	/**
  1004
+	 * Make the list of node ids
  1005
+	 * 
  1006
+	 * Note: makeNodeIdToNodeTitlesMap() must be called before this function
  1007
+	 * 
  1008
+	 * @param project the project JSON object
  1009
+	 */
  1010
+	private void makeNodeIdList(JSONObject project) {
  1011
+		//make a new Vector and set it to the global list object
  1012
+		nodeIdList = new Vector<String>();
  1013
+		
  1014
+		try {
  1015
+			//get the sequences
  1016
+			JSONArray sequences = project.getJSONArray("sequences");
  1017
+			
  1018
+			//get the start point of the project
  1019
+			String startPoint = project.getString("startPoint");
  1020
+			
  1021
+			//pass startsequence to recursive function that traverses activities and steps
  1022
+			traverseNodeIdsToMakeNodeIdList(sequences, startPoint, "", 1, startPoint);
  1023
+		} catch (JSONException e) {
  1024
+			e.printStackTrace();
  1025
+		}
  1026
+	}
  1027
+	
  1028
+	/**
  1029
+	 * Traverses the sequences in the project file to create a list of nodes in 
  1030
+	 * the order that they appear in the project and at the same time determining
  1031
+	 * the position of each node (e.g. 1.1, 1.2, 2.1, 2.2, etc.)
  1032
+	 * 
  1033
+	 * @param sequences the JSONArray of sequences
  1034
+	 * @param identifier the id of the sequence or node we are currently on
  1035
+	 * @param positionSoFar the position we have traversed down to so far
  1036
+	 * e.g. if we are on Activity 2
  1037
+	 * positionSoFar will be "2."
  1038
+	 * @param nodePosition the position within the current sequence
  1039
+	 * e.g. if we are on Activity 2, Step 3
  1040
+	 * @param startPoint the id of the start point sequence of the project
  1041
+	 * nodePosition will be 3
  1042
+	 */
  1043
+	private void traverseNodeIdsToMakeNodeIdList(JSONArray sequences, String identifier, String positionSoFar, int nodePosition, String startPoint) {
  1044
+		try {
  1045
+			//try to get the project sequence with the given identifier
  1046
+			JSONObject projectSequence = getProjectSequence(sequences, identifier);
  1047
+			
  1048
+			if(projectSequence == null) {
  1049
+				//the identifier actually points to a node, this is our base case
  1050
+				
  1051
+				//whether to include the data for this step in the export
  1052
+				boolean exportStep = true;
  1053
+				
  1054
+				if(customSteps.size() != 0) {
  1055
+					//the teacher has provided a list of custom steps
  1056
+					
  1057
+					if(!customSteps.contains(identifier)) {
  1058
+						//the current node id is not listed in the custom steps so we will not export the data for it
  1059
+						exportStep = false;
  1060
+					}
  1061
+				}
  1062
+				
  1063
+				if(exportStep) {
  1064
+					//we will export the data for this step
  1065
+					
  1066
+					//add the identifier to our list of nodes
  1067
+					nodeIdList.add(identifier);
  1068
+					
  1069
+					//obtain the title of the node
  1070
+					String nodeTitle = nodeIdToNodeTitles.get(identifier);
  1071
+					
  1072
+					//add the pre-pend the position to the title
  1073
+					String nodeTitleWithPosition = positionSoFar + nodePosition + " " + nodeTitle;
  1074
+					
  1075
+					//add the title with position to the map
  1076
+					nodeIdToNodeTitlesWithPosition.put(identifier, nodeTitleWithPosition);					
  1077
+				}
  1078
+			} else {
  1079
+				//the identifier points to a sequence so we need to loop through its refs