Location: PHPKode > projects > GPDE > gpde_1.1.4/gpde/combined_search/module-combined_search.php
<?php
/*
	GPDE (Griss Proteomics Database Engine) 
	>> A biological view on PRIDE
    Copyright (C) 2008, 2009  Johannes Griss - hide@address.com

    This file is part of GPDE.
    For license see notice in /LICENSE.txt and <http://www.griss.co.at/?gpde:License>.
*/

require_once("config.php");
require("module.php");
require("backend_visual". DIRECTORY_SEPARATOR ."database_combsea.php");
require("backend_admin" . DIRECTORY_SEPARATOR  . "accession_converter.php");

class Module_combined_search extends Module {
	private $_template;
	private $_db;
	private $converter;
	private $_statusid;
	
	function __construct() {
		$this->_template = "combined_search". DIRECTORY_SEPARATOR ."module-combined_search.template";
	}
	
	function render() {
		session_start();
		
		// template requires a list of all cell types		
		$cells = array(); // initialize the $cells variable with an empty array
		$cellIds = array();
		
		try {
			// make sure the database is online
			if ($this->DbIsOffline())
				throw new Exception("Database is currently offline");
			
			// connect to the database
			$this->connectDb();
			
			// get all cells
			$cells = $this->_db->GetCellTypes();
			
			// get the cell ids
			$cellIds = $this->_db->GetUsedCellIdentifiers();

			// generate an identifier for the status file			
			$statusId = "Status" . rand();
			$_SESSION['status_identifier'] = $statusId; // cannot trust the session variable since several tabs f.e. in Firefox use the same session
						
			require_once($this->_template);
		}
		catch (Exception $e) {
			$this->Send500();
			print $e->getMessage();
		}
	}
	
	/**
	 * Creates the private $_db member and connects to the database. Throws a ProtdbException on error.
	 */
	private function connectDb() {
		$this->_db = new ProtdbCombSea();
		$this->_db->Connect(DB_HOST, DB_USER, DB_PASS, DB_NAME, DB_PORT, DB_SOCKET);
		
		$this->converter = new AccessionConverter();
		if (!$this->converter->Connect(DB_HOST,DB_USER,DB_PASS,DB_NAME,DB_PORT,DB_SOCKET)) {
			throw new ProtdbException("Failed to connect to database");
		}
	}
	
	/**
	 * Starts the comparison of different data sets. If $_POST['a'] == "fileupload" only the file to compare is
	 * being uploaded and no real comparison is being started.
	 */
	function compare() {
		try {
			session_start();
			
			// make sure the database is not offline
			if ($this->DbIsOffline())
				throw new Exception("Database is currently offline");
			
			// make sure the action and statusid parameter is set
			if (!isset($_POST['a']) && isset($_GET['a']))
				$_POST['a'] = $_GET['a'];
			
			if (!isset($_POST['statusid']) && isset($_GET['statusid']))
				$_POST['statusid'] = $_GET['statusid'];
			
			if (!isset($_POST['a']) || !isset($_POST['statusid']))
				throw new Exception("Missing parameter");
				
			// save the status id
			$this->_statusid = $_POST['statusid'];
				
			switch($_POST['a']) {
				case "fileupload":
					$this->saveUploadFile();
					break;
				case "status":
					$this->sendStatus();
					break;
				case "compare":
					$this->compareData();
					break;
				case "chart":
					$this->drawImage();
					break;
				case "export":
					$this->exportResult();
					break;
				case "last_result":
					$this->sendLastResult();
					break;
			}
		}
		catch (Exception $e) {
			$this->Send500();
			print $e->getMessage();
		}
	}
	
	/**
	 * Save the uploaded file.
	 */
	private function saveUploadFile() {
		try {
			// find out which file was sent (userfile_a or userfile_b)
			if (isset($_FILES['userfile_a']))
				$fileId = "userfile_a";
			else if (isset($_FILES['userfile_b']))
				$fileId = "userfile_b";
			else
				$fileId = false;
			
			// check if the file was uploaded
			if (!$fileId)
				throw new Exception("File wasn't sent");
				
			// move the uploaded file in the tmp folder
			$temp_file = tempnam(sys_get_temp_dir(), 'Datafile');
			
			if (!move_uploaded_file($_FILES[$fileId]['tmp_name'], $temp_file))
				throw new Exception("File upload failed.");

			// save the temporary file name
			$_SESSION[$fileId] = $temp_file;
			
			// send the status file name
			print "Complete";
		}
		catch (Exception $e) {
			// just send an error message
			print "Error: " . $e->getMessage();
		}
	}
	
	/**
	 * Write the current status to the status file.
	 * @param $percent - the progress in percent (0 - 100), if "false" the gif is displayed
	 * @param $message - the message to display
	 */
	private function setStatus($percent, $message) {		
		file_put_contents($this->getStatusFile(), 
						  $percent . "\n" . $message);
	}
	
	/**
	 * Send the content of the status file
	 */
	private function sendStatus() {		
		// send the contents of the status file
		if (file_exists($this->getStatusFile()))
			print file_get_contents($this->getStatusFile());
		else
			$this->Send500();
	}
	
	/**
	 * Returns the path of the current status file
	 * @return string
	 */
	private function getStatusFile() {
		// make sure the status identifier exists			
		if (!isset($this->_statusid))
			throw new Exception("Missing status identifier");
		
		$statusFile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $this->_statusid;
		
		return $statusFile;
	}
	
	/**
	 * Unlink the status file.
	 */
	private function unlinkStatusFile() {
		// just try to unlink the status file
		unlink($this->getStatusFile());
	}
	
	/**
	 * Function to handle compare data requests.
	 */
	private function compareData() {
		// close the session since the required status identifier should already be present
		session_write_close();
		
		// make sure all required parameters are set
		if (!isset($_POST['typea']) || !isset($_POST['poola']) || !isset($_POST['poolb']))
			throw new Exception("Missing parameter");
		
		// connect to the database
		$this->connectDb();
			
		// initialize the variables to hold the two accession arrays
		$poola = false;
		$poolb = false;
		
		$infoA = array();
		$infoB = array();
			
		// get the accession list for pool a
		$poola = $this->fetchPool("a", $infoA);
		
		// get the accession list for pool b
		$poolb = $this->fetchPool("b", $infoB);

		// compare the data
		$this->setStatus("false", "Comparing proteins...");
		$comparedAccessions = $this->compareProteinPools($poola, $poolb);
		
		// free some memory
		unset($poola);
		unset($poolb);
		
		// save the additional info
		$comparedAccessions['info']['infoa'] = $infoA;
		$comparedAccessions['info']['infob'] = $infoB;
		
		// get the missing protein names and upate the protein's accessions
		$this->setStatus("false", "Inserting missing protein names");
		$this->fillMissingProteinNames($comparedAccessions);
		$this->removeDuplicateEntries($comparedAccessions);
		
		// add the protein's classifications
		$this->addClassifications($comparedAccessions);
		
		// save the result as file
		$this->saveResultInFile($comparedAccessions);
		
		// send the array as json
		$this->SendResult($comparedAccessions);
		
		// delete the status file
		$this->unlinkStatusFile();
	}
	
	/**
	 * Fetches the submitted accessions for the given pool (a or b). Throws an Expception on error.
	 * @param $id - the pool's id (a or b)
	 * @param $info - reference of a variable to which the extra information will be written
	 * @return array - an array of accessions
	 */
	private function fetchPool($id, &$info) {
		// get the accession list for pool a
		$userfile 	= $_SESSION['userfile_' . $id];
		$pool 		= false;
		
		switch ($_POST['type' . $id]) 
		{
			case "combined_search-content_box-".$id."-upload_tab":
				$this->setStatus("false", "Extracting accessions from file...");
				
				// get the accessions from the uploaded file, then delete it
				$pool = $this->getAccessionsFromFile($userfile, $info);
				unlink($userfile);
				
				// convert the accessions and sort the array
				$pool = $this->convertAccessions($pool);
				ksort($pool); // sort the array
				break;
				
			case "combined_search-content_box-".$id."-enter_tab":
				$this->setStatus("false", "Extracting accessions from input...");
				
				$pool = $this->parseUserInput($_POST['pool' . $id]);
				$pool = $this->convertAccessions($pool);
				ksort($pool); // sort the array
				
				$info['type'] = "User input";
				
				break;
				
			case "combined_search-content_box-".$id."-select_tab":
				$this->setStatus("false", "Fetching accession list for pool ".$id."...");
				
				// database queries don't need to be converted or sorted
				$json = str_replace("\\\"", "\"", $_POST['pool' . $id]);
				$cells = json_decode($json, true);
				$pool = $this->_db->GetCellAccessions($cells);
				
				// save the info about the user input
				$info['type'] 	= "Cells selected";
				$info['count'] = count($cells);
				$info['cells'] = $this->_db->GetCellNames($cells);
				break;
		}
		
		return $pool;
	}
	
	/**
	 * Extract the accession list from a file. Throws an exception on error.
	 * @param $filename - path to the file
	 * @param $additional - (optional) reference to a variable to hold additional infos about the file (associative array)
	 * @return array - ['accession' => 'name']
	 */
	private function getAccessionsFromFile($filename, &$additional) {
		// include the required php file
		require_once("backend_visual" . DIRECTORY_SEPARATOR . "pride_accessions_parser.php");
		require_once("backend_visual" . DIRECTORY_SEPARATOR . "tsv_file.php");
		
		$accessions = false;
		$additional = array();
		
		// try to guess the file type (either it's PRIDE or a text file
		$buf = file_get_contents($filename, 0, NULL, 0, 100); // get the first 100 bytes
		
		if (strpos($buf, "<ExperimentCollection version=\"2.1\">") !== false) {
			$parser = new PrideAccessionsParser();
			$accessions = $parser->getAccessions($filename);
			
			$additional['type'] 		= "PRIDE file";
			$additional['accession'] 	= $parser->getExperimentAccession();
			$additional['sample_name']	= $parser->getExperimentSampleName();
			$additional['title']		= $parser->getExperimentTitle();
		}
		else {			
			// try to open the file
			$h = @fopen($filename, "r");
			
			if (!$h)	throw new Exception("Could not open text file.");
			
			// initialize the $accessions variable
			$accessions = array();
			$buf = "";
			$firstLine = true; // indicates whether this is the first line to be read in
			$hasHeader = false;
			$peptideCountIndex = false; $accessionIndex = false; $nameIndex = false;
			
			// read the file into the buffer
			while (!feof($h)) {
				$line = fgets($h, 4096);
				
				// if it's the first line try to guess if the file uses a header
				if ($firstLine) {
					$tmp = strtolower($line);
					
					if (strpos($tmp, "accession") !== false || strpos($tmp, "accnr") !== false 
						|| strpos($tmp, "identifier") !== false)
						$hasHeader = true;
					
					$firstLine = false;
				}
				
				// save the line in the buffer
				$buf .= $line;
			}
			
			// transform windows file
			$buf = str_replace("\r\n", "\n", $buf);
			
			// parse the file line by line
			$line = strtok($buf, "\n");
			$firstLine = true;
			
			while ($line !== false) {
				// ignore any empty line
				if ($line == "")	continue;
				
				// get the fields
				$fields = explode("\t", $line);
				
				// if it's the first line and the file has a header, read it in
				if ($firstLine && $hasHeader) {
					foreach ($fields as $index => $field) {
						// make all field name's lower caps
						$field = strtolower($field);
						
						if ($accessionIndex === false && ($field == 'accession' || $field == 'identifier' || $field == 'accnr') )
							$accessionIndex = $index;
							
						if ($nameIndex === false && ($field == 'name' || $field == 'protein description line') )
							$nameIndex = $index;
						
						if ($peptideCountIndex === false && ($field == 'number of peptides' || $field == 'peptide_count' || $field == 'peptide count') )
							$peptideCountIndex = $index;
					}
					
					// make sure the required fields were found
					if ($accessionIndex === false)
						throw new Exception("File does not contain a column 'accession'");
						
					$firstLine = false;
						
					// go to the next line
					$line = strtok("\n");
					
					continue;
				}
				
				// save the object
				if ($hasHeader) {
					$acc = trim( $fields[$accessionIndex] );
					
					$accessions[$acc] = Array();
					
					if ($nameIndex) 		$accessions[$acc]['name'] 			= trim( $fields[$nameIndex] );
					if ($peptideCountIndex) $accessions[$acc]['peptide_count'] 	= trim( $fields[$peptideCountIndex] );
				}	
				else {
					// only save the accession
					$accessions[trim($line)] = Array('name' => false);
				}			
				
				// get the next line
				$line = strtok("\n");
			}
			
			$additional['type'] = "Text file";
		}
		
		return $accessions;
	}
	
	/**
	 * Converts an array of accessions. Throws an Exception on error.
	 * @param $accessions - [0-n]['accessions' => 'name']
	 * @return array - ['accessions' => ['name' ... ] ]
	 */
	private function convertAccessions($accessions) {
		// variables for the status information
		$totalAccessions = count($accessions);
		$convertedAccessions = 0;
		
		// initialize the return value
		$convAccs = array();
		
		foreach ($accessions as $acc => $object) {
			// update the status
			$this->setStatus( ($convertedAccessions*100) / $totalAccessions, 
				"Converting accessions (" . $convertedAccessions . "/" . $totalAccessions . ")..." );
			
			// convert the accession
			try {
				$newAcc = $this->converter->convertAccession($acc, true);
				
				// save the object under its new accession
				$convAccs[$newAcc] = $object;
			}
			catch (AccessionConverterException $e) {
				// just use the old accession
				$convAccs[$acc] = $object;
			}
			
			// tell the user that the accession was converted
			$convertedAccessions++;
		}
		
		return $convAccs;
	}
	
	/**
	 * Parses the user entered accessions
	 * @param $input
	 * @return array - ['accession' => ['name'] ]
	 */
	private function parseUserInput($input) {		
		$lines 		= explode("\n", $input);
		$accessions = array();
		$prot = "";
		
		foreach($lines as $l) {
			$prot = trim($l);
			if ($prot)
				$accessions[$prot] = array('name' => false);
		}
		
		return $accessions;
	}
	
	/**
	 * Compares the two pools of proteins and returns the result as a combined array. Additionally
	 * the array contains an "info" element with "onlya", "onlyb", "both" as integers.
	 * @param $poola - array containing the proteins of pool a
	 * @param $poolb - array containing the proteins of pool b
	 * @return array - [0-n] ['accession', 'name', 'exists' = "a/b/x"] ]
	 */
	private function compareProteinPools($poola, $poolb) {
		$comparedPool = array(); // initialize return variable
		$index = 0;
		$pA = ""; // accession of the current protein in pool a
		$pB = ""; // accession of the current protein in pool b
		
		$onlyA = 0;
		$onlyB = 0;
		$both = 0;
		
		while($pA = key($poola)) {
			$pB = key($poolb); // get the current key from pool a

			// add every protein from b that is lexically smaller than the current protein in a
			while ($pB && strcmp($pA, $pB) > 0) {
				$comparedPool['proteins'][$index]['accession'] = $pB;
				$comparedPool['proteins'][$index]['name'] = $poolb[$pB]['name'];
				$comparedPool['proteins'][$index]['exists'] = "b"; // only exists in pool b
				$comparedPool['proteins'][$index]['peptide_count_b'] = $poolb[$pB]['peptide_count'];
				$comparedPool['proteins'][$index]['gpde'] = $poolb[$pB]['gpde'];
				$index++;
				
				$onlyB++;
				
				// move to the next protein
				next($poolb);
				$pB = key($poolb);
			}
			
			// check whether the current protein of a exists in b
			if ($pB && strcmp($pA, $pB) == 0) {
				$comparedPool['proteins'][$index]['exists'] = "x"; // exists in both pools
				$comparedPool['proteins'][$index]['accession'] = $pB;
				$comparedPool['proteins'][$index]['gpde'] = $poolb[$pB]['gpde'];
				
				// try to save some name
				$comparedPool['proteins'][$index]['name'] = ($poolb[$bB]['name']) ? $poolb[$pB]['name'] : $poola[$pA]['name'];
				
				// save the different peptide counts
				$comparedPool['proteins'][$index]['peptide_count_b'] = $poolb[$pB]['peptide_count'];
				$comparedPool['proteins'][$index]['peptide_count_a'] = $poola[$pA]['peptide_count'];
				
				$index++;
				
				$both++;
				// since this item of b was used move to the next one
				next($poolb);
			}
			else { 
				// the protein only exists in a
				$comparedPool['proteins'][$index]['name'] = $poola[$pA]['name'];
				$comparedPool['proteins'][$index]['accession'] = $pA;
				$comparedPool['proteins'][$index]['exists'] = "a";
				$comparedPool['proteins'][$index]['peptide_count_a'] = $poola[$pA]['peptide_count'];
				$comparedPool['proteins'][$index]['gpde'] = $poola[$pA]['gpde'];
				$index++;

				$onlyA++;
			}
			
			// move to the next protein in a
			next($poola);
		}
		
		// if the current protein of b is already in the array move to the next
		if ( key_exists(key($poolb), $comparedPool) )
			next($poolb);
			
		// add all remaining proteins of b (which are surely not in a)
		while ($pB = key($poolb)) {
			$comparedPool['proteins'][$index]['name'] = $poolb[$pB]['name'];
			$comparedPool['proteins'][$index]['accession'] = $pB;
			$comparedPool['proteins'][$index]['exists'] = "b";
			$comparedPool['proteins'][$index]['peptide_count_b'] = $poolb[$pB]['peptide_count'];
			$comparedPool['proteins'][$index]['gpde'] = $poolb[$pB]['gpde'];
			$index++;
			
			$onlyB++;
			
			next($poolb);
		}
		
		// save the info element
		$comparedPool['info']['poola'] = count($poola);
		$comparedPool['info']['poolb'] = count($poolb);
		
		$comparedPool['info']['onlya'] 	= $onlyA;
		$comparedPool['info']['onlyb'] 	= $onlyB;
		$comparedPool['info']['both'] 	= $both;
		
		return $comparedPool;
	}
	
	/**
	 * Gets the missing protein names. First the database is searched. If the protein doen't exist there
	 * the name is loaded from uniprot. Additionally the protein's accession is updated as well if necessary. 
	 * Throws an exception on error.
	 * @param $proteins
	 */
	private function fillMissingProteinNames(&$proteins) {		
		// load the UniprotRequest class
		require("backend_admin" . DIRECTORY_SEPARATOR . "uniprot.php");
		
		// connect to the db
		$this->connectDb();
		
		// save the path to the name cache
		$cacheDir 	= PROT_SEQUENCE_PATH . "Names" . DIRECTORY_SEPARATOR;
		$cache		= "";
		
		if (!is_dir($cacheDir)) {
			if (!mkdir($cacheDir))
				throw new Exception("Could not create the directory to cache the protein names.");	
		}
		
		// get the name for every protein where name == false
		foreach($proteins['proteins'] as $i => $p) 
		{
			if (!$p['name']) {
				// reset the used variables
				$newAccession 	= false;
				$newName 		= false;
				
				// try to get the name from the db
				$newName = $this->_db->GetProteinName($p['accession']);
				
				// if the name is in the database, the protein is as well
				if ($newName)
					$proteins['proteins'][$i]['gpde'] = 1;
				
				// if this didn't work, check if the name is cached
				if (!$newName && file_exists($cacheDir . $p['accession'])) {
					$cache = file_get_contents($cacheDir . $p['accession']);
					
					$newAccession 	= substr($cache, 0, strpos($cache, "\n"));
					$newName 		= substr($cache, strpos($cache, "\n") + 1);
				}
				
				// if it didn't work download it from ipi
				if (!$newName) {
					try {
						$uniprot = new UniprotEntry($p['accession']);
						$uniprot->parseXml();
						
						$newName 		= $uniprot->getName();
						$newAccession 	= $uniprot->getAccession(); // remove the version information
						
						// save the new name in the cache
						file_put_contents($cacheDir . $p['accession'], $newAccession . "\n" . $newName);
					}
					catch (UniprotEntryException $e) {
						$newName = "UniProt Entry could not be fetched.";
					}
				}
				
				// change the accession if necessary
				if ($p['accession'] != $newAccession && $newAccession) {
					$proteins['proteins'][$i]['accession'] = $newAccession;
				}
				
				$proteins['proteins'][$i]['name'] = $newName;
			}
			else {
				// as the protein's name is there, only check if the protein is in the database
				if (!$p['gpde'])
					$proteins['proteins'][$i]['gpde'] = $this->_db->isAccessionInDb($p['accession']);
			}
			
			// show the user how many names were already processed
			$this->setStatus( ($i*100) / count($proteins['proteins']), 
				"Inserting missing protein names (" . $i . "/" . count($proteins['proteins']) . ")");
		}
	}
	
	/**
	 * Remove duplicate entries from the result list
	 * @param $proteins
	 */
	private function removeDuplicateEntries(&$proteins) {		
		$dupA = 0; // number of duplicates in pool a
		$dupB = 0; // number of duplicates in pool b
		$dupOnlyA = 0;
		$dupOnlyB = 0;
		$dupBoth = 0;
		
		$accessions = array(); // array holding all accessions that already were found
		
		// get the total protein count
		$proteinCount = count($proteins['proteins']);
		
		$this->setStatus("false", "Removing duplicate entries...");
		
		// loop through the array starting from the end (to be able to directly delete entries without altering the original order)
		for ($i = $proteinCount - 1; $i >= 0; $i--) {
			// check if this protein's accession is in the array
			if (in_array($proteins['proteins'][$i]['accession'], $accessions)) {
				// "count" the protein
				switch ($proteins['proteins'][$i]['exists']) {
					case "a":
						$dupA++;
						$dupOnlyA++;
						break;
					case "b":
						$dupB++;
						$dupOnlyB++;
						break;
					case "x":
						$dupA++;
						$dupB++;
						$dupBoth++;
						break;
				}
				
				// remove the protein from the proteins array
				array_splice($proteins['proteins'], $i, 1);
			}
			else {
				// add the accession to the accessions array
				array_push($accessions, $proteins['proteins'][$i]['accession']);
			}
			
			// update the status
			$this->setStatus(round(($proteinCount - $i) / $proteinCount * 100), "Removing duplicate entries...");
		}
		
		// correct the extra info
		$proteins['info']['poola']	-= $dupA;
		$proteins['info']['poolb']	-= $dupB;
		$proteins['info']['onlya']	-= $dupOnlyA;
		$proteins['info']['onlyb']	-= $dupOnlyB;
		$proteins['info']['both'] 	-= $dupBoth;
		
		// add the info about duplicate entries to the info
		$proteins['info']['dupa'] = $dupA;
		$proteins['info']['dupb'] = $dupB;
	}
	
	/**
	 * Adds the classification to every protein that may be in the database. Adds an array of all classifications present in the sample
	 * [0-n]['id', 'description', 'color'].
	 * Throws an exception on error.
	 * @param $proteins
	 */
	private function addClassifications(&$proteins) {
		$classifications = array(); // array holding all classifications present in the sample
		$counts = array(); // array holding the protein counts for every classification
		$pClass = false; // variable holding the current protein' classification
		$index = false; // index of the looked for classifications
		
		// set the status
		$this->setStatus("false", "Inserting protein classifications...");
		
		// loop through the proteins
		foreach($proteins['proteins'] as $i => $p) {
			// if the protein's accession is 6 it may be an UniProt accession
			if (strlen($p['accession']) == 6) {
				try {
					// get the protein's classifications
					$pClass = $this->_db->GetProteinClassificationByAccession($p['accession'], true);
					
					if (!$pClass) $pClass = Array();
					
					$proteins['proteins'][$i]['classifications'] = $pClass;
					
					// if it's a new classification add it to the list of classifications present in the sample
					foreach ($pClass as $c) {						
						// if the classification doesn't yet exist in the classification array, add it
						if ( !isset($classifications[$c])) {
							// save the id
							$classifications[$c]['id'] = $c;
							
							// reset all protein counts
							$classifications[$c]['protein_count_a'] = 0;
							$classifications[$c]['peptide_count_a'] = 0;
							$classifications[$c]['peptide_count_b'] = 0;
						}
						
						// set the protein and peptide count
						if ($p['exists'] == "a" || $p['exists'] == "x") {
							$classifications[$c]['protein_count_a']++;
							$classifications[$c]['peptide_count_a'] += ($p['peptide_count_a']) ? $p['peptide_count_a'] : 0;
						}
						if ($p['exists'] == "b" || $p['exists'] == "x") {
							$classifications[$c]['protein_count_b']++;
							$classifications[$c]['peptide_count_b'] += ($p['peptide_count_b']) ? $p['peptide_count_b'] : 0;
						}
					}
				}
				catch (ProtdbException $e){
					// ignore any errors - in this case the protein just doesn't get any classification added
				}
			}
			else {
				// add empty classifications
				$proteins['proteins'][$i]['classifications'] = array();
			}
			
			// set the status
			$this->setStatus(round($i / count($proteins['proteins']) * 100), "Inserting protein classifications...");
		}
		
		// get the classifications present in the sample
		$proteins['classifications'] = $this->_db->GetClassifications($classifications);		
	}
	
	private function drawImage() {
		$onlya = $_GET['pa'];
		$onlyb = $_GET['pb'];
		$both = $_GET['px'];
		
		if (!isset($onlya) || !isset($onlyb) || !isset($both)) {
			return;
		}
			
		// create the empty image
		$width = 70;
		$image = ImageCreate($width + 1, $width + 1);
		
		$total = $onlya + $onlyb + $both;
		$lastEnd = 0.0;
		
		// create the colors
		$white = ImageColorAllocate ($image, 255, 255, 255);
		$red = ImageColorAllocate ($image, 255, 165, 71); // #FFA647
		$green = ImageColorAllocate($image, 106, 253, 61); // #6AFD3D
		$black = ImageColorAllocate($image, 50, 50, 50);
		
		// draw the three pieces
		if ($onlya) {
			ImageFilledArc($image, $width / 2, $width / 2, $width, $width, $lastEnd, ($onlya / $total) * 360, $green, IMG_ARC_PIE);
			$lastEnd = ($onlya / $total) * 360;
		}
		
		if ($onlyb) {
			ImageFilledArc($image, $width / 2, $width / 2, $width, $width, $lastEnd, ($onlyb / $total) * 360 + $lastEnd, $red, IMG_ARC_EDGED);
			ImageFilledArc($image, $width / 2, $width / 2, $width, $width, $lastEnd, ($onlyb / $total) * 360 + $lastEnd, $black, IMG_ARC_NOFILL | IMG_ARC_EDGED);
			$lastEnd += ($onlyb / $total) * 360.0;
		}
		
		if ($both) {
			ImageFilledArc($image, $width / 2, $width / 2, $width, $width, $lastEnd, 360, $white, IMG_ARC_PIE);
			ImageFilledArc($image, $width / 2, $width / 2, $width, $width, $lastEnd, 360 , $black, IMG_ARC_NOFILL | IMG_ARC_EDGED);
		}
		
		// draw a black circle araound everything
		ImageFilledArc($image, $width / 2, $width / 2, $width, $width, 0, 360, $black, IMG_ARC_NOFILL);
		
		// send the image
		header ("Content-type: image/png");
		ImagePNG($image);
		
		ImageDestroy($image);
	}
	
	/**
	 * This function stores the gotten result in a file. This is used to enable functions like the file
	 * export as well as the history function of the browser. In case a result set was already saved before
	 * this file is being overwritten.
	 * @param $result - The array to save
	 */
	private function saveResultInFile($result) {		
		// make sure the status id was set
		if (!$this->_statusid)
			throw new Exception("Missing status identifier. Could not save result set.");
		
		// encode the data in json
		$json = json_encode($result);
		
		if (!$json)
			throw new Exception("Could not create JSON object.");
		
		// save the data
		file_put_contents(sys_get_temp_dir() . DIRECTORY_SEPARATOR . $this->_statusid . "result", $json);
	}
	
	/**
	 * Reads the saved result from the file and returns it as an array. If the file does not exist
	 * the function returns false. Throws an exception on error.
	 * @return array
	 */
	private function getSavedResult() {		
		// if the file doesn't exist return false
		if (!is_file(sys_get_temp_dir() . DIRECTORY_SEPARATOR . $this->_statusid . "result"))
			return false;
		
		$json = file_get_contents(sys_get_temp_dir() . DIRECTORY_SEPARATOR . $this->_statusid . "result");
		
		if (!$json)
			throw new Exception("Could not read from the result file.");
		
		return json_decode($json, true);
	}
	
	/**
	 * Send the last result set as a text file. Throws an excption on error.
	 */
	private function exportResult() {
		// make sure the required parameter is set
		if (!isset($_POST['filetype']))
			throw new Exception("Missing parameter");
		
		// try to get the last result
		$result = $this->getSavedResult();
		
		if (!$result)
			throw new Exception("No result to export.");
			
		// check if there's a filter submitted (list of accessions)
		$proteinAccessions = $_POST['filter'];
		
		if ($proteinAccessions) {
			$proteinAccessions = str_replace('\"', '"', $proteinAccessions);
			$proteinAccessions = json_decode($proteinAccessions, true);
		}
		
		// build the text file
		$tsv = "Accession\tName\tIdentified in\r\n";
		
		foreach($result['proteins'] as $p) {
			// ignore all proteins not selected by the user
			if ($_POST['filetype'] != "all" && $p['exists'] != $_POST['filetype'])
				continue;
				
			// ignore all proteins not in the filter
			if ($proteinAccessions && (!in_array($p['accession'], $proteinAccessions)))
				continue;
			
			// add the protein to the file
			$tsv .= $p['accession'] . "\t" . $p['name'] . "\t";
			
			switch($p['exists']) {
				case "a": $tsv .= "pool a"; break;
				case "b": $tsv .= "pool b"; break;
				case "x": $tsv .= "both"; break;
			}
			
			$tsv .= "\r\n";
		}
		
		// send the text file
		// check if compression is supported by the browser
		if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], "gzip") !== false) {
			// compress the json object and send the required header
			$tsv = gzencode($tsv, 9);
			header("Content-encoding: gzip");
		}
		
		// We'll be outputting a tsv file
		header('Content-type: text/tsv');
		header('Content-Length: ' . strlen($tsv));
		
		// Just use one file name		
		header('Content-Disposition: attachment; filename="result.txt"');
		
		echo $tsv;
	}
	
	/**
	 * Sends the last retrieved result.
	 */
	private function sendLastResult() {
		// get the last saved result		
		$result = $this->getSavedResult();
		
		if (!$result)	throw new Exception("No result saved.");
		
		$this->SendResult($result);
	}
}
?>
Return current item: GPDE