diff --git a/adapter/adapter-service/src/main/scala/net/shrine/adapter/Obfuscator.scala b/adapter/adapter-service/src/main/scala/net/shrine/adapter/Obfuscator.scala
index 9450aa826..dbd275c8b 100644
--- a/adapter/adapter-service/src/main/scala/net/shrine/adapter/Obfuscator.scala
+++ b/adapter/adapter-service/src/main/scala/net/shrine/adapter/Obfuscator.scala
@@ -1,77 +1,77 @@
package net.shrine.adapter
import com.typesafe.config.Config
import net.shrine.log.Log
import net.shrine.protocol.QueryResult
import scala.util.Random
/**
* @author Ricardo De Lima
* @author Bill Simons
* @author clint
* @author dwalend
* @since August 18, 2009
* @see http://cbmi.med.harvard.edu
*/
case class Obfuscator(binSize:Int,stdDev:Double,noiseClamp:Int) {
//todo a problem instead?
if((stdDev < 6.5) || (noiseClamp < 10) || (binSize < 5)) Log.warn(s"$this does not include enough obfuscation to prevent an unobservable reidentificaiton attack. We recommend stdDev >= 6.5, noiseClamp >= 10, and binSize >= 5")
val random = new Random
def obfuscateResults(doObfuscation: Boolean)(results: Seq[QueryResult]): Seq[QueryResult] = {
if (doObfuscation) results.map(obfuscate) else results
}
def obfuscate(result: QueryResult): QueryResult = {
val withObfscSetSize = result.modifySetSize(obfuscate)
if (withObfscSetSize.breakdowns.isEmpty) {
withObfscSetSize
}
else {
val obfuscatedBreakdowns = result.breakdowns.mapValues(_.mapValues(obfuscate))
withObfscSetSize.withBreakdowns(obfuscatedBreakdowns)
}
}
def obfuscate(l: Long): Long = {
def roundToNearest(i: Double, n: Double): Long = {
Math.round(
if ((i % n) >= n / 2) i + n - (i % n) //round up
else i - i % n //round down
)
}
def clampedGaussian(i: Long, clamp: Long): Double = {
val noise = random.nextGaussian() * stdDev
//clamp it
if (noise > clamp) clamp
else if (noise < -clamp) -clamp
else noise
}
//bin
val binned = roundToNearest(l, binSize)
//add noise
val noised = binned + clampedGaussian(binned, noiseClamp)
//bin again
val rounded = roundToNearest(noised, binSize)
//finally, if rounded is clamp or smaller, report that the result is too small.
- if(rounded > noiseClamp) rounded
+ if((rounded > noiseClamp) && (rounded > 10)) rounded //todo fix with SHRINE-1716
else Obfuscator.LESS_THAN_CLAMP //will be reported as "$clamped or fewer" //todo this happens elsewhere in the code, too. But where?
}
}
object Obfuscator {
def apply(config:Config): Obfuscator = Obfuscator(config.getInt("binSize"),config.getDouble("sigma"),config.getInt("clamp"))
val LESS_THAN_CLAMP = -1L
}
\ No newline at end of file
diff --git a/adapter/adapter-service/src/main/scala/net/shrine/adapter/RunQueryAdapter.scala b/adapter/adapter-service/src/main/scala/net/shrine/adapter/RunQueryAdapter.scala
index c1775fd82..4fec9b732 100644
--- a/adapter/adapter-service/src/main/scala/net/shrine/adapter/RunQueryAdapter.scala
+++ b/adapter/adapter-service/src/main/scala/net/shrine/adapter/RunQueryAdapter.scala
@@ -1,284 +1,284 @@
package net.shrine.adapter
import net.shrine.adapter.audit.AdapterAuditDb
import scala.util.Failure
import scala.util.Success
import scala.util.Try
import scala.xml.NodeSeq
import net.shrine.adapter.dao.AdapterDao
import net.shrine.adapter.translators.QueryDefinitionTranslator
import net.shrine.protocol.{AuthenticationInfo, BroadcastMessage, Credential, ErrorFromCrcException, ErrorResponse, HiveCredentials, I2b2ResultEnvelope, MissingCrCXmlResultException, QueryResult, RawCrcRunQueryResponse, ReadResultRequest, ReadResultResponse, ResultOutputType, RunQueryRequest, RunQueryResponse, ShrineResponse}
import net.shrine.client.Poster
import net.shrine.problem.{AbstractProblem, LoggingProblemHandler, Problem, ProblemNotYetEncoded, ProblemSources}
import scala.util.control.NonFatal
import net.shrine.util.XmlDateHelper
import scala.concurrent.duration.Duration
import scala.xml.XML
/**
* @author Bill Simons
* @author clint
* @since 4/15/11
* @see http://cbmi.med.harvard.edu
* @see http://chip.org
*
* NOTICE: This software comes with NO guarantees whatsoever and is
* licensed as Lgpl Open Source
* @see http://www.gnu.org/licenses/lgpl.html
*/
final case class RunQueryAdapter(
poster: Poster,
dao: AdapterDao,
override val hiveCredentials: HiveCredentials,
conceptTranslator: QueryDefinitionTranslator,
adapterLockoutAttemptsThreshold: Int, //Set to 0 to disable lockout. todo remove in SHRINE 1.24
doObfuscation: Boolean,
runQueriesImmediately: Boolean,
breakdownTypes: Set[ResultOutputType],
collectAdapterAudit:Boolean,
botCountTimeThresholds:Seq[(Long,Duration)],
obfuscator: Obfuscator
) extends CrcAdapter[RunQueryRequest, RunQueryResponse](poster, hiveCredentials) {
logStartup()
import RunQueryAdapter._
override protected[adapter] def parseShrineResponse(xml: NodeSeq) = RawCrcRunQueryResponse.fromI2b2(breakdownTypes)(xml).get //TODO: Avoid .get call
override protected[adapter] def translateNetworkToLocal(request: RunQueryRequest): RunQueryRequest = {
try { request.mapQueryDefinition(conceptTranslator.translate) }
catch {
case NonFatal(e) => throw new AdapterMappingException(request,s"Error mapping query terms from network to local forms.", e)
}
}
override protected[adapter] def processRequest(message: BroadcastMessage): ShrineResponse = {
if (collectAdapterAudit) AdapterAuditDb.db.insertQueryReceived(message)
if (isLockedOut(message.networkAuthn)) {
throw new AdapterLockoutException(message.networkAuthn,poster.url)
}
dao.checkIfBot(message.networkAuthn,botCountTimeThresholds)
val runQueryReq = message.request.asInstanceOf[RunQueryRequest]
//We need to use the network identity from the BroadcastMessage, since that will have the network username
//(ie, ecommons) of the querying user. Using the AuthenticationInfo from the incoming request breaks the fetching
//of previous queries on deployed systems where the credentials in the identity param to this method and the authn
//field of the incoming request are different, like the HMS Shrine deployment.
//NB: Credential field is wiped out to preserve old behavior -Clint 14 Nov, 2013
val authnToUse = message.networkAuthn.copy(credential = Credential("", isToken = false))
if (!runQueriesImmediately) {
debug(s"Queueing query from user ${message.networkAuthn.domain}:${message.networkAuthn.username}")
storeQuery(authnToUse, message, runQueryReq)
} else {
debug(s"Performing query from user ${message.networkAuthn.domain}:${message.networkAuthn.username}")
val result: ShrineResponse = runQuery(authnToUse, message.copy(request = runQueryReq.withAuthn(authnToUse)), runQueryReq.withAuthn(authnToUse))
if (collectAdapterAudit) AdapterAuditDb.db.insertResultSent(runQueryReq.networkQueryId,result)
result
}
}
private def storeQuery(authnToUse: AuthenticationInfo, message: BroadcastMessage, request: RunQueryRequest): RunQueryResponse = {
//Use dummy ids for what we would have received from the CRC
val masterId: Long = -1L
val queryInstanceId: Long = -1L
val resultId: Long = -1L
//TODO: is this right?? Or maybe it's project id?
val groupId = authnToUse.domain
- val invalidSetSize = -1L
+ val invalidSetSize = -1L //This might be where the results of 10 or fewer become -1
val now = XmlDateHelper.now
val queryResult = QueryResult(resultId, queryInstanceId, Some(ResultOutputType.PATIENT_COUNT_XML), invalidSetSize, Some(now), Some(now), Some("Query enqueued for later processing"), QueryResult.StatusType.Held, Some("Query enqueued for later processing"))
dao.inTransaction {
val insertedQueryId = dao.insertQuery(masterId.toString, request.networkQueryId, authnToUse, request.queryDefinition, isFlagged = false, hasBeenRun = false, flagMessage = None)
val insertedQueryResultIds = dao.insertQueryResults(insertedQueryId, Seq(queryResult))
//NB: We need to insert dummy QueryResult and Count records so that calls to StoredQueries.retrieve() in
//AbstractReadQueryResultAdapter, called when retrieving results for previously-queued-or-incomplete
//queries, will work.
val countQueryResultId = insertedQueryResultIds(ResultOutputType.PATIENT_COUNT_XML).head
dao.insertCountResult(countQueryResultId, -1L, -1L)
}
RunQueryResponse(masterId, XmlDateHelper.now, authnToUse.username, groupId, request.queryDefinition, queryInstanceId, queryResult)
}
private def runQuery(authnToUse: AuthenticationInfo, message: BroadcastMessage, request: RunQueryRequest): ShrineResponse = {
if (collectAdapterAudit) AdapterAuditDb.db.insertExecutionStarted(request)
//NB: Pass through ErrorResponses received from the CRC.
//See: https://open.med.harvard.edu/jira/browse/SHRINE-794
val result = super.processRequest(message) match {
case e: ErrorResponse => e
case rawRunQueryResponse: RawCrcRunQueryResponse => processRawCrcRunQueryResponse(authnToUse, request, rawRunQueryResponse)
}
if (collectAdapterAudit) AdapterAuditDb.db.insertExecutionCompletedShrineResponse(request,result)
result
}
private[adapter] def processRawCrcRunQueryResponse(authnToUse: AuthenticationInfo, request: RunQueryRequest, rawRunQueryResponse: RawCrcRunQueryResponse): RunQueryResponse = {
def isBreakdown(result: QueryResult) = result.resultType.exists(_.isBreakdown)
val originalResults: Seq[QueryResult] = rawRunQueryResponse.results
val (originalBreakdownResults, originalNonBreakDownResults): (Seq[QueryResult],Seq[QueryResult]) = originalResults.partition(isBreakdown)
val originalBreakdownCountAttempts: Seq[(QueryResult, Try[QueryResult])] = attemptToRetrieveBreakdowns(request, originalBreakdownResults)
val (successfulBreakdownCountAttempts, failedBreakdownCountAttempts) = originalBreakdownCountAttempts.partition { case (_, t) => t.isSuccess }
val failedBreakdownCountAttemptsWithProblems = failedBreakdownCountAttempts.map { attempt =>
val originalResult: QueryResult = attempt._1
val queryResult:QueryResult = if (originalResult.problemDigest.isDefined) originalResult
else {
attempt._2 match {
case Success(_) => originalResult
case Failure(x) => //noinspection RedundantBlock
{
val problem:Problem = x match {
case e: ErrorFromCrcException => ErrorFromCrcBreakdown(e)
case e: MissingCrCXmlResultException => CannotInterpretCrcBreakdownXml(e)
case NonFatal(e) => {
val summary = s"Unexpected exception while interpreting breakdown response"
ProblemNotYetEncoded(summary, e)
}
}
//TODO: is this needed? LoggingProblemHandler.handleProblem(problem)
originalResult.copy(problemDigest = Some(problem.toDigest))
}
}
}
(queryResult,attempt._2)
}
logBreakdownFailures(rawRunQueryResponse, failedBreakdownCountAttemptsWithProblems)
val originalMergedBreakdowns: Map[ResultOutputType, I2b2ResultEnvelope] = {
val withBreakdownCounts = successfulBreakdownCountAttempts.collect { case (_, Success(queryResultWithBreakdowns)) => queryResultWithBreakdowns }
withBreakdownCounts.map(_.breakdowns).fold(Map.empty)(_ ++ _)
}
val obfuscatedQueryResults = originalResults.map(obfuscator.obfuscate)
val obfuscatedNonBreakdownQueryResults = obfuscatedQueryResults.filterNot(isBreakdown)
val obfuscatedMergedBreakdowns = originalMergedBreakdowns.mapValues(_.mapValues(obfuscator.obfuscate))
val failedBreakdownTypes = failedBreakdownCountAttemptsWithProblems.flatMap { case (qr, _) => qr.resultType }
dao.storeResults(
authn = authnToUse,
masterId = rawRunQueryResponse.queryId.toString,
networkQueryId = request.networkQueryId,
queryDefinition = request.queryDefinition,
rawQueryResults = originalResults,
obfuscatedQueryResults = obfuscatedQueryResults,
failedBreakdownTypes = failedBreakdownTypes,
mergedBreakdowns = originalMergedBreakdowns,
obfuscatedBreakdowns = obfuscatedMergedBreakdowns)
// at this point the queryResult could be a mix of successes and failures.
// SHRINE reports only the successes. See SHRINE-1567 for details
val queryResults: Seq[QueryResult] = if (doObfuscation) obfuscatedNonBreakdownQueryResults else originalNonBreakDownResults
val breakdownsToReturn: Map[ResultOutputType, I2b2ResultEnvelope] = if (doObfuscation) obfuscatedMergedBreakdowns else originalMergedBreakdowns
//TODO: Will fail in the case of NO non-breakdown QueryResults. Can this ever happen, and is it worth protecting against here?
//can failedBreakdownCountAttempts be mixed back in here?
val resultWithBreakdowns: QueryResult = queryResults.head.withBreakdowns(breakdownsToReturn)
if(debugEnabled) {
def justBreakdowns(breakdowns: Map[ResultOutputType, I2b2ResultEnvelope]) = breakdowns.mapValues(_.data)
val obfuscationMessage = s"obfuscation is ${if(doObfuscation) "ON" else "OFF"}"
debug(s"Returning QueryResult with count ${resultWithBreakdowns.setSize} (original count: ${originalNonBreakDownResults.headOption.map(_.setSize)} ; $obfuscationMessage)")
debug(s"Returning QueryResult with breakdowns ${justBreakdowns(resultWithBreakdowns.breakdowns)} (original breakdowns: ${justBreakdowns(originalMergedBreakdowns)} ; $obfuscationMessage)")
debug(s"Full QueryResult: $resultWithBreakdowns")
}
//if any results had problems, this commented out code can turn it into an error QueryResult
//See SHRINE-1619
//val problem: Option[ProblemDigest] = failedBreakdownCountAttemptsWithProblems.headOption.flatMap(x => x._1.problemDigest)
//val queryResult = problem.fold(resultWithBreakdowns)(pd => QueryResult.errorResult(Some(pd.description),"Error with CRC",pd))
rawRunQueryResponse.toRunQueryResponse.withResult(resultWithBreakdowns)
}
private def getResultFromCrc(parentRequest: RunQueryRequest, networkResultId: Long): Try[ReadResultResponse] = {
def readResultRequest(runQueryReq: RunQueryRequest, networkResultId: Long) = ReadResultRequest(hiveCredentials.projectId, runQueryReq.waitTime, hiveCredentials.toAuthenticationInfo, networkResultId.toString)
Try(XML.loadString(callCrc(readResultRequest(parentRequest, networkResultId)))).flatMap(ReadResultResponse.fromI2b2(breakdownTypes))
}
private[adapter] def attemptToRetrieveCount(runQueryReq: RunQueryRequest, originalCountQueryResult: QueryResult): (QueryResult, Try[QueryResult]) = {
originalCountQueryResult -> (for {
countData <- getResultFromCrc(runQueryReq, originalCountQueryResult.resultId)
} yield originalCountQueryResult.withSetSize(countData.metadata.setSize))
}
private[adapter] def attemptToRetrieveBreakdowns(runQueryReq: RunQueryRequest, breakdownResults: Seq[QueryResult]): Seq[(QueryResult, Try[QueryResult])] = {
breakdownResults.map { origBreakdownResult =>
origBreakdownResult -> (for {
breakdownData <- getResultFromCrc(runQueryReq, origBreakdownResult.resultId).map(_.data)
} yield origBreakdownResult.withBreakdown(breakdownData))
}
}
private[adapter] def logBreakdownFailures(response: RawCrcRunQueryResponse,
failures: Seq[(QueryResult, Try[QueryResult])]) {
for {
(origQueryResult, Failure(e)) <- failures
} {
error(s"Couldn't load breakdown for QueryResult with masterId: ${response.queryId}, instanceId: ${origQueryResult.instanceId}, resultId: ${origQueryResult.resultId}. Asked for result type: ${origQueryResult.resultType}", e)
}
}
private def isLockedOut(authn: AuthenticationInfo): Boolean = {
adapterLockoutAttemptsThreshold match {
case 0 => false
case _ => dao.isUserLockedOut(authn, adapterLockoutAttemptsThreshold)
}
}
private def logStartup(): Unit = {
val message = {
if (runQueriesImmediately) { s"${getClass.getSimpleName} will run queries immediately" }
else { s"${getClass.getSimpleName} will queue queries for later execution" }
}
info(message)
}
}
case class ErrorFromCrcBreakdown(x:ErrorFromCrcException) extends AbstractProblem(ProblemSources.Adapter) {
override val throwable = Some(x)
override val summary: String = "The CRC reported an error."
override val description = "The CRC reported an internal error."
}
case class CannotInterpretCrcBreakdownXml(x:MissingCrCXmlResultException) extends AbstractProblem(ProblemSources.Adapter) {
override val throwable = Some(x)
override val summary: String = "SHRINE cannot interpret the CRC response."
override val description = "The CRC responded, but SHRINE could not interpret that response."
}
\ No newline at end of file
diff --git a/shrine-webclient/src/main/html/js-i2b2/cells/CRC/CRC_view_Graphs.js b/shrine-webclient/src/main/html/js-i2b2/cells/CRC/CRC_view_Graphs.js
index a15b9a606..1c1325191 100644
--- a/shrine-webclient/src/main/html/js-i2b2/cells/CRC/CRC_view_Graphs.js
+++ b/shrine-webclient/src/main/html/js-i2b2/cells/CRC/CRC_view_Graphs.js
@@ -1,1044 +1,1044 @@
/** -----------------------------------------------------------------------------------------------------------------------
* @projectDescription View controller for the query graph window (which is a GUI-only component of the CRC module).
* @inherits i2b2.CRC.view
* @namespace i2b2.CRC.view.graphs
* @author Shawn Murphy MD PhD, Hannah Murphy
* @version 1.7
* @description This set of functions uses D3 and its derivative C3 to graph the text that results in the query-status-window
* of the i2b2 web client.
* The main function is "createGraphs".
* Because it makes extensive use of Vector Graphic in the D3 library it will only work in Microsoft Internet
* Explorer 9 and above. It assumes the STATUS window is a specific height which is 146px. In theory the
* Width can vary, but right not it is set to 535px in many places.
* It draws the graphs in a div (which should be the dimensions above), using a string which is essentially
* screen-scraped from the text what is placed in the query_status box of the web client. To distinguish the
* normal i2b2 vs. SHRINE text, a boolean flag is used. A regular i2b2 result (bIsMultiSite = false)
* or a SHRINE result (bIsMultiSite = true).
* Internally, everything works off an array the is produced from the test that is a six element array of
* **** 0 "query name", 1 "title", 2 "site", 3 "element name", 4 quantity, 5 "sentence" ****
* for example, one element would be:
* **** ["Circulatory sys@20:21:19", "Age patient breakdown", "MGH" "0-9 years old", 0, "0-9 years old: 0"] ****
* It also uses some jQuery, but only for the scroll bar function with *** jQuery Stuff ***
* in the comments.
* There are four web client javascript files in the CRC folder that have references to functions in this
* javascript file, and the default.htm folder in the main web client folder, they are:
* CRC_view_Status, CRC_ctlr_QryStatus, CRC_ctlr_QryTools, and cell_config_data
** -----------------------------------------------------------------------------------------------------------------------*/
console.group('Load & Execute component file: CRC > view > Graphs');
console.time('execute time');
//i2b2.PM.model.isObfuscated = true; // for testing
// Constants
var msSpecialBreakdownSite = ""; // this constant designates a site from which the breakdown arrays will always be obtained
var msStringDefineingNumberOfPatients = "number of patients"; // this constant is what appears in the breakdown text
// which is the number of patients and is lower cased and trimmed on both sides for comparison
// create and save the screen objects and more constants
i2b2.CRC.view.graphs = new i2b2Base_cellViewController(i2b2.CRC, 'graphs');
i2b2.CRC.view.graphs.visible = false;
i2b2.CRC.view.graphs.iObfuscatedFloorNumber = 3; // this is the amount reported that the numbers are obfuscated by
i2b2.CRC.view.graphs.sObfuscatedText = "<3"; // this is the text that is replaced for a small number in obfuscated mode
// so that it can be cleaned up before the next display
i2b2.CRC.view.graphs.sObfuscatedEnding = "±3"; //this is the text that is added to all numbers in obfuscated mode
-i2b2.CRC.view.graphs.sObfuscatedSHRINEText = "10 patients or fewer"; // this is the text that is replaced for a small number in obfuscated mode
+i2b2.CRC.view.graphs.sObfuscatedSHRINEText = "10 patients or fewer"; // this is the text that is replaced for a small number in obfuscated mode //todo fix with SHRINE-1716
// so that it can be cleaned up before the next display
-i2b2.CRC.view.graphs.sObfuscatedSHRINEEnding = "+-3 patients"; //this is the text that is added to all numbers in obfuscated mode
+i2b2.CRC.view.graphs.sObfuscatedSHRINEEnding = "+-10 patients"; //this is the text that is added to all numbers in obfuscated mode //todo fix with SHRINE-1716
//i2b2.CRC.view.graphs.bIsSHRINE = false; // this changes the way the graphs are made if the file is being run in SHRINE mode
// NOTE THAT THIS IS DEMO ONLY IN THIS VERSION - IT DOES NOT REALLY WORK
i2b2.CRC.view.graphs.asTitleOfShrineGroup = [];
// These functions manage the graph divs, but DECISIONS are made in the CRC_view_Status code
i2b2.CRC.view.graphs.show = function() {
i2b2.CRC.view.graphs.visible = true;
$('crcGraphsBox').show();
}
i2b2.CRC.view.graphs.hide = function() {
i2b2.CRC.view.graphs.visible = false;
$('crcGraphsBox').hide();
}
i2b2.CRC.view.graphs.showDisplay = function() {
var targs = $('infoQueryStatusChart').parentNode.parentNode.select('DIV.tabBox.active');
// remove all active tabs
targs.each(function(el) { el.removeClassName('active'); });
// set us as active
$('infoQueryStatusChart').parentNode.parentNode.select('DIV.tabBox.tabQueryGraphs')[0].addClassName('active');
$('infoQueryStatusChart').show();
}
i2b2.CRC.view.graphs.hideDisplay = function() {
$('infoQueryStatusChart').hide();
}
// ================================================================================================== //
/*********************************************************************************
FUNCTION createGraphs
Takes a Div, the text from the query status view, and a multisite flag and populates the Div
**********************************************************************************/
i2b2.CRC.view.graphs.createGraphs = function(sDivName, sInputString, bIsMultiSite) {
try {
if (sDivName === undefined || sDivName === null || sDivName === "") throw ("ERROR 201 - sDivName in function createGraphs is null");
//i2b2.CRC.view.graphs.sNameOfPreviousDiv = sDivName;
i2b2.CRC.view.graphs.clearGraphs(sDivName);
if (!i2b2.CRC.view.graphs.bisGTIE8) {
i2b2.CRC.view.graphs.aDivForIE8(sDivName);
return;
}
// if (bIsMultiSite) sInputString = i2b2.CRC.view.graphs.returnTestString(true); //For testing
if (sInputString === undefined || sInputString === null || sInputString === "") throw ("ERROR 202 - sInputString in function createGraphs is null");
var asBreakdownArray = [[]];
var iBreakdown = 0;
// make the input array
var asInputArray = i2b2.CRC.view.graphs.parseInputIntoArray(sInputString, bIsMultiSite);
// Pull out unique breakdown types
var asBreakdownTypes = [];
var iBreakdown = 0;
for (var i = 0; i < asInputArray.length; i++) {
asBreakdownTypes[iBreakdown] = asInputArray[i][1];
iBreakdown++;
}
var asUniqueBreakdownTypes = [];
for (var i=0; i < asBreakdownTypes.length; i++) {
if (asUniqueBreakdownTypes.indexOf(asBreakdownTypes[i]) === -1 && asBreakdownTypes[i] !== '')
asUniqueBreakdownTypes.push(asBreakdownTypes[i]);
}
if (asUniqueBreakdownTypes.length === 0) throw ("ERROR 203 in createGraphs, there are no breakdown types in *unique* array");
// rearrange unique array so that patient number is on the top
for (var i = 0; i < asUniqueBreakdownTypes.length; i++) {
if (asUniqueBreakdownTypes[i].toLowerCase().trim() == msStringDefineingNumberOfPatients.toLowerCase().trim()) {
var sTempVariable = asUniqueBreakdownTypes[0];
asUniqueBreakdownTypes[0] = asUniqueBreakdownTypes[i];
asUniqueBreakdownTypes[i] = sTempVariable;
break;
}
}
//Make Divs in the original div for the charts
oParentDiv = document.getElementById(sDivName);
for (var i=0; i0)
{
for(var x = 1; x < allSites.length ; x++)
{
var siteName = allSites[x];
siteReturningResults.set(siteName,x); //SiteName,Index pairs
sitesReturningError.set(siteName,true);
}
}
for(var x = 2; x < _2DResultsArray.length ; x++) //Skipping the site name and patient count rows
{
var thisSiteData = _2DResultsArray[x];
for(var y = 1 ; y < thisSiteData.length; y++) //Skip the category name column
{
if(thisSiteData[y] != " ")
{
var siteName = null;
siteReturningResults.each(function(item){
if(item.value==y)
{
siteName = item.key;
}
});
if(siteName){
sitesReturningError.unset(siteName);
}
}
}
}
sitesReturningError.each(function(item){
if(siteReturningResults.get(item.key))
siteReturningResults.unset(item.key);
});
}
var titleDiv = jQuery("#" + sDivName + ' #chartTitle0');
if(titleDiv)
titleDiv.html("Patient Count");
graph_multiplesite_patient_number("#" + sDivName + " #chart0", asUniqueBreakdownTypes[0], asInputArray);
for (var i=1; i=0) continue;
if(title.toLowerCase().indexOf('vital')>=0)
title = title + ' Status';
var chartTitle = "Patient " + title + " Count Breakdown";
var titleDiv = jQuery("#" + sDivName + ' #chartTitle' + i);
if(titleDiv)
titleDiv.html(chartTitle);
var dataArray = i2b2.CRC.view.graphs.getGraphDataArray(_2DResultsArray,asUniqueBreakdownTypes[i],siteReturningResults);
graph_multiplesite_patient_breakdown("#" + sDivName + " #" + chartDivId, siteReturningResults.keys(), dataArray);
}
}
}
catch(err) {
console.error(err);
}
} // END of function createGraphs
i2b2.CRC.view.graphs.getGraphDataArray = function(_2DResultsArray,breakdownType,siteHash) {
var maxRowsNum = siteHash.size();
if(maxRowsNum>0)
{
var breakdownHash = $H();
for(x=2;x<_2DResultsArray.length;x++) //Skip the first row which consists of only site names and 2nd row that has total patient count
{
var thisRow = _2DResultsArray[x];
var category = thisRow[0];
if(category == " ") continue;
if(category.toLowerCase().indexOf(breakdownType.toLowerCase())>=0)
{
breakdownHash.set(category,x);
}
}
var maxColsNum = breakdownHash.size() + 1;
var graphDataArray = new Array(maxRowsNum+1);
for (var i = 0; i <= maxRowsNum; i++) {
graphDataArray[i] = new Array(maxColsNum);
}
graphDataArray[0][0] = 'x';
var dataArrayColIndex = 1;
breakdownHash.each(function(item){
var dataArrayRowIndex = 0;
var inpArrayRowIndex = item.value;
var breakDownCategory = item.key;
var breakDown = breakDownCategory;
var tempBrkDn = breakDownCategory.split("|");
if(tempBrkDn.length>1)
breakDown = tempBrkDn[1];
graphDataArray[dataArrayRowIndex][dataArrayColIndex] = breakDown;
dataArrayRowIndex++;
siteHash.each(function(entry){
var siteName = entry.key;
var inpArrayColIndex = entry.value;
graphDataArray[dataArrayRowIndex][0] = siteName;
var breakDownValue = _2DResultsArray[inpArrayRowIndex][inpArrayColIndex];
if(breakDownValue == " ")
breakDownValue = "Not Provided";
graphDataArray[dataArrayRowIndex][dataArrayColIndex] = breakDownValue;
dataArrayRowIndex++;
});
dataArrayColIndex++;
});
return graphDataArray;
}
else
return null;
}
/*****************************************************************************************************
@Function i2b2.CRC.view.graphs.parseInputIntoArray(sInputString, isMultiSite)
@Input (String *text from query status*, Boolean false = single site, true = multiple site)
@Output Create a two dimensional array out of these strings called asInputFragments
Each array element is a six element array of 0 "query name", 1 "title", 2 "site", 3 "element name", 4 quantity, 5 "sentence"
for example, one element would be ["Circulatory sys@20:21:19", "Age patient breakdown", "MGH" "0-9 years old", 0, "0-9 years old: 0"]
*****************************************************************************************************/
i2b2.CRC.view.graphs.parseInputIntoArray = function(sInputString, isMultiSite) {
var sCheckForNothing = "something"; // this gets checked to be a zero length string
try {
var old_demo = false;
if (sInputString === undefined || sInputString === null || sInputString === "") throw ("ERROR - sInputString in function parseInputIntoArray is empty");
var asInputFragments = [[]];
if (!isMultiSite) {
var asTempArray = [];
var sLatestTitle, sLatestQueryName, sLatestElementName, iLatestQuantity, sLatestSite;
// process input one line at a time to look for the start of a block.
// you know it because it has two \"'s
// begin your parsing by separating into an array of sentences that were delimited with \n
var asInputSentences = sInputString.split("\n");
var iFragmentArrayCounter = 0;
for(var i = 0; i < asInputSentences.length; i++) {
if (asInputSentences[i].indexOf("for") > 0) {
asTempArray = asInputSentences[i].split("for");
sLatestTitle = asTempArray[0];
sLatestQueryName = asTempArray[1];
sLatestSite = ".";
} else if (asInputSentences[i].indexOf(":") > 0) {
asTemp2Array = asInputSentences[i].split(":");
sLatestElementName = asTemp2Array[0];
iLatestQuantity = asTemp2Array[1];
asInputFragments[iFragmentArrayCounter] = new Array (6);
asInputFragments[iFragmentArrayCounter][0] = sLatestQueryName;
asInputFragments[iFragmentArrayCounter][1] = sLatestTitle;
asInputFragments[iFragmentArrayCounter][2] = sLatestSite;
asInputFragments[iFragmentArrayCounter][3] = sLatestElementName;
asInputFragments[iFragmentArrayCounter][4] = i2b2.CRC.view.graphs.sValueOfi2b2Text(iLatestQuantity);
asInputFragments[iFragmentArrayCounter][5] = asInputSentences[i];
for(var j = 0; j < 6; j++) {
}
iFragmentArrayCounter++;
} else {
}
}
}
else if (old_demo == true) { // parsing for old_demo SHRINE strings
var asTempArray = [];
var asTemp2Array = [];
var sLatestTitle, sLatestQueryName, sLatestElementName, iLatestQuantity, sLatestSite;
// process input one line at a time to look for the start of a block.
// you know it because it has two \"'s
// begin your parsing by separating into an array of sentences that were delimited with \n
var asInputSentences = sInputString.split("\n");
var iFragmentArrayCounter = 0;
for (var i = 0; i < asInputSentences.length; i++) {
if (asInputSentences[i].indexOf("for") > 0) {
asTempArray = asInputSentences[i].split("for");
sLatestTitle = asTempArray[0];
if (asTempArray[1].indexOf("=") > 0) {
asTemp2Array = asTempArray[1].split("=");
sLatestQueryName = asTemp2Array[0];
sLatestSite = asTemp2Array[1];
}
else {
sLatestQueryName = asTempArray[1];
sLatestSite = "xxx";
}
} else if (asInputSentences[i].indexOf(":") > 0) {
asTemp2Array = asInputSentences[i].split(":");
sLatestElementName = asTemp2Array[0];
iLatestQuantity = asTemp2Array[1];
//document.write("
Element " + i + " = " + asInputSentences[i]);
asInputFragments[iFragmentArrayCounter] = new Array (6);
asInputFragments[iFragmentArrayCounter][0] = sLatestQueryName;
asInputFragments[iFragmentArrayCounter][1] = sLatestTitle;
asInputFragments[iFragmentArrayCounter][2] = sLatestSite;
asInputFragments[iFragmentArrayCounter][3] = sLatestElementName;
asInputFragments[iFragmentArrayCounter][4] = iLatestQuantity;
asInputFragments[iFragmentArrayCounter][5] = asInputSentences[i];
iFragmentArrayCounter++;
} else {
}
}
}
else { // parsing for SHRINE strings
//Read all the main breakdown categories
var asInputSentences = sInputString.split("\n");
var actualBrkDowns = $H();
for (var i = 0; i < asInputSentences.length; i++) {
var thisLine = asInputSentences[i];
if(thisLine && thisLine.toLowerCase().trim().indexOf('breakdown')>0)
{
asTemp2Array = thisLine.split(":"); // Finding a type of breakdown
mainCategory = asTemp2Array[0].trim();
actualBrkDowns.set(mainCategory,mainCategory);
}
}
i2b2.CRC.view.graphs.asTitleOfShrineGroup = [];
actualBrkDowns.each(function(category)
{
i2b2.CRC.view.graphs.asTitleOfShrineGroup.push(category.key);
});
var asInputFragments = [[]];
try{
// parsing for SHRINE strings
var asTempArray = [];
var asTemp2Array = [];
var asInputSentencesBySite =[];
var sLatestTitle, sLatestQueryName, sLatestElementName, iLatestQuantity, sLatestSite, sQueryName;
var aSiteArray = [];
var aTempSiteArray = [];
// process input one line at a time to look for the start of a block.
// you know it because it has two \"'s
// begin your parsing by separating into an array of sentences that were delimited with \n
var iQueryNameIndex = -1;
// look for query title
for (var i = 0; i < asInputSentences.length; i++) {
if (asInputSentences[i].indexOf("Finished Query:") !== -1) {
asTempArray = asInputSentences[i].split('Finished Query:');
sQueryName = asTempArray[1].trim();
iQueryNameIndex = i;
break;
}
}
if (iQueryNameIndex == -1) return;
sLatestQueryName = sQueryName;
// look for Hospitals (which are to the right of the query name) and put each hospital into an array
var iSiteNumber = -1;
for (var i = iQueryNameIndex+1; i < asInputSentences.length; i++) {
if (asInputSentences[i].indexOf(sQueryName) !== -1) {
if (iSiteNumber !== -1) aSiteArray[iSiteNumber] = aTempSiteArray;
iSiteNumber = iSiteNumber + 1;
aTempSiteArray = new Array(3);
asTempArray = asInputSentences[i].split(sQueryName);
aTempSiteArray[0] = asTempArray[0].trim();
aTempSiteArray[1] = i;
} else {
aTempSiteArray[2] = i;
}
}
if (iSiteNumber == -1) return;
aSiteArray[iSiteNumber] = aTempSiteArray;
var iBeginIndex;
var iEndIndex;
var iFragmentArrayCounter=0;
for(var ii = 0; ii <= iSiteNumber; ii++) {
sLatestSite = aSiteArray[ii][0];
iBeginIndex = aSiteArray[ii][1];
iEndIndex = aSiteArray[ii][2];
for(var i = iBeginIndex; i < iEndIndex; i++) {
var a = asInputSentences[i];
if (asInputSentences[i].indexOf("Patient Count") > -1) {
asTemp2Array = asInputSentences[i].split(" -");
sLatestTitle = 'Patient Count';
sLatestElementName = 'Patient Count';
iLatestQuantity = asTemp2Array[1];
asInputFragments[iFragmentArrayCounter] = new Array (6);
asInputFragments[iFragmentArrayCounter][0] = sLatestQueryName;
asInputFragments[iFragmentArrayCounter][1] = sLatestTitle;
asInputFragments[iFragmentArrayCounter][2] = sLatestSite;
asInputFragments[iFragmentArrayCounter][3] = sLatestElementName;
asInputFragments[iFragmentArrayCounter][4] = i2b2.CRC.view.graphs.sValueOfSHRINEText(iLatestQuantity);
asInputFragments[iFragmentArrayCounter][5] = asInputSentences[i];
iFragmentArrayCounter++;
} else if (i2b2.CRC.view.graphs.asTitleOfShrineGroup.indexOf(asInputSentences[i]) > -1) {
asTemp2Array = asInputSentences[i].split(" "); // Finding a type of breakdown
sLatestTitle = asTemp2Array[1].trim();
sCheckForNothing = asTemp2Array[1];
} else if (asInputSentences[i].indexOf(" -") > -1) { // parsing the values
//Lets make sure the - is not part of query name
if (asInputSentences[i].indexOf('"') < 0){
asTemp2Array = asInputSentences[i].split(" -");
sLatestElementName = asTemp2Array[0];
iLatestQuantity = asTemp2Array[1];
if (i2b2.CRC.view.graphs.asTitleOfShrineGroup.indexOf(sLatestElementName.toLowerCase().trim()) > -1) {
sLatestTitle = sLatestElementName.trim();
sCheckForNothing = iLatestQuantity;
}
asInputFragments[iFragmentArrayCounter] = new Array (6);
asInputFragments[iFragmentArrayCounter][0] = sLatestQueryName;
asInputFragments[iFragmentArrayCounter][1] = sLatestTitle;
asInputFragments[iFragmentArrayCounter][2] = sLatestSite;
asInputFragments[iFragmentArrayCounter][3] = sLatestElementName;
asInputFragments[iFragmentArrayCounter][4] = i2b2.CRC.view.graphs.sValueOfSHRINEText(iLatestQuantity);
asInputFragments[iFragmentArrayCounter][5] = asInputSentences[i];
iFragmentArrayCounter++;
}
} else if ((asInputSentences[i].indexOf("ERROR") > -1) || (asInputSentences[i].indexOf("Still Processing Request") > -1) || (asInputSentences[i].indexOf("Results not available") > -1)) {
asTemp2Array = asInputSentences[i].split(" : ");
sLatestError = asTemp2Array[0];
iLatestErrorMsg = asTemp2Array[1];
asInputFragments[iFragmentArrayCounter] = new Array (6);
asInputFragments[iFragmentArrayCounter][0] = sLatestQueryName;
asInputFragments[iFragmentArrayCounter][1] = sLatestError;
asInputFragments[iFragmentArrayCounter][2] = sLatestSite;
asInputFragments[iFragmentArrayCounter][3] = iLatestErrorMsg;
asInputFragments[iFragmentArrayCounter][4] = '';
asInputFragments[iFragmentArrayCounter][5] = '';
iFragmentArrayCounter++;
} else {
//Not required to parse
} // and of fragment processing if statement
//document.write("
Element " + i + " = " + sLatestTitle + " " + sLatestQueryName + " " + sLatestElementName + " " + iLatestQuantity);
} //
} // end of for loop that processes sites in SHRINE
}
catch(err)
{
console.error(err);
}
} // end of if statement that directs to single or multisite
return asInputFragments
}
catch(err) {
console.error(err);
}
} // END of function i2b2.CRC.view.graphs.parseInputIntoArray
/*********************************************************************************
FUNCTION graph_singlesite_patient_number
Fills in the Div for a single patient number display
**********************************************************************************/
function graph_singlesite_patient_number(sDivName, sBreakdownType, asInputFragments) {
try {
if (sBreakdownType === undefined || sBreakdownType === null) throw ("ERROR - sBreakdownType in function graph_patient_breakdown is null");
var asBreakdownArray = [[]];
var iBreakdown = 0;
// for loop to only pull out data from the breakdown type specified in sBreakdownType variable
for (var i = 0; i < asInputFragments.length; i++) {
if (asInputFragments[i][1].toLowerCase().trim() === sBreakdownType.toLowerCase().trim()) {
//document.write("
OK? " + i + " = " + asInputFragments[i][0]);
asBreakdownArray[iBreakdown] = new Array (2);
asBreakdownArray[iBreakdown][0] = asInputFragments[i][3];
asBreakdownArray[iBreakdown][1] = asInputFragments[i][4];
iBreakdown++;
} else {
//document.write("
ERROR? " + i + " = " + asInputFragments[i][0]);
}
}
// establish style and draw out containing Div
var sDivStyle = "font-family: Verdana, Geneva, sans-serif;"
+ "font-size: 12px;"
+ "text-align: center;"
+ "vertical-align: middle;"
+ "background-color: white;"
+ "width: 100%;";
document.getElementById(sDivName).setAttribute("style",sDivStyle);
// establish table in Div and set up its style.
var sDisplayNumber = i2b2.CRC.view.graphs.sTexti2b2Value(asBreakdownArray[0][1]);
var sTableHtml = '' +
'' +
'  | ' +
'
' +
'' +
''+sBreakdownType+' | ' +
'
' +
'' +
''+sDisplayNumber+' | ' +
'
' +
'' +
'For Query '+asInputFragments[0][0]+' | ' +
'
' +
'' +
'  | ' +
'
' +
'
';
document.getElementById(sDivName).innerHTML = sTableHtml;
}
catch(err) {
console.error(err);
}
} // END of function graph_single_patient_number
/*********************************************************************************
FUNCTION graph_singlesite_patient_breakdown
function where the dataset is displayed
**********************************************************************************/
function graph_singlesite_patient_breakdown(sDivName,sBreakdownType,asInputFragments) {
try {
if (sBreakdownType === undefined || sBreakdownType === null) throw ("ERROR 101 in graph_patient_breakdown, sBreakdownType is null");
var asBreakdownArray = [[]];
var iBreakdown = 0;
// for loop to only pull out data from the breakdown type specified in sBreakdownType variable
// for multiple sites make a data array for each site
for (var i = 0; i < asInputFragments.length; i++) {
if (asInputFragments[i][1].toLowerCase().trim() === sBreakdownType.toLowerCase().trim()) {
//console.log("IN> " + i + " = " + asInputFragments[i][2]);
asBreakdownArray[iBreakdown] = new Array (3);
asBreakdownArray[iBreakdown][0] = asInputFragments[i][2]; // site
asBreakdownArray[iBreakdown][1] = asInputFragments[i][3]; // text
asBreakdownArray[iBreakdown][2] = asInputFragments[i][4]; // number
iBreakdown++;
} else {
//console.log("OUT> " + i + " = " + asInputFragments[i][0]); // items that were left out
}
}
// the text 'patient breakdown' is removed and remainder trimmed
var sBreakdownText = "";
var iPbLocation = sBreakdownType.toLowerCase().indexOf(" patient breakdown");
if (iPbLocation != -1) {
sBreakdownText = sBreakdownType.substring(0,iPbLocation);
//console.log(sBreakdownText + "|");
} else {
sBreakdownText = sBreakdownType;
//console.log(sBreakdownText + "it is");
}
// function where the dataset arrays are created:
var iBreakdown = 1;
var c3xaxis = new Array();
var c3values = new Array();
c3xaxis[0] = 'x';
c3values[0] = sBreakdownText;
for (var i = 0; i < asInputFragments.length; i++) {
if (asInputFragments[i][1].toLowerCase().trim() === sBreakdownType.toLowerCase().trim()) {
//document.write("
OK? " + i + " = " + asInputFragments[i][0]);
c3xaxis[iBreakdown] = asInputFragments[i][3];
c3values[iBreakdown] = asInputFragments[i][4];
iBreakdown++;
} else {
//document.write("
ERROR? " + i + " = " + asInputFragments[i][0]);
}
}
// Trying out some C3
var graph_color = 'darkblue';
//var graph_color = 'hsl(' + Math.floor( 360 * Math.random() ) + ', 85%, 55%)'; // random color
if(!(typeof c3 === 'undefined')){
var chart = c3.generate({
bindto: '#' + sDivName,
size: {
//width: 535,
height: 200
},
data: {
x: 'x',
columns: [
c3xaxis,
c3values
],
type: 'bar',
color: function (color, d) {return graph_color;},
labels: true
},
legend: {
//position: 'inset'
position: 'right'
},
axis: {
x: {
type: 'category',
tick: {
rotate: 25
},
height: 45
},
y: {
label: {
text: 'Number of Patients',
//position: 'outer-middle',
position: 'outer-bottom'
}
}
},
bar: {
width: {
ratio: 0.75 // this makes bar width 75% of length between ticks
}
}
});
}
}
catch(err) {
console.error(err);
}
}; // end of function graph_singlesite_patient_breakdown
/*********************************************************************************
FUNCTION graph_multiplesite_patient_number
Fills in the Div for multiple patient number display
**********************************************************************************/
function graph_multiplesite_patient_number(sDivName,sBreakdownType,asInputFragments) {
try
{
if (sBreakdownType === undefined || sBreakdownType === null) throw ("ERROR - sBreakdownType in function graph_patient_breakdown is null");
var asBreakdownArray = [[]];
var iBreakdown = 0;
// for loop to only pull out data from the breakdown type specified in sBreakdownType variable
// for multiple sites make a data array for each site
for (var i = 0; i < asInputFragments.length; i++) {
if (asInputFragments[i][1].toLowerCase().trim() === sBreakdownType.toLowerCase().trim()) {
//console.log("IN> " + i + " = " + asInputFragments[i][2]);
asBreakdownArray[iBreakdown] = new Array (3);
asBreakdownArray[iBreakdown][0] = asInputFragments[i][2]; // site
asBreakdownArray[iBreakdown][1] = asInputFragments[i][3]; // text
asBreakdownArray[iBreakdown][2] = asInputFragments[i][4]; // number
iBreakdown++;
} else {
//console.log("OUT> " + i + " = " + asInputFragments[i][0]); // items that were left out
}
}
// function where the dataset arrays are created:
var c3values = new Array();
var allCountsZero = true;
for (var i = 0; i < asBreakdownArray.length; i++) {
//console.log("Element " + i + " = " + asBreakdownArray[i][0] + " " + asBreakdownArray[i][2]);
c3values[i] = new Array(2);
c3values[i][0] = asBreakdownArray[i][0].trim() + " " + asBreakdownArray[i][1].trim();
var patCount = Number(asBreakdownArray[i][2]);
c3values[i][1] = patCount;
if(patCount>0)
allCountsZero = false;
}
// C3 that makes pie chart
if(allCountsZero)
{
jQuery(sDivName).html("Not enough patients were returned to render the graph.");
}
else
{
if(!(typeof c3 === 'undefined')){
var chart = c3.generate({
bindto: sDivName,
size: {
//width: 535,
height: 200
},
data: {
columns: c3values,
type: 'pie'
},
pie: {
label: {
format: d3.format('^g,')
}
},
legend: {
position: 'right'
},
axis: {
x: {
type: 'category',
tick: {
rotate: 25
},
height: 45
},
y: {
label: {
text: 'Number of Patients',
position: 'outer-bottom'
}
}
},
bar: {
width: {
ratio: 0.75 // this makes bar width 75% of length between ticks
}
}
});
}
}
}
catch(err) {
console.error(err);
}
}; // end of function graph_multiplesite_patient_number
/*********************************************************************************
FUNCTION graph_multiplesite_patient_breakdown
function where the dataset is displayed
**********************************************************************************/
function graph_multiplesite_patient_breakdown(sDivName,asUniqueBreakdownSites,c3dataarray) {
try {
if(!(typeof c3 === 'undefined')){
var chart = c3.generate({
bindto: sDivName,
size: {
width: 1000,
height: 200
},
data: {
x: 'x',
columns: c3dataarray,
type: 'bar',
groups: [asUniqueBreakdownSites]
},
padding: {
left: 60,
bottom: 40
},
legend: {
position: 'right'
},
axis: {
x: {
type: 'category',
tick: {
rotate: 25
},
height: 45
},
y: {
label: {
text: 'Number of Patients'
// position: 'outer-bottom'
}
}
},
bar: {
width: {
ratio: 0.50 // this makes bar width 75% of length between ticks
}
}
});
}
}
catch(err) {
console.error(err);
}
}; // end of function graph_multiplesite_patient_breakdown
/*********************************************************************************
FUNCTION ie
function to test for the version of internet explorer
usage if ie < 9 then ...
**********************************************************************************/
var ie = (function(){
var undef,
v = 3,
div = document.createElement('div'),
all = div.getElementsByTagName('i');
while (
div.innerHTML = '',
all[0]
);
return v > 4 ? v : undef;
}());
/*********************************************************************************
FUNCTION bisGTIE8
function to specifically test for internet explorer gt 8 or any other browser
which returns "true"
usage if bisGTIE8 then ...
**********************************************************************************/
i2b2.CRC.view.graphs.bisGTIE8 = (function(){
try {
if ( document.addEventListener ) {
//alert("you got IE9 or greater (or a modern browser)");
return true;
}
else {
return false;
}
}
catch (e) {
return false;
}
}());
/*********************************************************************************
FUNCTION sValueOfi2b2Text
Return a INTEGER number that was obfuscated = MAKES IT ZERO
**********************************************************************************/
i2b2.CRC.view.graphs.sValueOfi2b2Text = function(sValue) {
try {
if (sValue === undefined || sValue === null || sValue === "") {
iValue = "undefined in";
return iValue;
}
if (i2b2.PM.model.isObfuscated) {
var asTempArray = [];
if (sValue.toLowerCase().trim() == i2b2.CRC.view.graphs.sObfuscatedText.toLowerCase().trim()) {
iValue = "0";
}
else {
if (sValue.indexOf(i2b2.CRC.view.graphs.sObfuscatedEnding) > 0) {
var asTempArray = sValue.split(i2b2.CRC.view.graphs.sObfuscatedEnding);
iValue = asTempArray[0];
}
else {
iValue = sValue;
}
}
}
else {
iValue = sValue;
}
function isNumber(obj) {return ! isNaN(obj-0) && obj; };
if (!isNumber(iValue)) {
iValue = "undefined #";
return iValue;
}
return iValue;
}
catch(err) {
console.error(err);
}
} // END of function sValueOfi2b2Text
/*********************************************************************************
FUNCTION sValueOfSHRINEText
Return a INTEGER number that was obfuscated = MAKES IT ZERO
**********************************************************************************/
i2b2.CRC.view.graphs.sValueOfSHRINEText = function(sValue) {
try {
if (sValue === undefined || sValue === null || sValue === "") {
iValue = "undefined in";
return iValue;
}
if (true) { // for testing
var asTempArray = [];
if (sValue.toLowerCase().trim() == i2b2.CRC.view.graphs.sObfuscatedSHRINEText.toLowerCase().trim()) {
iValue = "0";
}
else {
if (sValue.indexOf(i2b2.CRC.view.graphs.sObfuscatedSHRINEEnding) > 0) {
//alert(i2b2.CRC.view.graphs.sObfuscatedSHRINEEnding);
var asTempArray = sValue.split(i2b2.CRC.view.graphs.sObfuscatedSHRINEEnding);
//alert(asTempArray);
iValue = asTempArray[0].trim();
}
else {
iValue = sValue;
}
}
}
else {
iValue = sValue;
}
function isNumber(obj) {return ! isNaN(obj-0) && obj; };
if (!isNumber(iValue)) {
iValue = "undefined #";
return iValue;
}
return iValue;
}
catch(err) {
console.error(err);
}
} // END of function sValueOfSHRINEText
/*********************************************************************************
FUNCTION sTexti2b2Value
Return a TEXT number that has obfuscated blurring - FOR DISPLAY ONLY
**********************************************************************************/
i2b2.CRC.view.graphs.sTexti2b2Value = function(iValue) {
try {
if (iValue === undefined || iValue === null || iValue === "") {
sValue = "undefined";
return sValue;
}
function isNumber(obj) {return ! isNaN(obj-0) && obj; };
if (!isNumber(iValue)) {
sValue = "undefined";
return sValue;
}
if (iValue >= i2b2.CRC.view.graphs.iObfuscatedFloorNumber) {
if (i2b2.PM.model.isObfuscated) {
sValue = iValue+i2b2.CRC.view.graphs.sObfuscatedEnding;
} else {
sValue = iValue;
}
} else {
if (i2b2.PM.model.isObfuscated) {
sValue = i2b2.CRC.view.graphs.sObfuscatedText;
} else {
sValue = iValue;
}
}
return sValue;
}
catch(err) {
console.error(err);
}
} // END of function sTexti2b2Value
/*********************************************************************************
FUNCTION clearGraphs
Clear the previously used Div for the graphs
**********************************************************************************/
i2b2.CRC.view.graphs.clearGraphs = function(sDivNameToClear) {
try {
if (sDivNameToClear === undefined || sDivNameToClear === null || sDivNameToClear === "")
throw ("ERROR 291 - i2b2.CRC.view.graphs.sNameOfPreviousDiv in function clearGraphs is null");
//Clear Divs in the original div for the charts
var oClearDiv = document.getElementById(sDivNameToClear);
oClearDiv.innerHTML = "";
}
catch(err) {
console.error(err);
}
} // END of function clearGraphs
/*********************************************************************************
FUNCTION clearDivForIE8
Clear Div and put message in the middle that "The Graphs Cannot Display in IE8"
**********************************************************************************/
i2b2.CRC.view.graphs.aDivForIE8 = function(sDivNameToClear) {
try {
if (sDivNameToClear === undefined || sDivNameToClear === null || sDivNameToClear === "")
throw ("ERROR 291 - i2b2.CRC.view.graphs.sNameOfPreviousDiv in function clearGraphs is null");
// Clear Divs in the original div for the charts
var oClearDiv = document.getElementById(sDivNameToClear);
var child = document.createElement("div");
child.setAttribute("id","IE_Div");
child.setAttribute("width","auto");
oClearDiv.appendChild(child);
// establish style and draw out containing Div
var sDivStyle = "font-family: Verdana, Geneva, sans-serif;"
+ "font-size: 12px;"
+ "text-align: center;"
+ "vertical-align: middle;"
+ "background-color: white;"
+ "width: 100%;";
child.setAttribute("style",sDivStyle);
// establish table in Div and set up its style.
var sTableHtml = '' +
'' +
'  | ' +
'
' +
'' +
'Graph Results is not supported for this version | ' +
'
' +
'' +
'of Internet Explorer. In order to display the graphs in | ' +
'
' +
'' +
'Internet Explorer you will need to use version 11 or higher. | ' +
'
' +
'' +
'  | ' +
'
' +
'
';
child.innerHTML = sTableHtml;
}
catch(err) {
console.error(err);
}
} // END of function clearDivForIE8
console.timeEnd('execute time');
console.groupEnd();
\ No newline at end of file