diff --git a/adapter/adapter-api/src/main/scala/net/shrine/adapter/client/RemoteAdapterClient.scala b/adapter/adapter-api/src/main/scala/net/shrine/adapter/client/RemoteAdapterClient.scala index fe825dee0..5d956e957 100644 --- a/adapter/adapter-api/src/main/scala/net/shrine/adapter/client/RemoteAdapterClient.scala +++ b/adapter/adapter-api/src/main/scala/net/shrine/adapter/client/RemoteAdapterClient.scala @@ -1,133 +1,131 @@ package net.shrine.adapter.client import java.net.{SocketTimeoutException, URL} import org.xml.sax.SAXParseException import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.concurrent.blocking import scala.concurrent.duration.Duration import scala.concurrent.duration.DurationInt import scala.util.control.NonFatal import scala.xml.{NodeSeq, XML} import com.sun.jersey.api.client.ClientHandlerException import net.shrine.client.{HttpResponse, Poster, TimeoutException} import net.shrine.problem.{AbstractProblem, ProblemNotYetEncoded, ProblemSources} import net.shrine.protocol.BroadcastMessage import net.shrine.protocol.ErrorResponse import net.shrine.protocol.NodeId import net.shrine.protocol.Result import scala.util.{Failure, Success, Try} import net.shrine.protocol.ResultOutputType /** * @author clint * @since Nov 15, 2013 * * */ final class RemoteAdapterClient private (nodeId:NodeId,val poster: Poster, val breakdownTypes: Set[ResultOutputType]) extends AdapterClient { import RemoteAdapterClient._ //NB: Overriding apply in the companion object screws up case-class code generation for some reason, so //we add the would-have-been-generated methods here override def toString = s"RemoteAdapterClient($poster)" override def hashCode: Int = 31 * (if(poster == null) 1 else poster.hashCode) override def equals(other: Any): Boolean = other match { case that: RemoteAdapterClient if that != null => poster == that.poster case _ => false } def url:Option[URL] = Some(new URL(poster.url)) //TODO: Revisit this import scala.concurrent.ExecutionContext.Implicits.global override def query(request: BroadcastMessage): Future[Result] = { val requestXml = request.toXml Future { blocking { val response: HttpResponse = poster.post(requestXml.toString()) interpretResponse(response) } }.recover { case e if isTimeout(e) => throw new TimeoutException(s"Invoking adapter at ${poster.url} timed out", e) } } def interpretResponse(response:HttpResponse):Result = { if(response.statusCode <= 400){ val responseXml = response.body import scala.concurrent.duration._ //Should we know the NodeID here? It would let us make a better error response. Try(XML.loadString(responseXml)).flatMap(Result.fromXml(breakdownTypes)) match { case Success(result) => result case Failure(x) => { val errorResponse = x match { case sx: SAXParseException => ErrorResponse(CouldNotParseXmlFromAdapter(poster.url,response.statusCode,responseXml,sx)) case _ => ErrorResponse(ProblemNotYetEncoded(s"Couldn't understand response from adapter at '${poster.url}': $responseXml", x)) } Result(nodeId, 0.milliseconds, errorResponse) } } } else { Result(nodeId,0.milliseconds,ErrorResponse(HttpErrorCodeFromAdapter(poster.url,response.statusCode,response.body))) } } } object RemoteAdapterClient { def apply(nodeId:NodeId,poster: Poster, breakdownTypes: Set[ResultOutputType]): RemoteAdapterClient = { //NB: Replicate URL-munging that used to be performed by JerseyAdapterClient val posterToUse = { if(poster.url.endsWith("requests")) { poster } else { poster.mapUrl(_ + "/requests") } } new RemoteAdapterClient(nodeId,posterToUse, breakdownTypes) } def isTimeout(e: Throwable): Boolean = e match { case e: SocketTimeoutException => true case e: ClientHandlerException => { val cause = e.getCause cause != null && cause.isInstanceOf[SocketTimeoutException] } case _ => false } } case class HttpErrorCodeFromAdapter(url:String,statusCode:Int,responseBody:String) extends AbstractProblem(ProblemSources.Adapter) { override def summary: String = "Hub received a fatal error response" override def description: String = s"Hub received error code $statusCode from the adapter at $url" override def detailsXml:NodeSeq =
{s"Http response body was $responseBody"}
- createAndLog } case class CouldNotParseXmlFromAdapter(url:String,statusCode:Int,responseBody:String,saxx: SAXParseException) extends AbstractProblem(ProblemSources.Adapter) { override def throwable = Some(saxx) override def summary: String = s"Hub could not parse response from adapter" override def description: String = s"Hub could not parse xml from $url due to ${saxx.toString}" override def detailsXml:NodeSeq =
{s"Http response code was $statusCode and the body was $responseBody"} {throwableDetail}
- createAndLog } \ No newline at end of file diff --git a/adapter/adapter-service/src/main/scala/net/shrine/adapter/AbstractReadQueryResultAdapter.scala b/adapter/adapter-service/src/main/scala/net/shrine/adapter/AbstractReadQueryResultAdapter.scala index 5432cda0a..6e948f3e2 100644 --- a/adapter/adapter-service/src/main/scala/net/shrine/adapter/AbstractReadQueryResultAdapter.scala +++ b/adapter/adapter-service/src/main/scala/net/shrine/adapter/AbstractReadQueryResultAdapter.scala @@ -1,301 +1,298 @@ package net.shrine.adapter import java.util.concurrent.Executors import java.util.concurrent.TimeUnit import net.shrine.adapter.audit.AdapterAuditDb import scala.Option.option2Iterable import scala.concurrent.Await import scala.concurrent.ExecutionContext import scala.concurrent.Future import scala.util.Failure import scala.util.Success import scala.util.Try import scala.xml.NodeSeq import net.shrine.adapter.Obfuscator.obfuscateResults import net.shrine.adapter.dao.AdapterDao import net.shrine.adapter.dao.model.Breakdown import net.shrine.adapter.dao.model.ShrineQueryResult import net.shrine.protocol.{AuthenticationInfo, BaseShrineRequest, BroadcastMessage, ErrorResponse, HasQueryResults, HiveCredentials, QueryResult, ReadResultRequest, ReadResultResponse, ResultOutputType, ShrineRequest, ShrineResponse} import net.shrine.protocol.query.QueryDefinition import net.shrine.util.StackTrace import net.shrine.util.Tries.sequence import scala.concurrent.duration.Duration import net.shrine.client.Poster import net.shrine.problem.{AbstractProblem, ProblemSources} /** * @author clint * @since Nov 2, 2012 * */ object AbstractReadQueryResultAdapter { private final case class RawResponseAttempts(countResponseAttempt: Try[ReadResultResponse], breakdownResponseAttempts: Seq[Try[ReadResultResponse]]) private final case class SpecificResponseAttempts[R](responseAttempt: Try[R], breakdownResponseAttempts: Seq[Try[ReadResultResponse]]) } //noinspection RedundantBlock abstract class AbstractReadQueryResultAdapter[Req <: BaseShrineRequest, Rsp <: ShrineResponse with HasQueryResults]( poster: Poster, override val hiveCredentials: HiveCredentials, dao: AdapterDao, doObfuscation: Boolean, getQueryId: Req => Long, getProjectId: Req => String, toResponse: (Long, QueryResult) => Rsp, breakdownTypes: Set[ResultOutputType], collectAdapterAudit:Boolean ) extends WithHiveCredentialsAdapter(hiveCredentials) { //TODO: Make this configurable private val numThreads = math.max(5, Runtime.getRuntime.availableProcessors) //TODO: Use scala.concurrent.ExecutionContext.Implicits.global instead? private lazy val executorService = Executors.newFixedThreadPool(numThreads) private lazy val executionContext = ExecutionContext.fromExecutorService(executorService) override def shutdown() { try { executorService.shutdown() executorService.awaitTermination(5, TimeUnit.SECONDS) } finally { executorService.shutdownNow() super.shutdown() } } import AbstractReadQueryResultAdapter._ override protected[adapter] def processRequest(message: BroadcastMessage): ShrineResponse = { val req = message.request.asInstanceOf[Req] val queryId = getQueryId(req) def findShrineQueryRow = dao.findQueryByNetworkId(queryId) def findShrineQueryResults = dao.findResultsFor(queryId) findShrineQueryRow match { case None => { debug(s"Query $queryId not found in the Shrine DB") ErrorResponse(QueryNotFound(queryId)) } case Some(shrineQueryRow) => { findShrineQueryResults match { case None => { debug(s"Query $queryId found but its results are not available") //TODO: When precisely can this happen? Should we go back to the CRC here? ErrorResponse(QueryResultNotAvailable(queryId)) } case Some(shrineQueryResult) => { if (shrineQueryResult.isDone) { debug(s"Query $queryId is done and already stored, returning stored results") makeResponseFrom(queryId, shrineQueryResult) } else { debug(s"Query $queryId is incomplete, asking CRC for results") val result: ShrineResponse = retrieveQueryResults(queryId, req, shrineQueryResult, message) if (collectAdapterAudit) AdapterAuditDb.db.insertResultSent(queryId,result) result } } } } } } private def makeResponseFrom(queryId: Long, shrineQueryResult: ShrineQueryResult): ShrineResponse = { shrineQueryResult.toQueryResults(doObfuscation).map(toResponse(queryId, _)).getOrElse(ErrorResponse(QueryNotFound(queryId))) } private def retrieveQueryResults(queryId: Long, req: Req, shrineQueryResult: ShrineQueryResult, message: BroadcastMessage): ShrineResponse = { //NB: If the requested query was not finished executing on the i2b2 side when Shrine recorded it, attempt to //retrieve it and all its sub-components (breakdown results, if any) in parallel. Asking for the results in //parallel is quite possibly too clever, but may be faster than asking for them serially. //TODO: Review this. //Make requests for results in parallel val futureResponses = scatter(message.networkAuthn, req, shrineQueryResult) //Gather all the results (block until they're all returned) val SpecificResponseAttempts(countResponseAttempt, breakdownResponseAttempts) = gather(queryId, futureResponses, req.waitTime) countResponseAttempt match { //If we successfully received the parent response (the one with query type PATIENT_COUNT_XML), re-store it along //with any retrieved breakdowns before returning it. case Success(countResponse) => { //NB: Only store the result if needed, that is, if all results are done //TODO: REVIEW THIS storeResultIfNecessary(shrineQueryResult, countResponse, req.authn, queryId, getFailedBreakdownTypes(breakdownResponseAttempts)) countResponse } case Failure(e) => ErrorResponse(CouldNotRetrieveQueryFromCrc(queryId,e)) } } private def scatter(authn: AuthenticationInfo, req: Req, shrineQueryResult: ShrineQueryResult): Future[RawResponseAttempts] = { def makeRequest(localResultId: Long) = ReadResultRequest(hiveCredentials.projectId, req.waitTime, hiveCredentials.toAuthenticationInfo, localResultId.toString) def process(localResultId: Long): ShrineResponse = { delegateResultRetrievingAdapter.process(authn, makeRequest(localResultId)) } implicit val executionContext = this.executionContext import scala.concurrent.blocking def futureBlockingAttempt[T](f: => T): Future[Try[T]] = Future(blocking(Try(f))) val futureCountAttempt: Future[Try[ShrineResponse]] = futureBlockingAttempt { process(shrineQueryResult.count.localId) } val futureBreakdownAttempts = Future.sequence(for { Breakdown(_, localResultId, resultType, data) <- shrineQueryResult.breakdowns } yield futureBlockingAttempt { process(localResultId) }) //Log errors retrieving count futureCountAttempt.collect { case Success(e: ErrorResponse) => error(s"Error requesting count result from the CRC: '$e'") case Failure(e) => error(s"Error requesting count result from the CRC: ", e) } //Log errors retrieving breakdown for { breakdownResponseAttempts <- futureBreakdownAttempts } { breakdownResponseAttempts.collect { case Success(e: ErrorResponse) => error(s"Error requesting breakdown result from the CRC: '$e'") case Failure(e) => error(s"Error requesting breakdown result from the CRC: ", e) } } //"Filter" for non-ErrorResponses val futureNonErrorCountAttempt: Future[Try[ReadResultResponse]] = futureCountAttempt.collect { case Success(resp: ReadResultResponse) => Success(resp) //NB: Need to repackage response here to avoid ugly, obscure, superfluous cast case unexpected => Failure(new Exception(s"Getting count result failed. Response is: '$unexpected'")) } //"Filter" for non-ErrorResponses val futureNonErrorBreakdownResponseAttempts: Future[Seq[Try[ReadResultResponse]]] = for { breakdownResponseAttempts <- futureBreakdownAttempts } yield { breakdownResponseAttempts.collect { case Success(resp: ReadResultResponse) => Try(resp) } } for { countResponseAttempt <- futureNonErrorCountAttempt breakdownResponseAttempts <- futureNonErrorBreakdownResponseAttempts } yield { RawResponseAttempts(countResponseAttempt, breakdownResponseAttempts) } } private def gather(queryId: Long, futureResponses: Future[RawResponseAttempts], waitTime: Duration): SpecificResponseAttempts[Rsp] = { val RawResponseAttempts(countResponseAttempt, breakdownResponseAttempts) = Await.result(futureResponses, waitTime) //Log any failures (countResponseAttempt +: breakdownResponseAttempts).collect { case Failure(e) => e }.foreach(error("Error retrieving result from the CRC: ", _)) //NB: Count response and ALL breakdown responses must be available (not Failures) or else a Failure will be returned val responseAttempt = for { countResponse: ReadResultResponse <- countResponseAttempt countQueryResult = countResponse.metadata breakdownResponses: Seq[ReadResultResponse] <- sequence(breakdownResponseAttempts) } yield { val breakdownsByType = (for { breakdownResponse <- breakdownResponses resultType <- breakdownResponse.metadata.resultType } yield resultType -> breakdownResponse.data).toMap val queryResultWithBreakdowns = countQueryResult.withBreakdowns(breakdownsByType) val queryResultToReturn = if(doObfuscation) Obfuscator.obfuscate(queryResultWithBreakdowns) else queryResultWithBreakdowns toResponse(queryId, queryResultToReturn) } SpecificResponseAttempts(responseAttempt, breakdownResponseAttempts) } private def getFailedBreakdownTypes(attempts: Seq[Try[ReadResultResponse]]): Set[ResultOutputType] = { val successfulBreakdownTypes = attempts.collect { case Success(ReadResultResponse(_, metadata, _)) => metadata.resultType }.flatten breakdownTypes -- successfulBreakdownTypes } private def storeResultIfNecessary(shrineQueryResult: ShrineQueryResult, response: Rsp, authn: AuthenticationInfo, queryId: Long, failedBreakdownTypes: Set[ResultOutputType]) { val responseIsDone = response.results.forall(_.statusType.isDone) if (responseIsDone) { storeResult(shrineQueryResult, response, authn, queryId, failedBreakdownTypes) } } private def storeResult(shrineQueryResult: ShrineQueryResult, response: Rsp, authn: AuthenticationInfo, queryId: Long, failedBreakdownTypes: Set[ResultOutputType]) { val rawResults = response.results val obfuscatedResults = obfuscateResults(doObfuscation)(response.results) for { shrineQuery <- dao.findQueryByNetworkId(queryId) queryResult <- rawResults.headOption obfuscatedQueryResult <- obfuscatedResults.headOption } { val queryDefinition = QueryDefinition(shrineQuery.name, shrineQuery.queryDefinition.expr) dao.inTransaction { dao.deleteQuery(queryId) dao.storeResults(authn, shrineQueryResult.localId, queryId, queryDefinition, rawResults, obfuscatedResults, failedBreakdownTypes.toSeq, queryResult.breakdowns, obfuscatedQueryResult.breakdowns) } } } private type Unmarshaller[R] = Set[ResultOutputType] => NodeSeq => Try[R] private final class DelegateAdapter[Rqst <: ShrineRequest, Rspns <: ShrineResponse](unmarshaller: Unmarshaller[Rspns]) extends CrcAdapter[Rqst, Rspns](poster, hiveCredentials) { def process(authn: AuthenticationInfo, req: Rqst): Rspns = processRequest(BroadcastMessage(authn, req)).asInstanceOf[Rspns] override protected def parseShrineResponse(xml: NodeSeq): ShrineResponse = unmarshaller(breakdownTypes)(xml).get //TODO: Avoid .get call } private lazy val delegateResultRetrievingAdapter = new DelegateAdapter[ReadResultRequest, ReadResultResponse](ReadResultResponse.fromI2b2 _) } case class QueryNotFound(queryId:Long) extends AbstractProblem(ProblemSources.Adapter) { override def summary: String = s"Query not found" override def description:String = s"No query with id $queryId found on ${stamp.host.getHostName}" - createAndLog } case class QueryResultNotAvailable(queryId:Long) extends AbstractProblem(ProblemSources.Adapter) { override def summary: String = s"Query $queryId found but its results are not available yet" override def description:String = s"Query $queryId found but its results are not available yet on ${stamp.host.getHostName}" - createAndLog } case class CouldNotRetrieveQueryFromCrc(queryId:Long,x: Throwable) extends AbstractProblem(ProblemSources.Adapter) { override def summary: String = s"Could not retrieve query $queryId from the CRC" override def description:String = s"Unhandled exception while retrieving query $queryId while retrieving it from the CRC on ${stamp.host.getHostName}" override def throwable = Some(x) - createAndLog } diff --git a/adapter/adapter-service/src/main/scala/net/shrine/adapter/Adapter.scala b/adapter/adapter-service/src/main/scala/net/shrine/adapter/Adapter.scala index 3914fedc6..d6592184c 100644 --- a/adapter/adapter-service/src/main/scala/net/shrine/adapter/Adapter.scala +++ b/adapter/adapter-service/src/main/scala/net/shrine/adapter/Adapter.scala @@ -1,104 +1,100 @@ package net.shrine.adapter import java.sql.SQLException import java.util.Date import net.shrine.adapter.dao.BotDetectedException import net.shrine.log.Loggable import net.shrine.problem.{AbstractProblem, LoggingProblemHandler, Problem, ProblemNotYetEncoded, ProblemSources} import net.shrine.protocol.{AuthenticationInfo, BaseShrineResponse, BroadcastMessage, ErrorResponse, ShrineRequest} import scala.util.control.NonFatal /** * @author Bill Simons * @since 4/8/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 */ abstract class Adapter extends Loggable { //noinspection RedundantBlock final def perform(message: BroadcastMessage): BaseShrineResponse = { def problemToErrorResponse(problem:Problem):ErrorResponse = { LoggingProblemHandler.handleProblem(problem) ErrorResponse(problem) } val shrineResponse = try { processRequest(message) } catch { case e: AdapterLockoutException => problemToErrorResponse(AdapterLockout(message.request.authn,e)) case e: BotDetectedException => problemToErrorResponse(BotDetected(e)) case e @ CrcInvocationException(invokedCrcUrl, request, cause) => problemToErrorResponse(CrcCouldNotBeInvoked(invokedCrcUrl,request,e)) case e: AdapterMappingException => problemToErrorResponse(AdapterMappingProblem(e)) case e: SQLException => problemToErrorResponse(AdapterDatabaseProblem(e)) case NonFatal(e) => { val summary = if(message == null) "Unknown problem in Adapter.perform with null BroadcastMessage" else s"Unexpected exception in Adapter" problemToErrorResponse(ProblemNotYetEncoded(summary,e)) } } shrineResponse } protected[adapter] def processRequest(message: BroadcastMessage): BaseShrineResponse //NOOP, may be overridden by subclasses def shutdown(): Unit = () } case class AdapterLockout(authn:AuthenticationInfo,x:AdapterLockoutException) extends AbstractProblem(ProblemSources.Adapter) { override val throwable = Some(x) override val summary: String = s"User '${authn.domain}:${authn.username}' locked out." override val description:String = s"User '${authn.domain}:${authn.username}' has run too many queries that produce the same result at ${x.url} ." - createAndLog } case class CrcCouldNotBeInvoked(crcUrl:String,request:ShrineRequest,x:CrcInvocationException) extends AbstractProblem(ProblemSources.Adapter) { override val throwable = Some(x) override val summary: String = s"Error communicating with I2B2 CRC." override val description: String = s"Error invoking the CRC at '$crcUrl' with a ${request.getClass.getSimpleName} due to ${throwable.get}." override val detailsXml =

Request is {request} {throwableDetail.getOrElse("")}
- createAndLog } case class AdapterMappingProblem(x:AdapterMappingException) extends AbstractProblem(ProblemSources.Adapter) { override val throwable = Some(x) override val summary: String = "Could not map query term(s)." override val description = s"The Shrine Adapter on ${stamp.host.getHostName} cannot map this query to its local terms." override val detailsXml =
Query Defitiontion is {x.runQueryRequest.queryDefinition} RunQueryRequest is ${x.runQueryRequest.elideAuthenticationInfo} {throwableDetail.getOrElse("")}
- createAndLog } case class AdapterDatabaseProblem(x:SQLException) extends AbstractProblem(ProblemSources.Adapter) { override val throwable = Some(x) override val summary: String = "Problem using the Adapter database." override val description = "The Shrine Adapter encountered a problem using a database." - createAndLog } case class BotDetected(bdx:BotDetectedException) extends AbstractProblem(ProblemSources.Adapter) { override val summary: String = s"A user has run enough queries in a short period of time the adapter suspects a bot." override val description: String = s"${bdx.domain}:${bdx.username} has run ${bdx.detectedCount} queries since ${new Date(bdx.sinceMs)}, more than the limit of ${bdx.limit} allowed in this time frame." } \ No newline at end of file diff --git a/adapter/adapter-service/src/main/scala/net/shrine/adapter/CrcAdapter.scala b/adapter/adapter-service/src/main/scala/net/shrine/adapter/CrcAdapter.scala index 1d6175726..df6ef54f2 100644 --- a/adapter/adapter-service/src/main/scala/net/shrine/adapter/CrcAdapter.scala +++ b/adapter/adapter-service/src/main/scala/net/shrine/adapter/CrcAdapter.scala @@ -1,110 +1,108 @@ package net.shrine.adapter import org.xml.sax.SAXParseException import scala.xml.NodeSeq import scala.xml.XML import net.shrine.protocol.{AuthenticationInfo, BaseShrineRequest, BaseShrineResponse, BroadcastMessage, Credential, ErrorResponse, HiveCredentials, ShrineRequest, ShrineResponse, TranslatableRequest} import net.shrine.util.XmlDateHelper import net.shrine.client.Poster import net.shrine.problem.{AbstractProblem, ProblemSources} import scala.util.Try import scala.util.control.NonFatal /** * @author Bill Simons * @since 4/11/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 */ abstract class CrcAdapter[T <: ShrineRequest, V <: ShrineResponse]( poster: Poster, override protected val hiveCredentials: HiveCredentials) extends WithHiveCredentialsAdapter(hiveCredentials) { protected def parseShrineResponse(nodeSeq: NodeSeq): ShrineResponse private[adapter] def parseShrineErrorResponseWithFallback(xmlResponseFromCrc: String): ShrineResponse = { //NB: See https://open.med.harvard.edu/jira/browse/SHRINE-534 //NB: https://open.med.harvard.edu/jira/browse/SHRINE-745 val shrineResponseAttempt = for { crcXml <- Try(XML.loadString(xmlResponseFromCrc)) shrineResponse <- Try(parseShrineResponse(crcXml)).recover { case NonFatal(e) => info(s"Exception while parsing $crcXml",e) ErrorResponse.fromI2b2(crcXml) } //todo pass the exception to build a proper error response, and log the exception } yield shrineResponse shrineResponseAttempt.recover { case saxx:SAXParseException => ErrorResponse(CannotParseXmlFromCrc(saxx,xmlResponseFromCrc)) case NonFatal(e) => error(s"Error parsing response from CRC: ", e) ErrorResponse(ExceptionWhileLoadingCrcResponse(e,xmlResponseFromCrc)) }.get } //NB: default is a noop; only RunQueryAdapter needs this for now protected[adapter] def translateNetworkToLocal(request: T): T = request protected[adapter] override def processRequest(message: BroadcastMessage): BaseShrineResponse = { val i2b2Response = callCrc(translateRequest(message.request)) parseShrineErrorResponseWithFallback(i2b2Response) } protected def callCrc(request: ShrineRequest): String = { debug(s"Sending Shrine-formatted request to the CRC at '${poster.url}': $request") val crcRequest = request.toI2b2String val crcResponse = XmlDateHelper.time(s"Calling the CRC at '${poster.url}'")(debug(_)) { //Wrap exceptions in a more descriptive form, to enable sending better error messages back to the legacy web client try { poster.post(crcRequest) } catch { case NonFatal(e) => throw CrcInvocationException(poster.url, request, e) } } crcResponse.body } private[adapter] def translateRequest(request: BaseShrineRequest): ShrineRequest = request match { case transReq: TranslatableRequest[T] => //noinspection RedundantBlock { val HiveCredentials(domain, username, password, project) = hiveCredentials val authInfo = AuthenticationInfo(domain, username, Credential(password, isToken = false)) translateNetworkToLocal(transReq.withAuthn(authInfo).withProject(project).asRequest) } case req: ShrineRequest => req case _ => throw new IllegalArgumentException(s"Unexpected request: $request") } } case class CannotParseXmlFromCrc(saxx:SAXParseException,xmlResponseFromCrc: String) extends AbstractProblem(ProblemSources.Adapter) { override val throwable = Some(saxx) override val summary: String = "Could not parse response from CRC." override val description:String = s"${saxx.getMessage} while parsing the response from the CRC." override val detailsXml =

{throwableDetail.getOrElse("")} Response is {xmlResponseFromCrc}
- createAndLog } case class ExceptionWhileLoadingCrcResponse(t:Throwable,xmlResponseFromCrc: String) extends AbstractProblem(ProblemSources.Adapter) { override val throwable = Some(t) override val summary: String = "Unanticipated exception with response from CRC." override val description:String = s"${t.getMessage} while parsing the response from the CRC." override val detailsXml =
{throwableDetail.getOrElse("")} Response is {xmlResponseFromCrc}
- createAndLog } \ 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 4ba5b0c10..3a638f214 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,291 +1,289 @@ 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, doObfuscation: Boolean, runQueriesImmediately: Boolean, breakdownTypes: Set[ResultOutputType], collectAdapterAudit:Boolean, botCountTimeThresholds:Seq[(Long,Duration)] ) 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 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) } } 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 = obfuscateBreakdowns(originalMergedBreakdowns) 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) } } object RunQueryAdapter { private[adapter] def obfuscateBreakdowns(breakdowns: Map[ResultOutputType, I2b2ResultEnvelope]): Map[ResultOutputType, I2b2ResultEnvelope] = { breakdowns.mapValues(_.mapValues(Obfuscator.obfuscate)) } } 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." - createAndLog } 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." - createAndLog } \ No newline at end of file diff --git a/adapter/adapter-service/src/main/scala/net/shrine/adapter/components/QueryDefinitions.scala b/adapter/adapter-service/src/main/scala/net/shrine/adapter/components/QueryDefinitions.scala index b678794d7..9e3069947 100644 --- a/adapter/adapter-service/src/main/scala/net/shrine/adapter/components/QueryDefinitions.scala +++ b/adapter/adapter-service/src/main/scala/net/shrine/adapter/components/QueryDefinitions.scala @@ -1,40 +1,39 @@ package net.shrine.adapter.components import net.shrine.adapter.dao.AdapterDao import net.shrine.problem.{AbstractProblem, ProblemSources} import net.shrine.protocol.ShrineResponse import net.shrine.protocol.ReadQueryDefinitionRequest import net.shrine.protocol.ReadQueryDefinitionResponse import net.shrine.protocol.ErrorResponse import net.shrine.protocol.query.QueryDefinition import net.shrine.protocol.AbstractReadQueryDefinitionRequest /** * @author clint * @since Apr 4, 2013 * * NB: Tested by ReadQueryDefinitionAdapterTest */ final case class QueryDefinitions[Req <: AbstractReadQueryDefinitionRequest](dao: AdapterDao) { def get(request: Req): ShrineResponse = { val resultOption = for { shrineQuery <- dao.findQueryByNetworkId(request.queryId) } yield { ReadQueryDefinitionResponse( shrineQuery.networkId, shrineQuery.name, shrineQuery.username, shrineQuery.dateCreated, //TODO: I2b2 or Shrine format? shrineQuery.queryDefinition.toI2b2String) } resultOption.getOrElse(ErrorResponse(QueryNotInDatabase(request))) } } case class QueryNotInDatabase(request:AbstractReadQueryDefinitionRequest) extends AbstractProblem(ProblemSources.Hub) { override val summary: String = s"Couldn't find query definition." override val description:String = s"The query definition with network id: ${request.queryId} does not exist at this site." - createAndLog } \ No newline at end of file diff --git a/adapter/adapter-service/src/main/scala/net/shrine/adapter/dao/squeryl/SquerylAdapterDao.scala b/adapter/adapter-service/src/main/scala/net/shrine/adapter/dao/squeryl/SquerylAdapterDao.scala index 8764e61bf..67cd0b8d1 100644 --- a/adapter/adapter-service/src/main/scala/net/shrine/adapter/dao/squeryl/SquerylAdapterDao.scala +++ b/adapter/adapter-service/src/main/scala/net/shrine/adapter/dao/squeryl/SquerylAdapterDao.scala @@ -1,490 +1,489 @@ package net.shrine.adapter.dao.squeryl import java.sql.Timestamp import javax.xml.datatype.XMLGregorianCalendar import net.shrine.adapter.dao.{AdapterDao, BotDetectedException} import net.shrine.adapter.dao.model.{ObfuscatedPair, ShrineQuery, ShrineQueryResult} import net.shrine.adapter.dao.model.squeryl.{SquerylBreakdownResultRow, SquerylCountRow, SquerylPrivilegedUser, SquerylQueryResultRow, SquerylShrineError, SquerylShrineQuery} import net.shrine.adapter.dao.squeryl.tables.Tables import net.shrine.dao.DateHelpers import net.shrine.dao.squeryl.{SquerylEntryPoint, SquerylInitializer} import net.shrine.log.Loggable import net.shrine.problem.{AbstractProblem, ProblemSources} import net.shrine.protocol.{AuthenticationInfo, I2b2ResultEnvelope, QueryResult, ResultOutputType} import net.shrine.protocol.query.QueryDefinition import net.shrine.util.XmlDateHelper import org.squeryl.Query import org.squeryl.dsl.{GroupWithMeasures, Measures} import scala.concurrent.duration.Duration import scala.util.Try import scala.xml.NodeSeq /** * @author clint * @since May 22, 2013 */ final class SquerylAdapterDao(initializer: SquerylInitializer, tables: Tables)(implicit breakdownTypes: Set[ResultOutputType]) extends AdapterDao with Loggable { initializer.init() override def inTransaction[T](f: => T): T = SquerylEntryPoint.inTransaction { f } import SquerylEntryPoint._ override def flagQuery(networkQueryId: Long, flagMessage: Option[String]): Unit = mutateFlagField(networkQueryId, newIsFlagged = true, flagMessage) override def unFlagQuery(networkQueryId: Long): Unit = mutateFlagField(networkQueryId, newIsFlagged = false, None) private def mutateFlagField(networkQueryId: Long, newIsFlagged: Boolean, newFlagMessage: Option[String]): Unit = { inTransaction { update(tables.shrineQueries) { queryRow => where(queryRow.networkId === networkQueryId). set(queryRow.isFlagged := newIsFlagged, queryRow.flagMessage := newFlagMessage) } } } override def storeResults( authn: AuthenticationInfo, masterId: String, networkQueryId: Long, queryDefinition: QueryDefinition, rawQueryResults: Seq[QueryResult], obfuscatedQueryResults: Seq[QueryResult], failedBreakdownTypes: Seq[ResultOutputType], mergedBreakdowns: Map[ResultOutputType, I2b2ResultEnvelope], obfuscatedBreakdowns: Map[ResultOutputType, I2b2ResultEnvelope]): Unit = { inTransaction { val insertedQueryId = insertQuery(masterId, networkQueryId, authn, queryDefinition, isFlagged = false, hasBeenRun = true, flagMessage = None) val insertedQueryResultIds = insertQueryResults(insertedQueryId, rawQueryResults) storeCountResults(rawQueryResults, obfuscatedQueryResults, insertedQueryResultIds) storeErrorResults(rawQueryResults, insertedQueryResultIds) storeBreakdownFailures(failedBreakdownTypes.toSet, insertedQueryResultIds) insertBreakdownResults(insertedQueryResultIds, mergedBreakdowns, obfuscatedBreakdowns) } } private[adapter] def storeCountResults(raw: Seq[QueryResult], obfuscated: Seq[QueryResult], insertedIds: Map[ResultOutputType, Seq[Int]]): Unit = { val notErrors = raw.filter(!_.isError) val obfuscatedNotErrors = obfuscated.filter(!_.isError) if(notErrors.size > 1) { warn(s"Got ${notErrors.size} raw (hopefully-)count results; more than 1 is unusual.") } if(obfuscatedNotErrors.size > 1) { warn(s"Got ${obfuscatedNotErrors.size} obfuscated (hopefully-)count results; more than 1 is unusual.") } if(notErrors.size != obfuscatedNotErrors.size) { warn(s"Got ${notErrors.size} raw and ${obfuscatedNotErrors.size} obfuscated (hopefully-)count results; that these numbers are different is unusual.") } import ResultOutputType.PATIENT_COUNT_XML def isCount(qr: QueryResult): Boolean = qr.resultType.contains(PATIENT_COUNT_XML) inTransaction { //NB: Take the count/setSize from the FIRST PATIENT_COUNT_XML QueryResult, //though the same count should be there for all of them, if there are more than one for { Seq(insertedCountQueryResultId) <- insertedIds.get(PATIENT_COUNT_XML) notError <- notErrors.find(isCount) //NB: Find a count result, just to be sure obfuscatedNotError <- obfuscatedNotErrors.find(isCount) //NB: Find a count result, just to be sure } { insertCountResult(insertedCountQueryResultId, notError.setSize, obfuscatedNotError.setSize) } } } private[adapter] def storeErrorResults(results: Seq[QueryResult], insertedIds: Map[ResultOutputType, Seq[Int]]): Unit = { val errors = results.filter(_.isError) val insertedErrorResultIds = insertedIds.getOrElse(ResultOutputType.ERROR,Nil) val insertedIdsToErrors = insertedErrorResultIds zip errors inTransaction { for { (insertedErrorResultId, errorQueryResult) <- insertedIdsToErrors } { val pd = errorQueryResult.problemDigest.get //it's an error so it will have a problem digest insertErrorResult( insertedErrorResultId, errorQueryResult.statusMessage.getOrElse("Unknown failure"), pd.codec, pd.stampText, pd.summary, pd.description, pd.detailsXml ) } } } private[adapter] def storeBreakdownFailures(failedBreakdownTypes: Set[ResultOutputType], insertedIds: Map[ResultOutputType, Seq[Int]]): Unit = { val insertedIdsForFailedBreakdownTypes = insertedIds.filterKeys(failedBreakdownTypes.contains) inTransaction { for { (failedBreakdownType, Seq(resultId)) <- insertedIdsForFailedBreakdownTypes } { //todo propagate backwards to the breakdown failure to create the corect problem object BreakdownFailure extends AbstractProblem(ProblemSources.Adapter) { override val summary: String = "Couldn't retrieve result breakdown" override val description:String = s"Couldn't retrieve result breakdown of type '$failedBreakdownType'" - createAndLog } val pd = BreakdownFailure.toDigest insertErrorResult( resultId, s"Couldn't retrieve breakdown of type '$failedBreakdownType'", pd.codec, pd.stampText, pd.summary, pd.description, pd.detailsXml ) } } } override def findRecentQueries(howMany: Int): Seq[ShrineQuery] = { inTransaction { Queries.queriesForAllUsers.take(howMany).map(_.toShrineQuery).toSeq } } def findAllCounts():Seq[SquerylCountRow] = { inTransaction{ Queries.allCountResults.toSeq } } override def renameQuery(networkQueryId: Long, newName: String) { inTransaction { update(tables.shrineQueries) { queryRow => where(queryRow.networkId === networkQueryId). set(queryRow.name := newName) } } } override def deleteQuery(networkQueryId: Long): Unit = { inTransaction { tables.shrineQueries.deleteWhere(_.networkId === networkQueryId) } } override def deleteQueryResultsFor(networkQueryId: Long): Unit = { inTransaction { val resultIdsForNetworkQueryId = join(tables.shrineQueries, tables.queryResults) { (queryRow, resultRow) => where(queryRow.networkId === networkQueryId). select(resultRow.id). on(queryRow.id === resultRow.queryId) }.toSet tables.queryResults.deleteWhere(_.id in resultIdsForNetworkQueryId) } } override def isUserLockedOut(authn: AuthenticationInfo, defaultThreshold: Int): Boolean = Try { inTransaction { val privilegedUserOption = Queries.privilegedUsers(authn.domain, authn.username).singleOption val threshold:Int = privilegedUserOption.flatMap(_.threshold).getOrElse(defaultThreshold.intValue) val thirtyDaysInThePast: XMLGregorianCalendar = DateHelpers.daysFromNow(-30) val overrideDate: XMLGregorianCalendar = privilegedUserOption.map(_.toPrivilegedUser).flatMap(_.overrideDate).getOrElse(thirtyDaysInThePast) //sorted instead of just finding max val counts: Seq[Long] = Queries.repeatedResults(authn.domain, authn.username, overrideDate).toSeq.sorted //and then grabbing the last, highest value in the sorted sequence val repeatedResultCount: Long = counts.lastOption.getOrElse(0L) val result = repeatedResultCount > threshold debug(s"User ${authn.domain}:${authn.username} locked out? $result") result } }.getOrElse(false) override def checkIfBot(authn:AuthenticationInfo, botTimeThresholds:Seq[(Long,Duration)]): Unit = { val now = System.currentTimeMillis() botTimeThresholds.foreach{countDuration => inTransaction { val sinceMs: Long = now - countDuration._2.toMillis val query: Query[Measures[Long]] = Queries.countQueriesForUserSince(authn.domain, authn.username, sinceMs) val queriesSince = query.headOption.map(_.measures).getOrElse(0L) if (queriesSince > countDuration._1) throw new BotDetectedException(domain = authn.domain, username = authn.username, detectedCount = queriesSince, sinceMs = sinceMs, limit = countDuration._1) }} } override def insertQuery(localMasterId: String, networkId: Long, authn: AuthenticationInfo, queryDefinition: QueryDefinition, isFlagged: Boolean, hasBeenRun: Boolean, flagMessage: Option[String]): Int = { inTransaction { val inserted = tables.shrineQueries.insert(new SquerylShrineQuery( 0, localMasterId, networkId, authn.username, authn.domain, XmlDateHelper.now, isFlagged, flagMessage, hasBeenRun, queryDefinition)) inserted.id } } /** * Insert rows into QueryResults, one for each QueryResult in the passed RunQueryResponse * Inserted rows are 'children' of the passed ShrineQuery (ie, they are the results of the query) */ override def insertQueryResults(parentQueryId: Int, results: Seq[QueryResult]): Map[ResultOutputType, Seq[Int]] = { def execTime(result: QueryResult): Option[Long] = { //TODO: How are locales handled here? Do we care? def toMillis(xmlGc: XMLGregorianCalendar) = xmlGc.toGregorianCalendar.getTimeInMillis for { start <- result.startDate end <- result.endDate } yield toMillis(end) - toMillis(start) } val typeToIdTuples = inTransaction { for { result <- results resultType = result.resultType.getOrElse(ResultOutputType.ERROR) //TODO: under what circumstances can QueryResults NOT have start and end dates set? elapsed = execTime(result) } yield { val lastInsertedQueryResultRow = tables.queryResults.insert(new SquerylQueryResultRow(0, result.resultId, parentQueryId, resultType, result.statusType, elapsed, XmlDateHelper.now)) (resultType, lastInsertedQueryResultRow.id) } } typeToIdTuples.groupBy { case (resultType, _) => resultType }.mapValues(_.map { case (_, count) => count }) } override def insertCountResult(resultId: Int, originalCount: Long, obfuscatedCount: Long) { //NB: Squeryl steers us toward inserting with dummy ids :( inTransaction { tables.countResults.insert(new SquerylCountRow(0, resultId, originalCount, obfuscatedCount, XmlDateHelper.now)) } } override def insertBreakdownResults(parentResultIds: Map[ResultOutputType, Seq[Int]], originalBreakdowns: Map[ResultOutputType, I2b2ResultEnvelope], obfuscatedBreakdowns: Map[ResultOutputType, I2b2ResultEnvelope]) { def merge(original: I2b2ResultEnvelope, obfuscated: I2b2ResultEnvelope): Map[String, ObfuscatedPair] = { Map.empty ++ (for { (key, originalValue) <- original.data obfuscatedValue <- obfuscated.data.get(key) } yield (key, ObfuscatedPair(originalValue, obfuscatedValue))) } inTransaction { for { (resultType, Seq(resultId)) <- parentResultIds if resultType.isBreakdown originalBreakdown <- originalBreakdowns.get(resultType) obfuscatedBreakdown <- obfuscatedBreakdowns.get(resultType) (key, ObfuscatedPair(original, obfuscated)) <- merge(originalBreakdown, obfuscatedBreakdown) } { tables.breakdownResults.insert(SquerylBreakdownResultRow(0, resultId, key, original, obfuscated)) } } } override def insertErrorResult(parentResultId: Int, errorMessage: String, codec:String, stampText:String, summary:String, digestDescription:String,detailsXml:NodeSeq) { //NB: Squeryl steers us toward inserting with dummy ids :( inTransaction { tables.errorResults.insert(SquerylShrineError(0, parentResultId, errorMessage, codec, stampText, summary, digestDescription, detailsXml.toString())) } } override def findQueryByNetworkId(networkQueryId: Long): Option[ShrineQuery] = { inTransaction { Queries.queriesByNetworkId(networkQueryId).headOption.map(_.toShrineQuery) } } override def findQueriesByUserAndDomain(domain: String, username: String, howMany: Int): Seq[ShrineQuery] = { inTransaction { Queries.queriesForUser(username, domain).take(howMany).toSeq.map(_.toShrineQuery) } } override def findQueriesByDomain(domain: String): Seq[ShrineQuery] = { inTransaction { Queries.queriesForDomain(domain).toList.map(_.toShrineQuery) } } override def findResultsFor(networkQueryId: Long): Option[ShrineQueryResult] = { inTransaction { val breakdownRowsByType = Queries.breakdownResults(networkQueryId).toSeq.groupBy { case (outputType, _) => outputType.toQueryResultRow.resultType }.mapValues(_.map { case (_, row) => row.toBreakdownResultRow }) val queryRowOption = Queries.queriesByNetworkId(networkQueryId).headOption.map(_.toShrineQuery) val countRowOption = Queries.countResults(networkQueryId).headOption.map(_.toCountRow) val queryResultRows = Queries.resultsForQuery(networkQueryId).toSeq.map(_.toQueryResultRow) val errorResultRows = Queries.errorResults(networkQueryId).toSeq.map(_.toShrineError) for { queryRow <- queryRowOption countRow <- countRowOption shrineQueryResult <- ShrineQueryResult.fromRows(queryRow, queryResultRows, countRow, breakdownRowsByType, errorResultRows) } yield { shrineQueryResult } } } /** * @author clint * @since Nov 19, 2012 */ object Queries { def privilegedUsers(domain: String, username: String): Query[SquerylPrivilegedUser] = { from(tables.privilegedUsers) { user => where(user.username === username and user.domain === domain).select(user) } } def countQueriesForUserSince(domain:String, username:String, sinceMs:Long): Query[Measures[Long]] = { val since = new Timestamp(sinceMs) from(tables.shrineQueries) { queryRow => where(queryRow.domain === domain and queryRow.username === username and queryRow.dateCreated >= since). compute(count) } } def repeatedResults(domain: String, username: String, overrideDate: XMLGregorianCalendar): Query[Long] = { val counts: Query[GroupWithMeasures[Long, Long]] = join(tables.shrineQueries, tables.queryResults, tables.countResults) { (queryRow, resultRow, countRow) => where(queryRow.username === username and queryRow.domain === domain and (countRow.originalValue <> 0L) and queryRow.dateCreated > DateHelpers.toTimestamp(overrideDate)). groupBy(countRow.originalValue). compute(count(countRow.originalValue)). on(queryRow.id === resultRow.queryId, resultRow.id === countRow.resultId) } //Filter for result counts > 0 from(counts) { cnt => where(cnt.measures gt 0).select(cnt.measures) } } val queriesForAllUsers: Query[SquerylShrineQuery] = { from(tables.shrineQueries) { queryRow => select(queryRow).orderBy(queryRow.dateCreated.desc) } } //TODO: Find a way to parameterize on limit, to avoid building the query every time //TODO: limit def queriesForUser(username: String, domain: String): Query[SquerylShrineQuery] = { from(tables.shrineQueries) { queryRow => where(queryRow.domain === domain and queryRow.username === username). select(queryRow). orderBy(queryRow.dateCreated.desc) } } def queriesForDomain(domain: String): Query[SquerylShrineQuery] = { from(tables.shrineQueries) { queryRow => where(queryRow.domain === domain). select(queryRow). orderBy(queryRow.dateCreated.desc) } } val allCountResults: Query[SquerylCountRow] = { from(tables.countResults) { queryRow => select(queryRow) } } def queriesByNetworkId(networkQueryId: Long): Query[SquerylShrineQuery] = { from(tables.shrineQueries) { queryRow => where(queryRow.networkId === networkQueryId).select(queryRow) } } //TODO: Find out how to compose queries, to re-use queriesByNetworkId def queryNamesByNetworkId(networkQueryId: Long): Query[String] = { from(tables.shrineQueries) { queryRow => where(queryRow.networkId === networkQueryId).select(queryRow.name) } } def resultsForQuery(networkQueryId: Long): Query[SquerylQueryResultRow] = { val resultsForNetworkQueryId = join(tables.shrineQueries, tables.queryResults) { (queryRow, resultRow) => where(queryRow.networkId === networkQueryId). select(resultRow). on(queryRow.id === resultRow.queryId) } from(resultsForNetworkQueryId)(select(_)) } def countResults(networkQueryId: Long): Query[SquerylCountRow] = { join(tables.shrineQueries, tables.queryResults, tables.countResults) { (queryRow, resultRow, countRow) => where(queryRow.networkId === networkQueryId). select(countRow). on(queryRow.id === resultRow.queryId, resultRow.id === countRow.resultId) } } def errorResults(networkQueryId: Long): Query[SquerylShrineError] = { join(tables.shrineQueries, tables.queryResults, tables.errorResults) { (queryRow, resultRow, errorRow) => where(queryRow.networkId === networkQueryId). select(errorRow). on(queryRow.id === resultRow.queryId, resultRow.id === errorRow.resultId) } } //NB: using groupBy here is too much of a pain; do it 'manually' later def breakdownResults(networkQueryId: Long): Query[(SquerylQueryResultRow, SquerylBreakdownResultRow)] = { join(tables.shrineQueries, tables.queryResults, tables.breakdownResults) { (queryRow, resultRow, breakdownRow) => where(queryRow.networkId === networkQueryId). select((resultRow, breakdownRow)). on(queryRow.id === resultRow.queryId, resultRow.id === breakdownRow.resultId) } } } } diff --git a/adapter/adapter-service/src/main/scala/net/shrine/adapter/service/AdapterService.scala b/adapter/adapter-service/src/main/scala/net/shrine/adapter/service/AdapterService.scala index cea546ce7..4156d09c9 100644 --- a/adapter/adapter-service/src/main/scala/net/shrine/adapter/service/AdapterService.scala +++ b/adapter/adapter-service/src/main/scala/net/shrine/adapter/service/AdapterService.scala @@ -1,105 +1,103 @@ package net.shrine.adapter.service import net.shrine.log.Loggable import net.shrine.protocol.{BaseShrineResponse, BroadcastMessage, ErrorResponse, NodeId, RequestType, Result, Signature} import net.shrine.adapter.AdapterMap import net.shrine.crypto.Verifier import net.shrine.problem.{AbstractProblem, ProblemSources} import scala.concurrent.duration.Duration import scala.concurrent.duration._ /** * Heart of the adapter. * * @author clint * @since Nov 14, 2013 */ final class AdapterService( nodeId: NodeId, signatureVerifier: Verifier, maxSignatureAge: Duration, adapterMap: AdapterMap) extends AdapterRequestHandler with Loggable { import AdapterService._ logStartup(adapterMap) override def handleRequest(message: BroadcastMessage): Result = { handleInvalidSignature(message).orElse { for { adapter <- adapterMap.adapterFor(message.request.requestType) } yield time(nodeId) { adapter.perform(message) } }.getOrElse { Result(nodeId, 0.milliseconds, ErrorResponse(UnknownRequestType(message.request.requestType))) } } /** * @return None if the signature is fine, Some(result with an ErrorResponse) if not */ private def handleInvalidSignature(message: BroadcastMessage): Option[Result] = { val (sigIsValid, elapsed) = time(signatureVerifier.verifySig(message, maxSignatureAge)) if(sigIsValid) { None } else { info(s"Incoming message had invalid signature: $message") Some(Result(nodeId, elapsed.milliseconds, ErrorResponse(CouldNotVerifySignature(message)))) } } } object AdapterService extends Loggable { private def logStartup(adapterMap: AdapterMap) { info("Adapter service initialized, will respond to the following queries: ") val sortedByReqType = adapterMap.requestsToAdapters.toSeq.sortBy { case (k, _) => k } sortedByReqType.foreach { case (requestType, adapter) => info(s" $requestType:\t(${adapter.getClass.getSimpleName})") } } private[service] def time[T](f: => T): (T, Long) = { val start = System.currentTimeMillis val result = f val elapsed = System.currentTimeMillis - start (result, elapsed) } private[service] def time(nodeId: NodeId)(f: => BaseShrineResponse): Result = { val (response, elapsed) = time(f) Result(nodeId, elapsed.milliseconds, response) } } case class CouldNotVerifySignature(message: BroadcastMessage) extends AbstractProblem(ProblemSources.Adapter){ val signature: Option[Signature] = message.signature override val summary: String = signature.fold("A message was not signed")(sig => s"The trust relationship with ${sig.signedBy} is not properly configured.") override val description: String = signature.fold(s"The Adapter at ${stamp.host.getHostName} could not properly validate a request because it had no signature.")(sig => s"The Adapter at ${stamp.host.getHostName} could not properly validate the request from ${sig.signedBy}. An incoming message from the hub had an invalid signature.") override val detailsXml = signature.fold(

)( sig =>
Signature is {sig}
) - createAndLog } case class UnknownRequestType(requestType: RequestType) extends AbstractProblem(ProblemSources.Adapter){ override val summary: String = s"Unknown request type $requestType" override val description: String = s"The Adapter at ${stamp.host.getHostName} received a request of type $requestType that it cannot process." - createAndLog } \ No newline at end of file diff --git a/adapter/adapter-service/src/main/sql/mysql.ddl b/adapter/adapter-service/src/main/sql/mysql.ddl index 4ae680a69..4e64fe18d 100644 --- a/adapter/adapter-service/src/main/sql/mysql.ddl +++ b/adapter/adapter-service/src/main/sql/mysql.ddl @@ -1,5 +1,6 @@ create table `queriesReceived` (`shrineNodeId` TEXT NOT NULL,`userName` TEXT NOT NULL,`networkQueryId` BIGINT NOT NULL,`queryName` TEXT NOT NULL,`topicId` TEXT,`topicName` TEXT,`timeQuerySent` BIGINT NOT NULL,`timeReceived` BIGINT NOT NULL); create table `executionsStarted` (`networkQueryId` BIGINT NOT NULL,`queryName` TEXT NOT NULL,`timeExecutionStarted` BIGINT NOT NULL); create table `executionsCompleted` (`networkQueryId` BIGINT NOT NULL,`replyId` BIGINT NOT NULL,`queryName` TEXT NOT NULL,`timeExecutionCompleted` BIGINT NOT NULL); create table `resultsSent` (`networkQueryId` BIGINT NOT NULL,`replyId` BIGINT NOT NULL,`queryName` TEXT NOT NULL,`timeResultsSent` BIGINT NOT NULL); -create table `problems` (`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,`codec` TEXT NOT NULL,`stampText` TEXT NOT NULL,`summary` TEXT NOT NULL,`description` TEXT NOT NULL,`detailsXml` CLOB NOT NULL,`epoch` BIGINT NOT NULL); \ No newline at end of file +create table `problems` (`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,`codec` TEXT NOT NULL,`stampText` TEXT NOT NULL,`summary` TEXT NOT NULL,`description` TEXT NOT NULL,`detailsXml` TEXT NOT NULL,`epoch` BIGINT NOT NULL); +create index `idx_epoch` on `problems` (`epoch`) \ No newline at end of file diff --git a/adapter/adapter-service/src/test/resources/dashboard.conf b/adapter/adapter-service/src/test/resources/dashboard.conf new file mode 100644 index 000000000..e280c18e3 --- /dev/null +++ b/adapter/adapter-service/src/test/resources/dashboard.conf @@ -0,0 +1,5 @@ +shrine { + problem { + problemHandler = "net.shrine.problem.LoggingProblemHandler$" + } +} \ No newline at end of file diff --git a/adapter/adapter-service/src/test/scala/net/shrine/adapter/AdapterTest.scala b/adapter/adapter-service/src/test/scala/net/shrine/adapter/AdapterTest.scala index 47cabba6d..06afa20dd 100644 --- a/adapter/adapter-service/src/test/scala/net/shrine/adapter/AdapterTest.scala +++ b/adapter/adapter-service/src/test/scala/net/shrine/adapter/AdapterTest.scala @@ -1,78 +1,78 @@ package net.shrine.adapter -import net.shrine.problem.{ProblemNotYetEncoded, TurnOffProblemConnector} +import net.shrine.problem.ProblemNotYetEncoded import net.shrine.protocol.query.QueryDefinition import net.shrine.util.ShouldMatchersForJUnit import net.shrine.protocol.{AuthenticationInfo, BaseShrineResponse, BroadcastMessage, Credential, DeleteQueryRequest, DeleteQueryResponse, ErrorResponse, RunQueryRequest} import org.junit.Test /** * @author clint * @since Mar 31, 2014 */ //noinspection UnitMethodIsParameterless -final class AdapterTest extends ShouldMatchersForJUnit with TurnOffProblemConnector { +final class AdapterTest extends ShouldMatchersForJUnit { private final class MockAdapter(toReturn: => BaseShrineResponse) extends Adapter { override protected[adapter] def processRequest(message: BroadcastMessage): BaseShrineResponse = toReturn } import scala.concurrent.duration._ private val lockedOutAuthn = AuthenticationInfo("d", "u", Credential("p", isToken = false)) private val networkAuthn = AuthenticationInfo("nd", "nu", Credential("np", isToken = false)) private val req = DeleteQueryRequest("pid", 1.second, lockedOutAuthn, 12345L) private val resp = DeleteQueryResponse(12345) @Test def testHandlesNonFailureCase: Unit = { val adapter = new MockAdapter(resp) adapter.perform(null) should equal(resp) } @Test def testHandlesLockoutCase: Unit = { doErrorResponseTest(new AdapterLockoutException(lockedOutAuthn,"test.com"),classOf[AdapterLockout]) } @Test def testHandlesCrcFailureCase: Unit = { val url = "http://example.com" doErrorResponseTest(CrcInvocationException(url, req, new Exception),classOf[CrcCouldNotBeInvoked]) } @Test def testHandlesMappingFailureCase: Unit = { val authn = AuthenticationInfo("some-domain", "some-user", Credential("some-password", isToken = false)) val projectId = "projectId" val queryDef = QueryDefinition("test query",None) val runQueryRequest = RunQueryRequest(projectId, 1.millisecond, authn, Some("topicId"), Some("Topic Name"), Set.empty, queryDef) doErrorResponseTest(new AdapterMappingException(runQueryRequest,"blarg", new Exception),classOf[AdapterMappingProblem]) } @Test def testHandlesGeneralFailureCase: Unit = { doErrorResponseTest(new Exception("blerg"),classOf[ProblemNotYetEncoded]) } //noinspection ScalaUnreachableCode,RedundantBlock private def doErrorResponseTest(exception: Throwable,problemClass:Class[_]) = { val adapter = new MockAdapter(throw exception) val response = adapter.perform(BroadcastMessage(networkAuthn, req)) response match { case errorResponse:ErrorResponse => { val pd = errorResponse.problemDigest pd.codec should be (problemClass.getName) } case x => fail(s"$x is not an ErrorResponse") } } } \ No newline at end of file diff --git a/apps/dashboard-app/src/main/js/src/app/diagnostic/diagnostic.model.js b/apps/dashboard-app/src/main/js/src/app/diagnostic/diagnostic.model.js index c156f485a..aca990692 100644 --- a/apps/dashboard-app/src/main/js/src/app/diagnostic/diagnostic.model.js +++ b/apps/dashboard-app/src/main/js/src/app/diagnostic/diagnostic.model.js @@ -1,179 +1,180 @@ (function (){ 'use strict'; // -- angular module -- // angular.module('shrine-tools') .factory('DiagnosticModel', DiagnosticModel) DiagnosticModel.$inject = ['$http', '$q', 'UrlGetter', 'XMLService']; function DiagnosticModel (h, q, urlGetter, xmlService) { var cache = {}; // -- private const -- // var Config = { OptionsEndpoint: 'admin/status/options', ConfigEndpoint: 'admin/status/config', SummaryEndpoint: 'admin/status/summary', ProblemEndpoint: 'admin/status/problems', HappyAllEndpoint: 'admin/happy/all' }; // -- public -- // return { getOptions: getOptions, getConfig: getConfig, getSummary: getSummary, getProblems: getProblems, getHappyAll: getHappyAll, cache: cache }; /** * Method for Handling a failed rest call. * @param failedResult * @returns {*} */ function onFail(failedResult) { return failedResult; } /*** * Method for handling a successful rest call. * @param result * @returns {*} */ function parseJsonResult(result) { return result.data; } /** * * @param result * @returns {*} */ function parseHappyAllResult(result) { var happyObj = {}; if(isQEPError(result.data)) { return $q.reject(result.data); } // -- append all -- // happyObj.all = xmlService.xmlStringToJson(result.data).all; // -- parse and append summary -- // happyObj.summary = parseSummaryFromAll(happyObj.all); return happyObj; } /** * * @param all * @returns {{}} */ function parseSummaryFromAll (all) { // var summary = {}; summary.isHub = !Boolean("" == all.notAHub); summary.shrineVersion = all.versionInfo.shrineVersion; summary.shrineBuildDate = all.versionInfo.buildDate; summary.ontologyVersion = all.versionInfo.ontologyVersion summary.ontologyTerm = ""; //to be implemented in config. summary.adapterOk = all.adapter.result.response.errorResponse === undefined; summary.keystoreOk = true; summary.qepOk = true; // -- verify hub is operating, if necessary -- // if(!summary.isHub) { summary.hubOk = true; } else if(all.net !== undefined) { var hasFailures = Number(all.net.failureCount) > 0; var hasInvalidResults = Number(all.net.validResultCount) != Number(all.net.expectedResultCount); var hasTimeouts = Number(all.net.timeoutCount) > 0; summary.hubOk = !hasFailures && !hasInvalidResults && !hasTimeouts; } return summary; } /** * Get View Options, initial call from diagnostic. * @param verb * @returns {*} */ function getOptions() { var url = urlGetter(Config.OptionsEndpoint) return h.get(url) .then(parseJsonResult, onFail); } /** * Returns the Shrine Configuration object. * @returns {*} */ function getConfig () { var url = urlGetter(Config.ConfigEndpoint) return h.get(url) .then(parseJsonResult, onFail); } /** * * @returns {*} */ function getSummary () { var url = urlGetter(Config.SummaryEndpoint) return h.get(url) .then(parseJsonResult, onFail); } /** * //todo * ProblemEndpoint: 'admin/status/problems', * @returns {*} */ function getProblems(offset, n, epoch) { - var url = urlGetter(Config.ProblemEndpoint+'?offset='+offset+'&n='+n+'&epoch='+epoch); + var epochString = epoch && isFinite(epoch)? '&epoch='+ epoch: ''; + var url = urlGetter(Config.ProblemEndpoint+'?offset='+offset+'&n='+n+epochString); return h.get(url) .then(parseJsonResult, onFail); } /** * * @returns {*} */ function getHappyAll() { var url = urlGetter(Config.HappyAllEndpoint, '.xml') return h.get(url) .then(parseHappyAllResult, onFail); } /** * * @param resultXML * @returns {boolean} */ function isQEPError(resultXML) { var result = resultXML.indexOf('') + resultXML.indexOf(''); return result == -2 } } })(); diff --git a/apps/dashboard-app/src/main/js/src/app/diagnostic/templates/my-pagination-template.html b/apps/dashboard-app/src/main/js/src/app/diagnostic/templates/my-pagination-template.html index d49d34e2d..f42fe7e77 100644 --- a/apps/dashboard-app/src/main/js/src/app/diagnostic/templates/my-pagination-template.html +++ b/apps/dashboard-app/src/main/js/src/app/diagnostic/templates/my-pagination-template.html @@ -1,18 +1,18 @@ - {{vm.floor(vm.probsOffset / vm.probsN) + 1}} / {{vm.floor((vm.probsSize - 1) / vm.probsN) + 1}} + {{vm.floor(vm.probsOffset / vm.probsN) + 1}} / {{vm.floor((vm.probsSize <= 0? 0: vm.probsSize -1) / vm.probsN) + 1}}
\ No newline at end of file diff --git a/apps/dashboard-app/src/main/js/src/app/diagnostic/views/problems.controller.js b/apps/dashboard-app/src/main/js/src/app/diagnostic/views/problems.controller.js index 73b910608..b237bfd1a 100644 --- a/apps/dashboard-app/src/main/js/src/app/diagnostic/views/problems.controller.js +++ b/apps/dashboard-app/src/main/js/src/app/diagnostic/views/problems.controller.js @@ -1,256 +1,253 @@ (function () { 'use strict'; // -- register controller with angular -- // angular.module('shrine-tools') .controller('ProblemsController', ProblemsController) .directive('myPagination', function () { return { restrict: 'A', replace: true, templateUrl: 'src/app/diagnostic/templates/my-pagination-template.html' } }).directive('myPages', function() { function rangeGen(max) { var result = []; for (var i = 0; i < max; i++) { result[i] = i + 1; } return result; } function checkPage(value, activePage, maxPage) { if (!isFinite(value)) { // Anything that's not a number and not an error is fine as a button return !!value; } else if (activePage == 1 || activePage == 2) { return value <= 4; } else if (activePage == maxPage || activePage == maxPage - 1) { return maxPage - value < 4; } else { var diff = value - activePage; return diff >= -2 && diff < 2; } } return { restrict: 'E', templateUrl: 'src/app/diagnostic/templates/paginator-template.html', scope: { maxPage: '=', handleButton: '=', activePage: '=' }, link: function(scope) { scope.rangeGen = rangeGen; scope.checkPage = checkPage; } } }); ProblemsController.$inject = ['$app', '$log', '$sce']; function ProblemsController ($app, $log, $sce) { var vm = this; init(); /** * */ function init () { vm.url = "https://open.med.harvard.edu/wiki/display/SHRINE/"; vm.submitDate = submitDate; vm.newPage = newPage; vm.floor = Math.floor; vm.handleButton = handleButton; vm.parseDetails = function(details) { return $sce.trustAsHtml(parseDetails(details)) }; vm.stringify = function(arg) { return JSON.stringify(arg, null, 2); }; vm.numCheck = function(any) {return isFinite(any)? (any - 1) * vm.probsN: vm.probsOffset}; //todo: Get rid of this and figure out something less hacky vm.formatCodec = function(word) { var index = word.lastIndexOf('.'); var arr = word.trim().split(""); arr[index] = '\n'; return arr.join(""); }; newPage(0, 20) } function handleButton(value) { var page = function(offset) { newPage(offset, vm.probsN) }; switch(value) { case '«': page(0); break; case '‹': page(vm.probsOffset - vm.probsN); break; case '›': page(vm.probsOffset + vm.probsN); break; case '»': page(vm.probsSize); break; default: page((value - 1) * vm.probsN); } } function floorMod(num1, num2) { if (!(num1 && num2)) { // can't mod without real numbers return num1; } else { var n1 = Math.floor(num1); var n2 = Math.floor(num2); return n1 - (n1 % n2); } } function submitDate(dateString) { if (checkDate(dateString)) { var epoch = new Date(dateString).getTime() + 86400000; // + a day vm.showDateError = false; newPage(vm.probsOffset, vm.probsN, epoch); } else { vm.showDateError = true; } } function checkDate(dateString) { try { // Using a try catch here since there are a lot of errors that can happen // that I don't want to deal with var split = dateString.split("-"); var month = parseInt(split[1]); var day = parseInt(split[2]); var year = parseInt(split[0]); return split.length == 3 && month <= 12 && month >= 1 && year >= 0 && validDay(day, month, year); } catch (err) { return false; } } function validDay(day, month, year) { var thirtyOne = [1, 3, 5, 7, 8, 10, 12]; var thirty = [4, 6, 9, 11]; if (contains(thirtyOne, month)) { return day <= 31 && day >= 1; } else if (contains(thirty, month)) { return day <= 30 && day >= 1; } else if (month == 2 && year % 4 == 0 && (!(year % 100 == 0) || year % 400 == 0)) { // Leap year is every year that is divisible by 4. If it's also divisible by 100, then it's only // A leap year if it is also divisible by 400. return day <= 29 && day >= 1; } else { return day <= 28 && day >= 1; } } function contains(arr, elem) { for (var i = 0; i < arr.length; i++) { if (arr[i] === elem) { return true; } } return false; } function newPage(offset, n, epoch) { - if (!(epoch && isFinite(epoch))) { - epoch = -1; - } if (!(n && isFinite(n))) { n = 20; } if (!(offset && isFinite(offset))) { offset = 0; } var clamp = function(num1) { if (!vm.probsSize) { // Can't clamp, since probsSize isn't set yet. return num1; } else { return Math.max(0, Math.min(vm.probsSize - 1, num1)); } }; var num = floorMod(clamp(offset), vm.probsN); $app.model.getProblems(num, n, epoch) .then(setProblems) } function setProblems(probs) { vm.problems = probs.problems; vm.probsSize = probs.size; vm.probsOffset = probs.offset; vm.probsN = probs.n; } function parseDetails(detailsObject) { var detailsTag = '

details

'; var detailsField = detailsObject['details']; if (detailsField === '') { return '

No details associated with this problem

' } else if (typeof(detailsField) === 'string') { return detailsTag + '

'+sanitizeString(detailsField)+'

'; } else if (typeof(detailsField) === 'object' && 'exception' in detailsField) { return detailsTag + parseException(detailsField['exception']); } else { return detailsTag + '
'+sanitizeString(JSON.stringify(detailsField))+'
' } } function parseException(exceptionObject) { - var exceptionTag = '

sanitizeString(exception)

'; + var exceptionTag = '

exception

'; var nameTag = '
'+sanitizeString(exceptionObject['name'])+'
'; var messageTag = '

'+sanitizeString(exceptionObject['message'])+'

'; var stackTrace = exceptionObject['stacktrace']; return exceptionTag + nameTag + messageTag + parseStackTrace(stackTrace); } function parseStackTrace(stackTraceObject) { if ('exception' in stackTraceObject) { return '

'+sanitizeString(stackTraceObject['line'])+'

' + parseException(stackTraceObject['exception']); } else { return '

stack trace

' + parseLines(stackTraceObject['line']); } } function parseLines(lineArray) { var result = '

'; for (var i =0; i < lineArray.length; i++) { result += sanitizeString(lineArray[i]) + '
'; } result += '

'; return result; } function sanitizeString(str) { var chars = str.split(''); var escapes = { '<': '<', '>': '>', '&': '&', '"': '"', "'": ''', ' ': ' ', '!': '!', '@': '@', '$': '$', '%': '%', '(': '(', ')': ')', '=': '=', '+': '+', '{': '{', '}': '}', '[': '[', ']': ']' }; for (var i = 0; i < chars.length; i++) { var c = chars[i]; if (c in escapes) { chars[i] = escapes[c] } } return chars.join(''); } } })(); diff --git a/apps/dashboard-app/src/main/resources/reference.conf b/apps/dashboard-app/src/main/resources/reference.conf index b1dd0f233..f0740198c 100644 --- a/apps/dashboard-app/src/main/resources/reference.conf +++ b/apps/dashboard-app/src/main/resources/reference.conf @@ -1,82 +1,85 @@ shrine { + problem { + problemHandler = "net.shrine.problem.DatabaseProblemHandler$" + } dashboard { gruntWatch = false //false for production, true for mvn tomcat7:run . Allows the client javascript and html files to be loaded via gruntWatch . happyBaseUrl = "https://localhost:6443/shrine/rest/happy" statusBaseUrl = "https://localhost:6443/shrine/rest/internalstatus" remoteDashboard { protocol = "https://" port = ":6443" pathPrefix = "shrine-dashboard/fromDashboard" } database { dataSourceFrom = "JNDI" //Can be JNDI or testDataSource . Use testDataSource for tests, JNDI everywhere else jndiDataSourceName = "java:comp/env/jdbc/adapterAuditDB" //or leave out for tests slickProfileClassName = "slick.driver.MySQLDriver$" // Can be // slick.driver.H2Driver$ // slick.driver.MySQLDriver$ // slick.driver.PostgresDriver$ // slick.driver.SQLServerDriver$ // slick.driver.JdbcDriver$ // freeslick.OracleProfile$ // freeslick.MSSQLServerProfile$ // // (Yes, with the $ on the end) // For testing without JNDI // testDataSource { //typical test settings for unit tests //driverClassName = "org.h2.Driver" //url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" //H2 embedded in-memory for unit tests //url = "jdbc:h2:~/stewardTest.h2" //H2 embedded on disk at ~/test // } createTablesOnStart = false //for testing with H2 in memory, when not running unit tests. Set to false normally } } pmEndpoint { url = "http://changeme.com/i2b2/services/PMService/getServices" //"http://services.i2b2.org/i2b2/services/PMService/getServices" acceptAllCerts = true timeout { seconds = 10 } } authenticate { realm = "SHRINE Steward API" usersource { type = "PmUserSource" //Must be ConfigUserSource (for isolated testing) or PmUserSource (for everything else) domain = "set shrine.authenticate.usersource.domain to the PM authentication domain in dashboard.conf" //"i2b2demo" } } // If the pmEndpoint acceptAllCerts = false then you need to supply a keystore // Or if you would like dashboard-to-dashboard comms to work. // keystore { // file = "shrine.keystore" // password = "chiptesting" // privateKeyAlias = "test-cert" // keyStoreType = "JKS" // caCertAliases = [carra ca] // } } //todo typesafe config precedence seems to do the right thing, but I haven't found the rules that say this reference.conf should override others akka { loglevel = INFO // log-config-on-start = on loggers = ["akka.event.slf4j.Slf4jLogger"] // logging-filter = "akka.event.slf4j.Slf4jLoggingFilter" // Toggles whether the threads created by this ActorSystem should be daemons or not daemonic = on } spray.servlet { boot-class = "net.shrine.dashboard.Boot" request-timeout = 30s } diff --git a/apps/dashboard-app/src/main/scala/net/shrine/dashboard/Boot.scala b/apps/dashboard-app/src/main/scala/net/shrine/dashboard/Boot.scala index 0f776cf44..224da53c7 100644 --- a/apps/dashboard-app/src/main/scala/net/shrine/dashboard/Boot.scala +++ b/apps/dashboard-app/src/main/scala/net/shrine/dashboard/Boot.scala @@ -1,17 +1,20 @@ package net.shrine.dashboard -import akka.actor.{Props, ActorSystem} +import akka.actor.{ActorSystem, Props} +import net.shrine.problem.Problems import spray.servlet.WebBoot // this class is instantiated by the servlet initializer // it needs to have a default constructor and implement // the spray.servlet.WebBoot trait class Boot extends WebBoot { + val warmUp = Problems.DatabaseConnector.warmup + // we need an ActorSystem to host our application in val system = ActorSystem("DashboardActors",DashboardConfigSource.config) // the service actor replies to incoming HttpRequests val serviceActor = system.actorOf(Props[DashboardServiceActor]) } \ No newline at end of file diff --git a/apps/dashboard-app/src/main/scala/net/shrine/dashboard/DashboardService.scala b/apps/dashboard-app/src/main/scala/net/shrine/dashboard/DashboardService.scala index b74089306..6d8c7a51c 100644 --- a/apps/dashboard-app/src/main/scala/net/shrine/dashboard/DashboardService.scala +++ b/apps/dashboard-app/src/main/scala/net/shrine/dashboard/DashboardService.scala @@ -1,479 +1,475 @@ package net.shrine.dashboard import akka.actor.Actor import akka.event.Logging import net.shrine.authentication.UserAuthenticator import net.shrine.authorization.steward.OutboundUser import net.shrine.dashboard.jwtauth.ShrineJwtAuthenticator import net.shrine.i2b2.protocol.pm.User import net.shrine.status.protocol.{Config => StatusProtocolConfig} import net.shrine.dashboard.httpclient.HttpClientDirectives.{forwardUnmatchedPath, requestUriThenRoute} import net.shrine.log.Loggable import net.shrine.problem.{ProblemDigest, Problems} import net.shrine.serialization.NodeSeqSerializer import shapeless.HNil import spray.http.{HttpRequest, HttpResponse, StatusCodes, Uri} import spray.httpx.Json4sSupport import spray.routing.directives.LogEntry import spray.routing._ import org.json4s.{DefaultFormats, Formats} import org.json4s.native.JsonMethods.{parse => json4sParse} import scala.collection.immutable.Iterable import scala.concurrent.duration.{Duration, FiniteDuration, SECONDS} import scala.concurrent.ExecutionContext.Implicits.global /** * Mixes the DashboardService trait with an Akka Actor to provide the actual service. */ class DashboardServiceActor extends Actor with DashboardService { // the HttpService trait defines only one abstract member, which // connects the services environment to the enclosing actor or test def actorRefFactory = context // this actor only runs our route, but you could add // other things here, like request stream processing // or timeout handling def receive = runRoute(route) } /** * A web service that provides the Dashboard endpoints. It is a trait to support testing independent of Akka. */ trait DashboardService extends HttpService with Json4sSupport with Loggable { implicit def json4sFormats: Formats = DefaultFormats val userAuthenticator = UserAuthenticator(DashboardConfigSource.config) //don't need to do anything special for unauthorized users, but they do need access to a static form. lazy val route:Route = gruntWatchCorsSupport{ redirectToIndex ~ staticResources ~ makeTrouble ~ about ~ authenticatedInBrowser ~ authenticatedDashboard } /** logs the request method, uri and response at info level */ def logEntryForRequestResponse(req: HttpRequest): Any => Option[LogEntry] = { case res: HttpResponse => Some(LogEntry(s"\n Request: $req\n Response: $res", Logging.InfoLevel)) case _ => None // other kind of responses } /** logs just the request method, uri and response status at info level */ def logEntryForRequest(req: HttpRequest): Any => Option[LogEntry] = { case res: HttpResponse => Some(LogEntry(s"\n Request: $req\n Response status: ${res.status}", Logging.InfoLevel)) case _ => None // other kind of responses } def authenticatedInBrowser: Route = pathPrefixTest("user"|"admin"|"toDashboard") { logRequestResponse(logEntryForRequestResponse _) { //logging is controlled by Akka's config, slf4j, and log4j config reportIfFailedToAuthenticate { authenticate(userAuthenticator.basicUserAuthenticator) { user => pathPrefix("user") { userRoute(user) } ~ - pathPrefix("admin") { - adminRoute(user) - } ~ - pathPrefix("toDashboard") { - toDashboardRoute(user) - } + pathPrefix("admin") { + adminRoute(user) + } ~ + pathPrefix("toDashboard") { + toDashboardRoute(user) + } } } } } val reportIfFailedToAuthenticate = routeRouteResponse { case Rejected(List(AuthenticationFailedRejection(_,_))) => complete("AuthenticationFailed") } def authenticatedDashboard:Route = pathPrefix("fromDashboard") { logRequestResponse(logEntryForRequestResponse _) { //logging is controlled by Akka's config, slf4j, and log4j config get { //all remote dashboard calls are gets. authenticate(ShrineJwtAuthenticator.authenticate) { user => adminRoute(user) } } } } def makeTrouble = pathPrefix("makeTrouble") { complete(throw new IllegalStateException("fake trouble")) } lazy val redirectToIndex = pathEnd { redirect("shrine-dashboard/client/index.html", StatusCodes.PermanentRedirect) //todo pick up "shrine-dashboard" programatically - } ~ + } ~ ( path("index.html") | pathSingleSlash) { redirect("client/index.html", StatusCodes.PermanentRedirect) - } + } lazy val staticResources = pathPrefix("client") { pathEnd { redirect("client/index.html", StatusCodes.PermanentRedirect) } ~ pathSingleSlash { redirect("index.html", StatusCodes.PermanentRedirect) } ~ { getFromResourceDirectory("client") } } lazy val about = pathPrefix("about") { complete("Nothing here yet") //todo } def userRoute(user:User):Route = get { pathPrefix("whoami") { complete(OutboundUser.createFromUser(user)) } } //todo check that this an admin. def adminRoute(user:User):Route = get { pathPrefix("happy") { val happyBaseUrl: String = DashboardConfigSource.config.getString("shrine.dashboard.happyBaseUrl") forwardUnmatchedPath(happyBaseUrl) } ~ - pathPrefix("messWithHappyVersion") { //todo is this used? + pathPrefix("messWithHappyVersion") { //todo is this used? val happyBaseUrl: String = DashboardConfigSource.config.getString("shrine.dashboard.happyBaseUrl") - def pullClasspathFromConfig(httpResponse:HttpResponse,uri:Uri):Route = { - ctx => { - val result = httpResponse.entity.asString - ctx.complete(s"Got '$result' from $uri") + def pullClasspathFromConfig(httpResponse:HttpResponse,uri:Uri):Route = { + ctx => { + val result = httpResponse.entity.asString + ctx.complete(s"Got '$result' from $uri") + } } - } - requestUriThenRoute(happyBaseUrl+"/version",pullClasspathFromConfig) - } ~ - pathPrefix("ping") {complete("pong")}~ - pathPrefix("status"){statusRoute(user)} + requestUriThenRoute(happyBaseUrl+"/version",pullClasspathFromConfig) + } ~ + pathPrefix("ping") {complete("pong")}~ + pathPrefix("status"){statusRoute(user)} } //Manually test this by running a curl command //curl -k -w "\n%{response_code}\n" -u dave:kablam "https://shrine-dev1.catalyst:6443/shrine-dashboard/toDashboard/shrine-dev2.catalyst/shrine-dashboard/fromDashboard/ping" /** * Forward a request from this dashboard to a remote dashboard */ def toDashboardRoute(user:User):Route = get { pathPrefix(Segment) { dnsName => val remoteDashboardProtocol = DashboardConfigSource.config.getString("shrine.dashboard.remoteDashboard.protocol") val remoteDashboardPort = DashboardConfigSource.config.getString("shrine.dashboard.remoteDashboard.port") val remoteDashboardPathPrefix = DashboardConfigSource.config.getString("shrine.dashboard.remoteDashboard.pathPrefix") val baseUrl = s"$remoteDashboardProtocol$dnsName$remoteDashboardPort/$remoteDashboardPathPrefix" forwardUnmatchedPath(baseUrl,Some(ShrineJwtAuthenticator.createOAuthCredentials(user))) } } def statusRoute(user:User):Route = get { pathPrefix("config"){getConfig}~ - pathPrefix("classpath"){getClasspath}~ - pathPrefix("options"){getOptionalParts}~ //todo rename path to optionalParts - pathPrefix("summary"){getSummary}~ - pathPrefix("problems"){getProblems} + pathPrefix("classpath"){getClasspath}~ + pathPrefix("options"){getOptionalParts}~ //todo rename path to optionalParts + pathPrefix("summary"){getSummary}~ + pathPrefix("problems"){getProblems} } val statusBaseUrl = DashboardConfigSource.config.getString("shrine.dashboard.statusBaseUrl") lazy val getConfig:Route = { def completeConfigRoute(httpResponse:HttpResponse,uri:Uri):Route = { ctx => { val config = ParsedConfig(httpResponse.entity.asString) ctx.complete( ShrineConfig(config) ) } } requestUriThenRoute(statusBaseUrl + "/config", completeConfigRoute) } lazy val getClasspath:Route = { def pullClasspathFromConfig(httpResponse:HttpResponse,uri:Uri):Route = { ctx => { val result = httpResponse.entity.asString val shrineConfig = ShrineConfig(ParsedConfig(result)) ctx.complete(shrineConfig) } } requestUriThenRoute(statusBaseUrl + "/config",pullClasspathFromConfig) } lazy val getOptionalParts:Route = { requestUriThenRoute(statusBaseUrl + "/optionalParts") } lazy val getSummary:Route = { requestUriThenRoute(statusBaseUrl + "/summary") } // table based view, can see N problems at a time. Front end sends how many problems that they want // to skip, and it will take N the 'nearest N' ie with n = 20, 0-19 -> 20, 20-39 -> 20-40 lazy val getProblems:Route = { def floorMod(x: Int, y: Int) = { x - (x % y) } val db = Problems.DatabaseConnector - parameters("offset" ? 0, "n" ? 20, "epoch" ? -1l) { (offsetPreMod: Int, nPreMax: Int, epoch: Long) => + parameters("offset".as[Int].?(0), "n".as[Int].?(20), "epoch".as[Long].?) {(offsetPreMod, nPreMax, epoch: Option[Long]) => { val n = Math.max(0, nPreMax) val moddedOffset = floorMod(Math.max(0, offsetPreMod), n) - // Constructing the query with comprehension means we only use one database connection. - // TODO: review with Dave to see if it's too clever - val query = - if (epoch == -1l) - for { - result <- db.IO.sizeAndProblemDigest(n, moddedOffset) - } yield (result._2, floorMod(Math.max(0, moddedOffset), n), n, result._1) - else - for { - dateOffset <- db.IO.findIndexOfDate(epoch) - moddedOffset = floorMod(dateOffset, n) - result <- db.IO.sizeAndProblemDigest(n, moddedOffset) - } yield (result._2, moddedOffset, n, result._1) - - val response: ProblemResponse = ProblemResponse.tupled(db.runBlocking(query)(new FiniteDuration(15, SECONDS))) + val query = for { + result <- db.IO.sizeAndProblemDigest(n, moddedOffset) + } yield (result._2, floorMod(Math.max(0, moddedOffset), n), n, result._1) + + val query2 = for { + dateOffset <- db.IO.findIndexOfDate(epoch.getOrElse(0)) + moddedOffset = floorMod(dateOffset, n) + result <- db.IO.sizeAndProblemDigest(n, moddedOffset) + } yield (result._2, moddedOffset, n, result._1) + + val response: ProblemResponse = ProblemResponse.tupled(db.runBlocking(if (epoch.isEmpty) query else query2)) implicit val formats = response.json4sMarshaller complete(response) - } + }} } } case class ProblemResponse(size: Int, offset: Int, n: Int, problems: Seq[ProblemDigest]) extends Json4sSupport { override implicit def json4sFormats: Formats = DefaultFormats + new NodeSeqSerializer } /** - * Centralized parsing logic for map of shrine.conf - * the class literal `T.class` in Java. - */ + * Centralized parsing logic for map of shrine.conf + * the class literal `T.class` in Java. + */ //todo most of this info should come directly from the status service in Shrine, not from reading the config case class ParsedConfig(configMap:Map[String, String]){ private val trueVal = "true" private val rootKey = "shrine" def isHub = getOrElse(rootKey + ".hub.create", "") .toLowerCase == trueVal def stewardEnabled = configMap.keySet .contains(rootKey + ".queryEntryPoint.shrineSteward") def shouldQuerySelf = getOrElse(rootKey + ".hub.shouldQuerySelf", "") .toLowerCase == trueVal def fromJsonString(jsonString:String): String = jsonString.split("\"").mkString("") def get(key:String): Option[String] = configMap.get(key).map(fromJsonString) def getOrElse(key:String, elseVal:String = ""): String = get(key).getOrElse(elseVal) } object ParsedConfig { def apply(jsonString:String):ParsedConfig = { implicit def json4sFormats: Formats = DefaultFormats ParsedConfig(json4sParse(jsonString).extract[StatusProtocolConfig].keyValues.filterKeys(_.toLowerCase.startsWith("shrine"))) } } case class DownstreamNode(name:String, url:String) object DownstreamNode { def create(configMap:Map[String,String]):Iterable[DownstreamNode] = { for ((k, v) <- configMap.filterKeys(_.toLowerCase.startsWith ("shrine.hub.downstreamnodes"))) yield DownstreamNode(k.split('.').last,v.split("\"").mkString("")) } } //todo replace with the actual config, scrubbed of passwords case class ShrineConfig(isHub:Boolean, hub:Hub, pmEndpoint:Endpoint, ontEndpoint:Endpoint, hiveCredentials: HiveCredentials, adapter: Adapter, queryEntryPoint:QEP, networkStatusQuery:String ) object ShrineConfig{ def apply(config:ParsedConfig):ShrineConfig = { val hub = Hub(config) val isHub = config.isHub val pmEndpoint = Endpoint("pm",config) val ontEndpoint = Endpoint("ont",config) val hiveCredentials = HiveCredentials(config) val adapter = Adapter(config) val queryEntryPoint = QEP(config) val networkStatusQuery = config.configMap("shrine.networkStatusQuery") ShrineConfig(isHub, hub, pmEndpoint, ontEndpoint, hiveCredentials, adapter, queryEntryPoint, networkStatusQuery) } } case class Endpoint(acceptAllCerts:Boolean, url:String, timeoutSeconds:Int) object Endpoint{ def apply(endpointType:String,parsedConfig:ParsedConfig):Endpoint = { val prefix = "shrine." + endpointType.toLowerCase + "Endpoint." val acceptAllCerts = parsedConfig.configMap.getOrElse(prefix + "acceptAllCerts", "") == "true" val url = parsedConfig.configMap.getOrElse(prefix + "url","") val timeoutSeconds = parsedConfig.configMap.getOrElse(prefix + "timeout.seconds", "0").toInt Endpoint(acceptAllCerts, url, timeoutSeconds) } } case class HiveCredentials(domain:String, username:String, password:String, crcProjectId:String, ontProjectId:String) object HiveCredentials{ def apply(parsedConfig:ParsedConfig):HiveCredentials = { val key = "shrine.hiveCredentials." val domain = parsedConfig.configMap.getOrElse(key + "domain","") val username = parsedConfig.configMap.getOrElse(key + "username","") val password = "REDACTED" val crcProjectId = parsedConfig.configMap.getOrElse(key + "crcProjectId","") val ontProjectId = parsedConfig.configMap.getOrElse(key + "ontProjectId","") HiveCredentials(domain, username, password, crcProjectId, ontProjectId) } } // -- hub only -- // //todo delete when the Dashboard front end can use the status service's hub method case class Hub(shouldQuerySelf:Boolean, create:Boolean, downstreamNodes:Iterable[DownstreamNode]) object Hub{ def apply(parsedConfig:ParsedConfig):Hub = { val shouldQuerySelf = parsedConfig.shouldQuerySelf val create = parsedConfig.isHub val downstreamNodes = DownstreamNode.create(parsedConfig.configMap) Hub(shouldQuerySelf, create, downstreamNodes) } } // -- adapter info -- // case class Adapter(crcEndpointUrl:String, setSizeObfuscation:Boolean, adapterLockoutAttemptsThreshold:Int, adapterMappingsFilename:String) object Adapter{ def apply(parsedConfig:ParsedConfig):Adapter = { val key = "shrine.adapter." val crcEndpointUrl = parsedConfig.configMap.getOrElse(key + "crcEndpoint.url","") val setSizeObfuscation = parsedConfig.configMap.getOrElse(key + "setSizeObfuscation","").toLowerCase == "true" val adapterLockoutAttemptsThreshold = parsedConfig.configMap.getOrElse(key + "adapterLockoutAttemptsThreshold", "0").toInt val adapterMappingsFileName = parsedConfig.configMap.getOrElse(key + "adapterMappingsFileName","") Adapter(crcEndpointUrl, setSizeObfuscation, adapterLockoutAttemptsThreshold, adapterMappingsFileName) } } case class Steward(qepUserName:String, stewardBaseUrl:String) object Steward { def apply (parsedConfig:ParsedConfig):Steward = { val key = "shrine.queryEntryPoint.shrineSteward." val qepUserName = parsedConfig.configMap.getOrElse(key + "qepUserName","") val stewardBaseUrl = parsedConfig.configMap.getOrElse(key + "stewardBaseUrl","") Steward(qepUserName, stewardBaseUrl) } } // -- if needed -- // case class TimeoutInfo (timeUnit:String, description:String) case class DatabaseInfo(createTablesOnStart:Boolean, dataSourceFrom:String, jndiDataSourceName:String, slickProfileClassName:String) case class Audit(database:DatabaseInfo, collectQepAudit:Boolean) object Audit{ def apply(parsedConfig:ParsedConfig):Audit = { val key = "shrine.queryEntryPoint.audit." val createTablesOnStart = parsedConfig.configMap.getOrElse(key + "database.createTablesOnStart","") == "true" val dataSourceFrom = parsedConfig.configMap.getOrElse(key + "database.dataSourceFrom","") val jndiDataSourceName = parsedConfig.configMap.getOrElse(key + "database.jndiDataSourceName","") val slickProfileClassName = parsedConfig.configMap.getOrElse(key + "database.slickProfileClassName","") val collectQepAudit = parsedConfig.configMap.getOrElse(key + "collectQepAudit","") == "true" val database = DatabaseInfo(createTablesOnStart, dataSourceFrom, jndiDataSourceName, slickProfileClassName) Audit(database, collectQepAudit) } } case class QEP( - maxQueryWaitTimeMinutes:Int, - create:Boolean, - attachSigningCert:Boolean, - authorizationType:String, - includeAggregateResults:Boolean, - authenticationType:String, - audit:Audit, - shrineSteward:Steward, - broadcasterServiceEndpointUrl:Option[String] -) + maxQueryWaitTimeMinutes:Int, + create:Boolean, + attachSigningCert:Boolean, + authorizationType:String, + includeAggregateResults:Boolean, + authenticationType:String, + audit:Audit, + shrineSteward:Steward, + broadcasterServiceEndpointUrl:Option[String] + ) object QEP{ val key = "shrine.queryEntryPoint." def apply(parsedConfig:ParsedConfig):QEP = QEP( maxQueryWaitTimeMinutes = parsedConfig.configMap.getOrElse(key + "maxQueryWaitTime.minutes", "0").toInt, create = parsedConfig.configMap.getOrElse(key + "create","") == "true", attachSigningCert = parsedConfig.configMap.getOrElse(key + "attachSigningCert","") == "true", authorizationType = parsedConfig.configMap.getOrElse(key + "authorizationType",""), includeAggregateResults = parsedConfig.configMap.getOrElse(key + "includeAggregateResults","") == "true", authenticationType = parsedConfig.configMap.getOrElse(key + "authenticationType", ""), audit = Audit(parsedConfig), shrineSteward = Steward(parsedConfig), broadcasterServiceEndpointUrl = parsedConfig.configMap.get(key + "broadcasterServiceEndpoint.url") ) } //adapted from https://gist.github.com/joseraya/176821d856b43b1cfe19 object gruntWatchCorsSupport extends Directive0 with RouteConcatenation { import spray.http.HttpHeaders.{`Access-Control-Allow-Methods`, `Access-Control-Max-Age`, `Access-Control-Allow-Headers`,`Access-Control-Allow-Origin`} import spray.routing.directives.RespondWithDirectives.respondWithHeaders import spray.routing.directives.MethodDirectives.options import spray.routing.directives.RouteDirectives.complete import spray.http.HttpMethods.{OPTIONS,GET,POST} import spray.http.AllOrigins private val allowOriginHeader = `Access-Control-Allow-Origin`(AllOrigins) private val optionsCorsHeaders = List( `Access-Control-Allow-Headers`("Origin, X-Requested-With, Content-Type, Accept, Accept-Encoding, Accept-Language, Host, Referer, User-Agent, Authorization"), `Access-Control-Max-Age`(1728000)) //20 days val gruntWatch:Boolean = DashboardConfigSource.config.getBoolean("shrine.dashboard.gruntWatch") override def happly(f: (HNil) => Route): Route = { if(gruntWatch) { options { respondWithHeaders(`Access-Control-Allow-Methods`(OPTIONS, GET, POST) :: allowOriginHeader :: optionsCorsHeaders){ complete(StatusCodes.OK) } } ~ f(HNil) } else f(HNil) } } diff --git a/apps/dashboard-app/src/test/resources/dashboard.conf b/apps/dashboard-app/src/test/resources/dashboard.conf index 66d1e6e38..a3edd6875 100644 --- a/apps/dashboard-app/src/test/resources/dashboard.conf +++ b/apps/dashboard-app/src/test/resources/dashboard.conf @@ -1,51 +1,54 @@ shrine { + problem { + problemHandler = "net.shrine.problem.DatabaseProblemHandler$" + } authenticate { usersource { //Bogus security for testing type = "ConfigUserSource" //Must be ConfigUserSource (for isolated testing) or PmUserSource (for everything else) researcher { username = "ben" password = "kapow" } steward { username = "dave" password = "kablam" } qep{ username = "qep" password = "trustme" } admin{ username = "keith" password = "shh!" } } } dashboard { happyBaseUrl = "classpath://resources/testhappy" statusBaseUrl = "classpath://resources/teststatus" database { dataSourceFrom = "testDataSource" slickProfileClassName = "slick.driver.H2Driver$" createTestValuesOnStart = true createTablesOnStart = true // For testing without JNDI testDataSource { //typical test settings for unit tests driverClassName = "org.h2.Driver" url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" } } } keystore { file = "shrine.keystore" password = "chiptesting" privateKeyAlias = "test-cert" keyStoreType = "JKS" caCertAliases = [carra ca] } } \ No newline at end of file diff --git a/apps/proxy/src/main/scala/net/shrine/proxy/ShrineProxy.scala b/apps/proxy/src/main/scala/net/shrine/proxy/ShrineProxy.scala index 59f159f44..bd5f820b5 100644 --- a/apps/proxy/src/main/scala/net/shrine/proxy/ShrineProxy.scala +++ b/apps/proxy/src/main/scala/net/shrine/proxy/ShrineProxy.scala @@ -1,105 +1,98 @@ package net.shrine.proxy -import java.io.BufferedReader -import java.io.File -import java.io.FileReader -import java.io.IOException -import java.util.ArrayList -import java.util.List import net.shrine.log.Loggable import scala.xml.XML import scala.xml.NodeSeq import net.shrine.client.JerseyHttpClient import net.shrine.client.HttpResponse import net.shrine.crypto.TrustParam.AcceptAllCerts -import scala.util.control.NonFatal import net.shrine.client.HttpClient /** * [ Author ] * * @author Clint Gilbert * @author Ricardo Delima * @author Andrew McMurry * @author Britt Fitch *

* Date: Apr 1, 2008 * Harvard Medical School Center for BioMedical Informatics * @link http://cbmi.med.harvard.edu *

* NB: In the previous version of this class, the black list had no effect on the result of calling * isAllawableDomain (now isAllowableUrl). This behavior is preserved here. -Clint * */ trait ShrineProxy { def isAllowableUrl(redirectURL: String): Boolean def redirect(request: NodeSeq): HttpResponse } object DefaultShrineProxy extends Loggable { private[proxy] def loadWhiteList: Set[String] = loadList("whitelist") private[proxy] def loadBlackList: Set[String] = loadList("blacklist") private def loadList(listname: String): Set[String] = { val confFile = getClass.getClassLoader.getResource("shrine-proxy-acl.xml").getFile val confXml = XML.loadFile(confFile) (confXml \\ "lists" \ listname \ "host").map(_.text.trim).toSet } val jerseyHttpClient: HttpClient = { import scala.concurrent.duration._ //TODO: Make timeout configurable? JerseyHttpClient(AcceptAllCerts, Duration.Inf) } } final class DefaultShrineProxy(val whiteList: Set[String], val blackList: Set[String], val httpClient: HttpClient) extends ShrineProxy with Loggable { def this() = this(DefaultShrineProxy.loadWhiteList, DefaultShrineProxy.loadBlackList, DefaultShrineProxy.jerseyHttpClient) import DefaultShrineProxy._ whiteList.foreach(entry => info(s"Whitelist entry: $entry")) blackList.foreach(entry => info(s"Blacklist entry: $entry")) info("Loaded access control lists.") override def isAllowableUrl(redirectURL: String): Boolean = whiteList.exists(redirectURL.startsWith) && !blackList.exists(redirectURL.startsWith) /** * Redirect to a URL embedded within the I2B2 message * * @param request a chunk of xml with a element, containing the url to redirect to. * @return the String result of accessing the url embedded in the passed request xml. * @throws ShrineMessageFormatException bad input XML */ override def redirect(request: NodeSeq): HttpResponse = { val redirectUrl = (request \\ "redirect_url").headOption.map(_.text.trim).getOrElse { error("Error parsing redirect_url tag") throw new ShrineMessageFormatException("Error parsing redirect_url tag") } if (redirectUrl == null || redirectUrl.isEmpty) { error("Detected missing redirect_url tag") throw new ShrineMessageFormatException("ShrineAdapter detected missing redirect_url tag") } //if redirectURL is not in the white list, do not proceed. if (!isAllowableUrl(redirectUrl)) { throw new ShrineMessageFormatException(s"redirectURL not in white list or is in black list: $redirectUrl") } debug(s"Proxy redirecting to $redirectUrl") httpClient.post(request.toString, redirectUrl) } } diff --git a/apps/shrine-app/src/test/resources/dashboard.conf b/apps/shrine-app/src/test/resources/dashboard.conf new file mode 100644 index 000000000..e280c18e3 --- /dev/null +++ b/apps/shrine-app/src/test/resources/dashboard.conf @@ -0,0 +1,5 @@ +shrine { + problem { + problemHandler = "net.shrine.problem.LoggingProblemHandler$" + } +} \ No newline at end of file diff --git a/apps/shrine-app/src/test/scala/net/shrine/status/StatusJaxrsTest.scala b/apps/shrine-app/src/test/scala/net/shrine/status/StatusJaxrsTest.scala index 156efa97a..fe851a016 100644 --- a/apps/shrine-app/src/test/scala/net/shrine/status/StatusJaxrsTest.scala +++ b/apps/shrine-app/src/test/scala/net/shrine/status/StatusJaxrsTest.scala @@ -1,133 +1,132 @@ package net.shrine.status import com.typesafe.config.ConfigFactory -import net.shrine.problem.TurnOffProblemConnector import net.shrine.util.ShouldMatchersForJUnit import org.json4s.{DefaultFormats, Formats} import org.junit.Test import org.json4s.native.Serialization import scala.collection.immutable.Map /** * Tests for StatusJaxrs * * @author david * @since 12/2/15 */ -class StatusJaxrsTest extends ShouldMatchersForJUnit with TurnOffProblemConnector { +class StatusJaxrsTest extends ShouldMatchersForJUnit { implicit def json4sFormats: Formats = DefaultFormats val expectedConfig = ConfigFactory.load("shrine") val statusJaxrs = StatusJaxrs(expectedConfig) @Test def testVersion() = { val versionString = statusJaxrs.version val version = Serialization.read[Version](versionString) version should equal(Version("changeMe")) } @Test def testConfig() = { val expectedJson4sConfig = Json4sConfig(expectedConfig) val configString = statusJaxrs.config val config = Serialization.read[Json4sConfig](configString) config should equal(expectedJson4sConfig) val passwordKeys = config.keyValues.filter(x => Json4sConfig.isPassword(x._1)) passwordKeys should equal(Map.empty[String,String]) } @Test def testSummary() = { val summaryString = statusJaxrs.summary val summary = Serialization.read[Summary](summaryString) summary.isHub should be (true) summary.adapterMappingsFileName.isDefined should be (true) summary.adapterMappingsDate.isEmpty should be (true) summary.adapterOk should be (true) summary.keystoreOk should be (true) summary.hubOk should be (false) summary.qepOk should be (true) } @Test def testI2b2() = { val i2b2String = statusJaxrs.i2b2 val i2b2 = Serialization.read[I2b2](i2b2String) i2b2.crcUrl.isDefined should be (true) } @Test def testOptionalParts() = { val string = statusJaxrs.optionalParts val actual = Serialization.read[OptionalParts](string) actual.isHub should be (true) actual.stewardEnabled should be (true) actual.shouldQuerySelf should be (false) actual.downstreamNodes.size should be (4) } @Test def testHub() = { val string = statusJaxrs.hub val actual = Serialization.read[Hub](string) actual.create should be (true) actual.shouldQuerySelf should be (false) actual.downstreamNodes.size should be (4) } @Test def testQep() = { val string = statusJaxrs.qep val actual = Serialization.read[Qep](string) actual.create should be (true) actual.attachSigningCert should be (true) actual.authenticationType should be ("PmAuthenticator") actual.authorizationType should be ("StewardQueryAuthorizationService") actual.includeAggregateResults should be (false) actual.maxQueryWaitTimeMillis should be (300000000L) } @Test def testAdapter() = { val string = statusJaxrs.adapter val actual = Serialization.read[Adapter](string) actual.adapterLockoutAttemptsThreshold should be (10) } @Test def testKeyStore() = { val string = statusJaxrs.keystore val actual = Serialization.read[KeyStoreReport](string) println(s"KeyStoreReport is $actual") } } diff --git a/apps/steward-app/src/main/scala/net/shrine/steward/Boot.scala b/apps/steward-app/src/main/scala/net/shrine/steward/Boot.scala index ed225fd98..f5028a65a 100644 --- a/apps/steward-app/src/main/scala/net/shrine/steward/Boot.scala +++ b/apps/steward-app/src/main/scala/net/shrine/steward/Boot.scala @@ -1,20 +1,23 @@ package net.shrine.steward -import akka.actor.{Props, ActorSystem} +import akka.actor.{ActorSystem, Props} import net.shrine.log.Loggable +import net.shrine.steward.db.StewardDatabase import spray.servlet.WebBoot // this class is instantiated by the servlet initializer // it needs to have a default constructor and implement // the spray.servlet.WebBoot trait class Boot extends WebBoot with Loggable { info(s"StewardActors akka daemonic config is ${StewardConfigSource.config.getString("akka.daemonic")}") + val warmUp = StewardDatabase.db.warmUp + // we need an ActorSystem to host our application in val system = ActorSystem("StewardActors",StewardConfigSource.config) // the service actor replies to incoming HttpRequests val serviceActor = system.actorOf(Props[StewardServiceActor]) } \ No newline at end of file diff --git a/apps/steward-app/src/main/scala/net/shrine/steward/StewardService.scala b/apps/steward-app/src/main/scala/net/shrine/steward/StewardService.scala index 98bf63e4a..817d48bc0 100644 --- a/apps/steward-app/src/main/scala/net/shrine/steward/StewardService.scala +++ b/apps/steward-app/src/main/scala/net/shrine/steward/StewardService.scala @@ -1,366 +1,365 @@ package net.shrine.steward import akka.actor.Actor import akka.event.Logging import net.shrine.authentication.UserAuthenticator - -import net.shrine.authorization.steward.{TopicIdAndName, Date, TopicId, InboundTopicRequest, InboundShrineQuery, StewardsTopics, TopicState, OutboundUser, OutboundTopic, UserName} +import net.shrine.authorization.steward._ import net.shrine.i2b2.protocol.pm.User -import net.shrine.steward.db.{DetectedAttemptByWrongUserToChangeTopic, ApprovedTopicCanNotBeChanged, TopicDoesNotExist, SortOrder, QueryParameters, StewardDatabase} +import net.shrine.serialization.NodeSeqSerializer +import net.shrine.steward.db._ import net.shrine.steward.pmauth.Authorizer import shapeless.HNil - -import spray.http.{HttpResponse, HttpRequest, StatusCodes} +import spray.http.{HttpRequest, HttpResponse, StatusCodes} import spray.httpx.Json4sSupport import spray.routing.directives.LogEntry -import spray.routing.{AuthenticationFailedRejection, Rejected, RouteConcatenation, Directive0, Route, HttpService} - -import org.json4s.{DefaultFormats, Formats} +import spray.routing._ +import org.json4s.{DefaultFormats, DefaultJsonFormats, Formats} import scala.concurrent.ExecutionContext.Implicits.global import scala.util.{Failure, Success, Try} // we don't implement our route structure directly in the service actor because // we want to be able to test it independently, without having to spin up an actor class StewardServiceActor extends Actor with StewardService { // the HttpService trait defines only one abstract member, which // connects the services environment to the enclosing actor or test def actorRefFactory = context // this actor only runs our route, but you could add // other things here, like request stream processing // or timeout handling def receive = runRoute(route) } // this trait defines our service behavior independently from the service actor trait StewardService extends HttpService with Json4sSupport { - implicit def json4sFormats: Formats = DefaultFormats + implicit def json4sFormats: Formats = DefaultFormats + new NodeSeqSerializer val userAuthenticator = UserAuthenticator(StewardConfigSource.config) //don't need to do anything special for unauthorized users, but they do need access to a static form. lazy val route:Route = gruntWatchCorsSupport{ requestLogRoute ~ fullLogRoute } lazy val requestLogRoute = logRequestResponse(logEntryForRequest _) { redirectToIndex ~ staticResources ~ makeTrouble ~ about } lazy val fullLogRoute = logRequestResponse(logEntryForRequestResponse _) { qepRoute ~ authenticatedInBrowser } // logs just the request method, uri and response at info level //logging is controlled by Akka's config, slf4j, and log4j config def logEntryForRequestResponse(req: HttpRequest): Any => Option[LogEntry] = { case res: HttpResponse => { Some(LogEntry(s"\n Request: $req \n Response: $res", Logging.InfoLevel)) } case _ => None // other kind of responses } // logs just the request method, uri and response status at info level def logEntryForRequest(req: HttpRequest): Any => Option[LogEntry] = { case res: HttpResponse => { Some(LogEntry(s"\n Request: $req \n Response status: ${res.status}", Logging.InfoLevel)) } case _ => None // other kind of responses } //pathPrefixTest shields the QEP code from the redirect. def authenticatedInBrowser: Route = pathPrefixTest("user"|"steward"|"researcher") { reportIfFailedToAuthenticate { authenticate(userAuthenticator.basicUserAuthenticator) { user => StewardDatabase.db.upsertUser(user) pathPrefix("user") {userRoute(user)} ~ pathPrefix("steward") {stewardRoute(user)} ~ pathPrefix("researcher") {researcherRoute(user)} } } } val reportIfFailedToAuthenticate = routeRouteResponse { case Rejected(List(AuthenticationFailedRejection(_,_))) => complete("AuthenticationFailed") } def makeTrouble = pathPrefix("makeTrouble") { complete(throw new IllegalStateException("fake trouble")) } lazy val redirectToIndex = pathEnd { redirect("steward/client/index.html", StatusCodes.PermanentRedirect) //todo pick up "steward" programatically } ~ ( path("index.html") | pathSingleSlash) { redirect("client/index.html", StatusCodes.PermanentRedirect) } lazy val staticResources = pathPrefix("client") { pathEnd { redirect("client/index.html", StatusCodes.PermanentRedirect) } ~ pathSingleSlash { redirect("index.html", StatusCodes.PermanentRedirect) } ~ { getFromResourceDirectory("client") } } lazy val about = pathPrefix("about") { path("createTopicsMode") { get { complete(StewardConfigSource.createTopicsInState.name) } } } def userRoute(user:User):Route = get { pathPrefix("whoami") { complete(OutboundUser.createFromUser(user)) } } def qepRoute:Route = pathPrefix("qep") { authenticate(userAuthenticator.basicUserAuthenticator) { user => StewardDatabase.db.upsertUser(user) authorize(Authorizer.authorizeQep(user)) { pathPrefix("requestQueryAccess") ( requestQueryAccess ) ~ pathPrefix("approvedTopics") ( getApprovedTopicsForUser ) } } } def requestQueryAccess:Route = post { requestQueryAccessWithTopic ~ requestQueryAccessWithoutTopic } def requestQueryAccessWithTopic:Route = path("user" /Segment/ "topic" / IntNumber) { (userId,topicId) => entity(as[InboundShrineQuery]) { shrineQuery:InboundShrineQuery => //todo really pull the user out of the shrine query and check vs the PM. If they aren't there, reject them for this new reason val result: (TopicState, Option[TopicIdAndName]) = StewardDatabase.db.logAndCheckQuery(userId,Some(topicId),shrineQuery) respondWithStatus(result._1.statusCode) { if(result._1.statusCode == StatusCodes.OK) complete (result._2.getOrElse("")) else complete(result._1.message) } } } def requestQueryAccessWithoutTopic:Route = path("user" /Segment) { userId => entity(as[InboundShrineQuery]) { shrineQuery:InboundShrineQuery => //todo really pull the user out of the shrine query and check vs the PM. If they aren't there, reject them for this new reason val result = StewardDatabase.db.logAndCheckQuery(userId,None,shrineQuery) respondWithStatus(result._1.statusCode) { if(result._1.statusCode == StatusCodes.OK) complete (result._2) else complete(result._1.message) } } } lazy val getApprovedTopicsForUser:Route = get { //todo change to "researcher" path("user" /Segment) { userId => //todo really pull the user out of the shrine query and check vs the PM. If they aren't there, reject them for this new reason val queryParameters = QueryParameters(researcherIdOption = Some(userId),stateOption = Some(TopicState.approved)) val researchersTopics = StewardDatabase.db.selectTopicsForResearcher(queryParameters) complete(researchersTopics) } } def researcherRoute(user:User):Route = authorize(Authorizer.authorizeResearcher(user)) { pathPrefix("topics") { getUserTopics(user.username) } ~ pathPrefix("queryHistory") { getUserQueryHistory(Some(user.username)) } ~ pathPrefix("requestTopicAccess") { requestTopicAccess(user) } ~ pathPrefix("editTopicRequest") { editTopicRequest(user) } } def getUserTopics(userId:UserName):Route = get { //lookup topics for this user in the db matchQueryParameters(Some(userId)){queryParameters:QueryParameters => val researchersTopics = StewardDatabase.db.selectTopicsForResearcher(queryParameters) complete(researchersTopics) } } def matchQueryParameters(userName: Option[UserName])(parameterRoute:QueryParameters => Route): Route = { parameters('state.?,'skip.as[Int].?,'limit.as[Int].?,'sortBy.as[String].?,'sortDirection.as[String].?,'minDate.as[Date].?,'maxDate.as[Date].?) { (stateStringOption,skipOption,limitOption,sortByOption,sortOption,minDate,maxDate) => val stateTry = TopicState.stateForStringOption(stateStringOption) stateTry match { case Success(stateOption) => val qp = QueryParameters(userName, stateOption, skipOption, limitOption, sortByOption, SortOrder.sortOrderForStringOption(sortOption), minDate, maxDate ) parameterRoute(qp) case Failure(ex) => badStateRoute(stateStringOption) } } } def badStateRoute(stateStringOption:Option[String]):Route = { respondWithStatus(StatusCodes.UnprocessableEntity) { complete(s"Topic state ${stateStringOption.getOrElse(s"$stateStringOption (stateStringOption should never be None at this point)")} unknown. Please specify one of ${TopicState.namesToStates.keySet}") } } def getUserQueryHistory(userIdOption:Option[UserName]):Route = get { - path("topic"/IntNumber) { topicId:TopicId => - getQueryHistoryForUserByTopic(userIdOption,Some(topicId)) + path("topic" / IntNumber) { topicId: TopicId => + getQueryHistoryForUserByTopic(userIdOption, Some(topicId)) } ~ - getQueryHistoryForUserByTopic(userIdOption,None) + getQueryHistoryForUserByTopic(userIdOption, None) } def getQueryHistoryForUserByTopic(userIdOption:Option[UserName],topicIdOption:Option[TopicId]) = get { matchQueryParameters(userIdOption) { queryParameters:QueryParameters => val queryHistory = StewardDatabase.db.selectQueryHistory(queryParameters, topicIdOption) - + implicit val formats = queryHistory.json4sMarshaller complete(queryHistory) } } def requestTopicAccess(user:User):Route = post { entity(as[InboundTopicRequest]) { topicRequest: InboundTopicRequest => //todo notify the data stewards StewardDatabase.db.createRequestForTopicAccess(user,topicRequest) + complete(StatusCodes.Accepted) } } def editTopicRequest(user:User):Route = post { path(IntNumber) { topicId => entity(as[InboundTopicRequest]) { topicRequest: InboundTopicRequest => //todo notify the data stewards val updatedTopicTry:Try[OutboundTopic] = StewardDatabase.db.updateRequestForTopicAccess(user, topicId, topicRequest) updatedTopicTry match { case Success(updatedTopic) => respondWithStatus(StatusCodes.Accepted) { complete(updatedTopic) } case Failure(x) => x match { case x:TopicDoesNotExist => respondWithStatus(StatusCodes.NotFound) { complete(x.getMessage) } case x:ApprovedTopicCanNotBeChanged => respondWithStatus(StatusCodes.Forbidden) { complete(x.getMessage) } case x:DetectedAttemptByWrongUserToChangeTopic => respondWithStatus(StatusCodes.Forbidden) { complete(x.getMessage) } case _ => throw x } } } } } def stewardRoute(user:User):Route = authorize(Authorizer.authorizeSteward(user)) { pathPrefix("queryHistory" / "user") {getUserQueryHistory } ~ pathPrefix("queryHistory") {getQueryHistory} ~ pathPrefix("topics" / "user")(getUserTopicsForSteward) ~ path("topics"){getTopicsForSteward} ~ pathPrefix("approveTopic")(approveTopicForUser(user)) ~ pathPrefix("rejectTopic")(rejectTopicForUser(user)) ~ pathPrefix("statistics"){getStatistics} } lazy val getUserQueryHistory:Route = pathPrefix(Segment) { userId => getUserQueryHistory(Some(userId)) } lazy val getQueryHistory:Route = getUserQueryHistory(None) lazy val getTopicsForSteward:Route = getTopicsForSteward(None) lazy val getUserTopicsForSteward:Route = path(Segment) { userId => getTopicsForSteward(Some(userId)) } def getTopicsForSteward(userIdOption:Option[UserName]):Route = get { //lookup topics for this user in the db matchQueryParameters(userIdOption) { queryParameters: QueryParameters => val stewardsTopics:StewardsTopics = StewardDatabase.db.selectTopicsForSteward(queryParameters) complete(stewardsTopics) } } def approveTopicForUser(user:User):Route = changeStateForTopic(TopicState.approved,user) def rejectTopicForUser(user:User):Route = changeStateForTopic(TopicState.rejected,user) def changeStateForTopic(state:TopicState,user:User):Route = post { path("topic" / IntNumber) { topicId => StewardDatabase.db.changeTopicState(topicId, state, user.username).fold(respondWithStatus(StatusCodes.UnprocessableEntity){ complete(s"No topic found for $topicId") })(topic => complete(StatusCodes.OK)) } } def getStatistics:Route = pathPrefix("queriesPerUser"){getQueriesPerUser} ~ pathPrefix("topicsPerState"){getTopicsPerState} def getQueriesPerUser:Route = get{ matchQueryParameters(None) { queryParameters: QueryParameters => val result = StewardDatabase.db.selectShrineQueryCountsPerUser(queryParameters) complete(result) } } def getTopicsPerState:Route = get{ matchQueryParameters(None) { queryParameters: QueryParameters => val result = StewardDatabase.db.selectTopicCountsPerState(queryParameters) complete(result) } } } //adapted from https://gist.github.com/joseraya/176821d856b43b1cfe19 object gruntWatchCorsSupport extends Directive0 with RouteConcatenation { import spray.http.HttpHeaders.{`Access-Control-Allow-Methods`, `Access-Control-Max-Age`, `Access-Control-Allow-Headers`,`Access-Control-Allow-Origin`} import spray.routing.directives.RespondWithDirectives.respondWithHeaders import spray.routing.directives.MethodDirectives.options import spray.routing.directives.RouteDirectives.complete import spray.http.HttpMethods.{OPTIONS,GET,POST} import spray.http.AllOrigins private val allowOriginHeader = `Access-Control-Allow-Origin`(AllOrigins) private val optionsCorsHeaders = List( `Access-Control-Allow-Headers`("Origin, X-Requested-With, Content-Type, Accept, Accept-Encoding, Accept-Language, Host, Referer, User-Agent, Authorization"), `Access-Control-Max-Age`(1728000)) //20 days val gruntWatch:Boolean = StewardConfigSource.config.getBoolean("shrine.steward.gruntWatch") override def happly(f: (HNil) => Route): Route = { if(gruntWatch) { options { respondWithHeaders(`Access-Control-Allow-Methods`(OPTIONS, GET, POST) :: allowOriginHeader :: optionsCorsHeaders){ complete(StatusCodes.OK) } } ~ f(HNil) } else f(HNil) } } diff --git a/apps/steward-app/src/main/scala/net/shrine/steward/db/StewardDatabase.scala b/apps/steward-app/src/main/scala/net/shrine/steward/db/StewardDatabase.scala index 980aba3c1..7d161711e 100644 --- a/apps/steward-app/src/main/scala/net/shrine/steward/db/StewardDatabase.scala +++ b/apps/steward-app/src/main/scala/net/shrine/steward/db/StewardDatabase.scala @@ -1,633 +1,638 @@ package net.shrine.steward.db import java.sql.SQLException import java.util.concurrent.atomic.AtomicInteger import javax.sql.DataSource import com.typesafe.config.Config import net.shrine.authorization.steward.{Date, ExternalQueryId, InboundShrineQuery, InboundTopicRequest, OutboundShrineQuery, OutboundTopic, OutboundUser, QueriesPerUser, QueryContents, QueryHistory, ResearchersTopics, StewardQueryId, StewardsTopics, TopicId, TopicIdAndName, TopicState, TopicStateName, TopicsPerState, UserName, researcherRole, stewardRole} import net.shrine.i2b2.protocol.pm.User import net.shrine.log.Loggable import net.shrine.slick.TestableDataSourceCreator import net.shrine.steward.{CreateTopicsMode, StewardConfigSource} import slick.dbio.Effect.Read import slick.driver.JdbcProfile import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration.{Duration, DurationInt} import scala.concurrent.{Await, Future, blocking} import scala.language.postfixOps import scala.util.Try /** * Database access code for the data steward service. * * I'm not letting Slick handle foreign key resolution for now. I want to keep that logic separate to handle dirty data with some grace. * * @author dwalend * @since 1.19 */ case class StewardDatabase(schemaDef:StewardSchema,dataSource: DataSource) extends Loggable { import schemaDef._ import jdbcProfile.api._ val database = Database.forDataSource(dataSource) def createTables() = schemaDef.createTables(database) def dropTables() = schemaDef.dropTables(database) def dbRun[R](action: DBIOAction[R, NoStream, Nothing]):R = { val future: Future[R] = database.run(action) blocking { Await.result(future, 10 seconds) } } + def warmUp = { + dbRun(allUserQuery.size.result) + } + def selectUsers:Seq[UserRecord] = { dbRun(allUserQuery.result) } // todo use whenever a shrine query is logged def upsertUser(user:User):Unit = { val userRecord = UserRecord(user) dbRun(allUserQuery.insertOrUpdate(userRecord)) } def createRequestForTopicAccess(user:User,topicRequest:InboundTopicRequest):TopicRecord = { val createInState = StewardConfigSource.createTopicsInState val now = System.currentTimeMillis() val topicRecord = TopicRecord(Some(nextTopicId.getAndIncrement),topicRequest.name,topicRequest.description,user.username,now,createInState.topicState) val userTopicRecord = UserTopicRecord(user.username,topicRecord.id.get,TopicState.approved,user.username,now) dbRun(for{ _ <- allTopicQuery += topicRecord _ <- allUserTopicQuery += userTopicRecord } yield topicRecord) } def updateRequestForTopicAccess(user:User,topicId:TopicId,topicRequest:InboundTopicRequest):Try[OutboundTopic] = Try { dbRun(mostRecentTopicQuery.filter(_.id === topicId).result.headOption.flatMap{ option => val oldTopicRecord = option.getOrElse(throw TopicDoesNotExist(topicId = topicId)) if(user.username != oldTopicRecord.createdBy) throw DetectedAttemptByWrongUserToChangeTopic(topicId,user.username,oldTopicRecord.createdBy) if(oldTopicRecord.state == TopicState.approved) throw ApprovedTopicCanNotBeChanged(topicId) val updatedTopic = oldTopicRecord.copy(name = topicRequest.name, description = topicRequest.description, changedBy = user.username, changeDate = System.currentTimeMillis()) (allTopicQuery += updatedTopic).flatMap{_ => outboundUsersForNamesAction(Set(updatedTopic.createdBy,updatedTopic.changedBy)).map(updatedTopic.toOutboundTopic) } } ) } def selectTopicsForResearcher(parameters:QueryParameters):ResearchersTopics = { require(parameters.researcherIdOption.isDefined,"A researcher's parameters must supply a user id") val (count,topics,userNamesToOutboundUsers) = dbRun( for{ count <- topicCountQuery(parameters).length.result topics <- topicSelectQuery(parameters).result userNamesToOutboundUsers <- outboundUsersForNamesAction((topics.map(_.createdBy) ++ topics.map(_.changedBy)).to[Set]) } yield (count, topics,userNamesToOutboundUsers)) ResearchersTopics(parameters.researcherIdOption.get, count, parameters.skipOption.getOrElse(0), topics.map(_.toOutboundTopic(userNamesToOutboundUsers))) } //treat as private (currently used in test) def selectTopics(queryParameters: QueryParameters):Seq[TopicRecord] = { dbRun(topicSelectQuery(queryParameters).result) } def selectTopicsForSteward(queryParameters: QueryParameters):StewardsTopics = { val (count,topics,userNamesToOutboundUsers) = dbRun{ for{ count <- topicCountQuery(queryParameters).length.result topics <- topicSelectQuery(queryParameters).result userNamesToOutboundUsers <- outboundUsersForNamesAction((topics.map(_.createdBy) ++ topics.map(_.changedBy)).to[Set]) } yield (count,topics,userNamesToOutboundUsers) } StewardsTopics(count, queryParameters.skipOption.getOrElse(0), topics.map(_.toOutboundTopic(userNamesToOutboundUsers))) } private def topicSelectQuery(queryParameters: QueryParameters):Query[TopicTable, TopicTable#TableElementType, Seq] = { val countFilter = topicCountQuery(queryParameters) //todo is there some way to do something with a map from column names to columns that I don't have to update? I couldn't find one. // val orderByQuery = queryParameters.sortByOption.fold(countFilter)( // columnName => limitFilter.sortBy(x => queryParameters.sortOrder.orderForColumn(countFilter.columnForName(columnName)))) val orderByQuery = queryParameters.sortByOption.fold(countFilter)( columnName => countFilter.sortBy(x => queryParameters.sortOrder.orderForColumn(columnName match { case "id" => x.id case "name" => x.name case "description" => x.description case "createdBy" => x.createdBy case "createDate" => x.createDate case "state" => x.state case "changedBy" => x.changedBy case "changeDate" => x.changeDate }))) val skipFilter = queryParameters.skipOption.fold(orderByQuery)(skip => orderByQuery.drop(skip)) val limitFilter = queryParameters.limitOption.fold(skipFilter)(limit => skipFilter.take(limit)) limitFilter } private def topicCountQuery(queryParameters: QueryParameters):Query[TopicTable, TopicTable#TableElementType, Seq] = { val allTopics:Query[TopicTable, TopicTable#TableElementType, Seq] = mostRecentTopicQuery val researcherFilter = queryParameters.researcherIdOption.fold(allTopics)(userId => allTopics.filter(_.createdBy === userId)) val stateFilter = queryParameters.stateOption.fold(researcherFilter)(state => researcherFilter.filter(_.state === state.name)) val minDateFilter = queryParameters.minDate.fold(stateFilter)(minDate => stateFilter.filter(_.changeDate >= minDate)) val maxDateFilter = queryParameters.maxDate.fold(minDateFilter)(maxDate => minDateFilter.filter(_.changeDate <= maxDate)) maxDateFilter } def changeTopicState(topicId:TopicId,state:TopicState,userId:UserName):Option[TopicRecord] = { val noTopicRecord:Option[TopicRecord] = None val noOpDBIO:DBIOAction[Option[TopicRecord], NoStream, Effect.Write] = DBIO.successful(noTopicRecord) dbRun(mostRecentTopicQuery.filter(_.id === topicId).result.headOption.flatMap( _.fold(noOpDBIO){ originalTopic => val updatedTopic = originalTopic.copy(state = state, changedBy = userId, changeDate = System.currentTimeMillis()) (allTopicQuery += updatedTopic).map(_ => Option(updatedTopic)) } )) } def selectTopicCountsPerState(queryParameters: QueryParameters):TopicsPerState = { dbRun(for{ totalTopics <- topicCountQuery(queryParameters).length.result topicsPerStateName <- topicCountsPerState(queryParameters).result } yield TopicsPerState(totalTopics,topicsPerStateName)) } private def topicCountsPerState(queryParameters: QueryParameters): Query[(Rep[TopicStateName], Rep[Int]), (TopicStateName, Int), Seq] = { val groupedByState = topicCountQuery(queryParameters).groupBy(topicRecord => topicRecord.state) groupedByState.map{case (state,result) => (state,result.length)} } def logAndCheckQuery(userId:UserName,topicId:Option[TopicId],shrineQuery:InboundShrineQuery):(TopicState,Option[TopicIdAndName]) = { //todo upsertUser(user) when the info is available from the PM val noOpDBIOForState: DBIOAction[TopicState, NoStream, Effect.Read] = DBIO.successful { if (StewardConfigSource.createTopicsInState == CreateTopicsMode.TopicsIgnoredJustLog) TopicState.approved else TopicState.createTopicsModeRequiresTopic } val noOpDBIOForTopicName: DBIOAction[Option[String], NoStream, Read] = DBIO.successful{None} val (state,topicName) = dbRun(for{ state <- topicId.fold(noOpDBIOForState)( someTopicId => mostRecentTopicQuery.filter(_.id === someTopicId).filter(_.createdBy === userId).map(_.state).result.headOption.map( _.fold(TopicState.unknownForUser)(state => TopicState.namesToStates(state))) ) topicName <- topicId.fold(noOpDBIOForTopicName)( someTopicId => mostRecentTopicQuery.filter(_.id === someTopicId).filter(_.createdBy === userId).map(_.name).result.headOption ) _ <- allQueryTable += ShrineQueryRecord(userId,topicId,shrineQuery,state) } yield (state,topicName)) val topicIdAndName:Option[TopicIdAndName] = (topicId,topicName) match { case (Some(id),Some(name)) => Option(TopicIdAndName(id.toString,name)) case (None,None) => None case (Some(id),None) => if(state == TopicState.unknownForUser) None else throw new IllegalStateException(s"How did you get here for $userId with $id and $state for $shrineQuery") } (state,topicIdAndName) } def selectQueryHistory(queryParameters: QueryParameters,topicParameter:Option[TopicId]):QueryHistory = { val (count,shrineQueries,topics,userNamesToOutboundUsers) = dbRun(for { count <- shrineQueryCountQuery(queryParameters,topicParameter).length.result shrineQueries <- shrineQuerySelectQuery(queryParameters, topicParameter).result topics <- mostRecentTopicQuery.filter(_.id.inSet(shrineQueries.map(_.topicId).to[Set].flatten)).result userNamesToOutboundUsers <- outboundUsersForNamesAction(shrineQueries.map(_.userId).to[Set] ++ (topics.map(_.createdBy) ++ topics.map(_.changedBy)).to[Set]) } yield (count,shrineQueries,topics,userNamesToOutboundUsers)) val topicIdsToTopics: Map[Option[TopicId], TopicRecord] = topics.map(x => (x.id, x)).toMap def toOutboundShrineQuery(queryRecord: ShrineQueryRecord): OutboundShrineQuery = { val topic = topicIdsToTopics.get(queryRecord.topicId) val outboundTopic: Option[OutboundTopic] = topic.map(_.toOutboundTopic(userNamesToOutboundUsers)) val outboundUserOption = userNamesToOutboundUsers.get(queryRecord.userId) //todo if a user is unknown and the system is in a mode that requires everyone to log into the data steward notify the data steward val outboundUser: OutboundUser = outboundUserOption.getOrElse(OutboundUser.createUnknownUser(queryRecord.userId)) queryRecord.createOutboundShrineQuery(outboundTopic, outboundUser) } - QueryHistory(count,queryParameters.skipOption.getOrElse(0),shrineQueries.map(toOutboundShrineQuery)) + val result = QueryHistory(count,queryParameters.skipOption.getOrElse(0),shrineQueries.map(toOutboundShrineQuery)) + result } private def outboundUsersForNamesAction(userNames:Set[UserName]):DBIOAction[Map[UserName, OutboundUser], NoStream, Read] = { allUserQuery.filter(_.userName.inSet(userNames)).result.map(_.map(x => (x.userName,x.asOutboundUser)).toMap) } private def shrineQuerySelectQuery(queryParameters: QueryParameters,topicParameter:Option[TopicId]):Query[QueryTable, QueryTable#TableElementType, Seq] = { val countQuery = shrineQueryCountQuery(queryParameters,topicParameter) //todo is there some way to do something with a map from column names to columns that I don't have to update? I couldn't find one. // val orderByQuery = queryParameters.sortByOption.fold(limitFilter)( // columnName => limitFilter.sortBy(x => queryParameters.sortOrder.orderForColumn(allQueryTable.columnForName(columnName)))) val orderByQuery = queryParameters.sortByOption.fold(countQuery) { case "topicName" => val joined = countQuery.join(mostRecentTopicQuery).on(_.topicId === _.id) joined.sortBy(x => queryParameters.sortOrder.orderForColumn(x._2.name)).map(x => x._1) case columnName => countQuery.sortBy(x => queryParameters.sortOrder.orderForColumn(columnName match { case "stewardId" => x.stewardId case "externalId" => x.externalId case "researcherId" => x.researcherId case "name" => x.name case "topic" => x.topicId case "queryContents" => x.queryContents case "stewardResponse" => x.stewardResponse case "date" => x.date })) } val skipFilter = queryParameters.skipOption.fold(orderByQuery)(skip => orderByQuery.drop(skip)) val limitFilter = queryParameters.limitOption.fold(skipFilter)(limit => skipFilter.take(limit)) limitFilter } private def shrineQueryCountQuery(queryParameters: QueryParameters,topicParameter:Option[TopicId]):Query[QueryTable, QueryTable#TableElementType, Seq] = { val allShrineQueries:Query[QueryTable, QueryTable#TableElementType, Seq] = allQueryTable val topicFilter:Query[QueryTable, QueryTable#TableElementType, Seq] = topicParameter.fold(allShrineQueries)(topicId => allShrineQueries.filter(_.topicId === topicId)) val researcherFilter:Query[QueryTable, QueryTable#TableElementType, Seq] = queryParameters.researcherIdOption.fold(topicFilter)(researcherId => topicFilter.filter(_.researcherId === researcherId)) //todo this is probably a binary Approved/Not approved val stateFilter:Query[QueryTable, QueryTable#TableElementType, Seq] = queryParameters.stateOption.fold(researcherFilter)(stewardResponse => researcherFilter.filter(_.stewardResponse === stewardResponse.name)) val minDateFilter = queryParameters.minDate.fold(stateFilter)(minDate => stateFilter.filter(_.date >= minDate)) val maxDateFilter = queryParameters.maxDate.fold(minDateFilter)(maxDate => minDateFilter.filter(_.date <= maxDate)) maxDateFilter } def selectShrineQueryCountsPerUser(queryParameters: QueryParameters):QueriesPerUser = { val (totalQueries,queriesPerUser,userNamesToOutboundUsers) = dbRun(for { totalQueries <- shrineQueryCountQuery(queryParameters,None).length.result queriesPerUser <- shrineQueryCountsPerResearcher(queryParameters).result userNamesToOutboundUsers <- outboundUsersForNamesAction(queriesPerUser.map(x => x._1).to[Set]) } yield (totalQueries,queriesPerUser,userNamesToOutboundUsers)) val queriesPerOutboundUser:Seq[(OutboundUser,Int)] = queriesPerUser.map(x => (userNamesToOutboundUsers(x._1),x._2)) QueriesPerUser(totalQueries,queriesPerOutboundUser) } private def shrineQueryCountsPerResearcher(queryParameters: QueryParameters): Query[(Rep[UserName],Rep[Int]),(UserName,Int),Seq] = { val filteredShrineQueries:Query[QueryTable, QueryTable#TableElementType, Seq] = shrineQueryCountQuery(queryParameters,None) val groupedByResearcher = filteredShrineQueries.groupBy(shrineQuery => shrineQuery.researcherId) groupedByResearcher.map{case (researcher,result) => (researcher,result.length)} } lazy val nextTopicId:AtomicInteger = new AtomicInteger({ dbRun(allTopicQuery.map(_.id).max.result).getOrElse(0) + 1 }) } /** * Separate class to support schema generation without actually connecting to the database. * * @param jdbcProfile Database profile to use for the schema */ case class StewardSchema(jdbcProfile: JdbcProfile) extends Loggable { import jdbcProfile.api._ def ddlForAllTables = { allUserQuery.schema ++ allTopicQuery.schema ++ allQueryTable.schema ++ allUserTopicQuery.schema } //to get the schema, use the REPL //println(StewardSchema.schema.ddlForAllTables.createStatements.mkString(";\n")) def createTables(database:Database) = { try { val future = database.run(ddlForAllTables.create) Await.result(future,10 seconds) } catch { //I'd prefer to check and create schema only if absent. No way to do that with Oracle. case x:SQLException => info("Caught exception while creating tables. Recover by assuming the tables already exist.",x) } } def dropTables(database:Database) = { val future = database.run(ddlForAllTables.drop) //Really wait forever for the cleanup Await.result(future,Duration.Inf) } class UserTable(tag:Tag) extends Table[UserRecord](tag,"users") { def userName = column[UserName]("userName",O.PrimaryKey) def fullName = column[String]("fullName") def isSteward = column[Boolean]("isSteward") def * = (userName,fullName,isSteward) <> (UserRecord.tupled,UserRecord.unapply) } class TopicTable(tag:Tag) extends Table[TopicRecord](tag,"topics") { def id = column[TopicId]("id") def name = column[String]("name") def description = column[String]("description") def createdBy = column[UserName]("createdBy") def createDate = column[Date]("createDate") def state = column[TopicStateName]("state") def changedBy = column[UserName]("changedBy") def changeDate = column[Date]("changeDate") def idIndex = index("idIndex",id,unique = false) def topicNameIndex = index("topicNameIndex",name,unique = false) def createdByIndex = index("createdByIndex",createdBy,unique = false) def createDateIndex = index("createDateIndex",createDate,unique = false) def stateIndex = index("stateIndex",state,unique = false) def changedByIndex = index("changedByIndex",changedBy,unique = false) def changeDateIndex = index("changeDateIndex",changeDate,unique = false) def * = (id.?, name, description, createdBy, createDate, state, changedBy, changeDate) <> (fromRow, toRow) //(TopicRecord.tupled,TopicRecord.unapply) def fromRow = (fromParams _).tupled def fromParams(id:Option[TopicId] = None, name:String, description:String, createdBy:UserName, createDate:Date, stateName:String, changedBy:UserName, changeDate:Date): TopicRecord = { TopicRecord(id, name, description, createdBy, createDate, TopicState.namesToStates(stateName), changedBy, changeDate) } def toRow(topicRecord: TopicRecord) = Some((topicRecord.id, topicRecord.name, topicRecord.description, topicRecord.createdBy, topicRecord.createDate, topicRecord.state.name, topicRecord.changedBy, topicRecord.changeDate )) } class UserTopicTable(tag:Tag) extends Table[UserTopicRecord](tag,"userTopic") { def researcher = column[UserName]("researcher") def topicId = column[TopicId]("topicId") def state = column[TopicStateName]("state") def changedBy = column[UserName]("changedBy") def changeDate = column[Date]("changeDate") def researcherTopicIdIndex = index("researcherTopicIdIndex",(researcher,topicId),unique = true) def * = (researcher, topicId, state, changedBy, changeDate) <> (fromRow, toRow) def fromRow = (fromParams _).tupled def fromParams(researcher:UserName, topicId:TopicId, stateName:String, changedBy:UserName, changeDate:Date): UserTopicRecord = { UserTopicRecord(researcher,topicId,TopicState.namesToStates(stateName), changedBy, changeDate) } def toRow(userTopicRecord: UserTopicRecord):Option[(UserName,TopicId,String,UserName,Date)] = Some((userTopicRecord.researcher, userTopicRecord.topicId, userTopicRecord.state.name, userTopicRecord.changedBy, userTopicRecord.changeDate )) } class QueryTable(tag:Tag) extends Table[ShrineQueryRecord](tag,"queries") { def stewardId = column[StewardQueryId]("stewardId",O.PrimaryKey,O.AutoInc) def externalId = column[ExternalQueryId]("id") def name = column[String]("name") def researcherId = column[UserName]("researcher") def topicId = column[Option[TopicId]]("topic") def queryContents = column[QueryContents]("queryContents") def stewardResponse = column[String]("stewardResponse") def date = column[Date]("date") def externalIdIndex = index("externalIdIndex",externalId,unique = false) def queryNameIndex = index("queryNameIndex",name,unique = false) def researcherIdIndex = index("researcherIdIndex",stewardId,unique = false) def topicIdIndex = index("topicIdIndex",topicId,unique = false) def stewardResponseIndex = index("stewardResponseIndex",stewardResponse,unique = false) def dateIndex = index("dateIndex",date,unique = false) def * = (stewardId.?,externalId,name,researcherId,topicId,queryContents,stewardResponse,date) <> (fromRow,toRow) def fromRow = (fromParams _).tupled def fromParams(stewardId:Option[StewardQueryId], externalId:ExternalQueryId, name:String, userId:UserName, topicId:Option[TopicId], queryContents: QueryContents, stewardResponse:String, date:Date): ShrineQueryRecord = { ShrineQueryRecord(stewardId,externalId, name, userId, topicId, queryContents,TopicState.namesToStates(stewardResponse),date) } def toRow(queryRecord: ShrineQueryRecord):Option[( Option[StewardQueryId], ExternalQueryId, String, UserName, Option[TopicId], QueryContents, String, Date )] = Some((queryRecord.stewardId, queryRecord.externalId, queryRecord.name, queryRecord.userId, queryRecord.topicId, queryRecord.queryContents, queryRecord.stewardResponse.name, queryRecord.date) ) } val allUserQuery = TableQuery[UserTable] val allTopicQuery = TableQuery[TopicTable] val allQueryTable = TableQuery[QueryTable] val allUserTopicQuery = TableQuery[UserTopicTable] val mostRecentTopicQuery: Query[TopicTable, TopicRecord, Seq] = for( topic <- allTopicQuery if !allTopicQuery.filter(_.id === topic.id).filter(_.changeDate > topic.changeDate).exists ) yield topic } object StewardSchema { val allConfig:Config = StewardConfigSource.config val config:Config = allConfig.getConfig("shrine.steward.database") val slickProfileClassName = config.getString("slickProfileClassName") val slickProfile:JdbcProfile = StewardConfigSource.objectForName(slickProfileClassName) val schema = StewardSchema(slickProfile) } object StewardDatabase { val dataSource:DataSource = TestableDataSourceCreator.dataSource(StewardSchema.config) val db = StewardDatabase(StewardSchema.schema,dataSource) val createTablesOnStart = StewardSchema.config.getBoolean("createTablesOnStart") if(createTablesOnStart) StewardDatabase.db.createTables() } //API help sealed case class SortOrder(name:String){ import slick.lifted.ColumnOrdered def orderForColumn[T](column:ColumnOrdered[T]):ColumnOrdered[T] = { if(this == SortOrder.ascending) column.asc else column.desc } } object SortOrder { val ascending = SortOrder("ascending") val descending = SortOrder("descending") val sortOrders = Seq(ascending,descending) val namesToSortOrders = sortOrders.map(x => (x.name,x)).toMap def sortOrderForStringOption(option:Option[String]) = option.fold(ascending)(namesToSortOrders(_)) } case class QueryParameters(researcherIdOption:Option[UserName] = None, stateOption:Option[TopicState] = None, skipOption:Option[Int] = None, limitOption:Option[Int] = None, sortByOption:Option[String] = None, sortOrder:SortOrder = SortOrder.ascending, minDate:Option[Date] = None, maxDate:Option[Date] = None ) //DAO case classes, exposed for testing only case class ShrineQueryRecord(stewardId: Option[StewardQueryId], externalId:ExternalQueryId, name:String, userId:UserName, topicId:Option[TopicId], queryContents: QueryContents, stewardResponse:TopicState, date:Date) { def createOutboundShrineQuery(outboundTopic:Option[OutboundTopic],outboundUser:OutboundUser): OutboundShrineQuery = { - OutboundShrineQuery(stewardId.get,externalId,name,outboundUser,outboundTopic,queryContents,stewardResponse.name,date) + OutboundShrineQuery(stewardId.get,externalId,name,outboundUser,outboundTopic,scala.xml.XML.loadString(queryContents),stewardResponse.name,date) } } object ShrineQueryRecord extends ((Option[StewardQueryId],ExternalQueryId,String,UserName,Option[TopicId],QueryContents,TopicState,Date) => ShrineQueryRecord) { def apply(userId:UserName,topicId:Option[TopicId],shrineQuery: InboundShrineQuery,stewardResponse:TopicState): ShrineQueryRecord = { ShrineQueryRecord( None, shrineQuery.externalId, shrineQuery.name, userId, topicId, shrineQuery.queryContents, stewardResponse, System.currentTimeMillis()) } } case class UserRecord(userName:UserName,fullName:String,isSteward:Boolean) { lazy val asOutboundUser:OutboundUser = OutboundUser(userName,fullName,if(isSteward) Set(stewardRole,researcherRole) else Set(researcherRole)) } object UserRecord extends ((UserName,String,Boolean) => UserRecord) { def apply(user:User):UserRecord = UserRecord(user.username,user.fullName,user.params.toList.contains((stewardRole,"true"))) } case class TopicRecord(id:Option[TopicId] = None, name:String, description:String, createdBy:UserName, createDate:Date, state:TopicState, changedBy:UserName, changeDate:Date) { def toOutboundTopic(userNamesToOutboundUsers: Map[UserName, OutboundUser]): OutboundTopic = { OutboundTopic(id.get, name, description, userNamesToOutboundUsers(createdBy), createDate, state.name, userNamesToOutboundUsers(changedBy), changeDate) } } object TopicRecord { def apply(id:Option[TopicId], name:String, description:String, createdBy:UserName, createDate:Date, state:TopicState ):TopicRecord = TopicRecord(id, name, description, createdBy, createDate, state, createdBy, createDate) } case class UserTopicRecord(researcher:UserName, topicId:TopicId, state:TopicState, changedBy:UserName, changeDate:Date) case class TopicDoesNotExist(topicId:TopicId) extends IllegalArgumentException(s"No topic for id $topicId") case class ApprovedTopicCanNotBeChanged(topicId:TopicId) extends IllegalStateException(s"Topic $topicId has been ${TopicState.approved}") case class DetectedAttemptByWrongUserToChangeTopic(topicId:TopicId,userId:UserName,ownerId:UserName) extends IllegalArgumentException(s"$userId does not own $topicId; $ownerId owns it.") \ No newline at end of file diff --git a/apps/steward-app/src/test/resources/drevil_all_topics.json b/apps/steward-app/src/test/resources/drevil_all_topics.json new file mode 100644 index 000000000..db7789f77 --- /dev/null +++ b/apps/steward-app/src/test/resources/drevil_all_topics.json @@ -0,0 +1 @@ +{"totalCount": 144, "skipped": 0, "queryRecords": [{"name": "(042)-R3: B-Male@16:00:47", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 2648707654509368797, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587248893, "stewardId": 400, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE500\\RE515\\161\\"]}}}, "name": "(042)-R3: B-Male@16:00:47"}}}, {"name": "Aceta-R3: B-Male@16:01:13", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 3600437163069893043, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587275020, "stewardId": 401, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\RE000\\RE500\\RE515\\161\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Aceta-R3: B-Male@16:01:13"}}}, {"name": "(042)-R3: B-Male@16:02:11", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 6263453912784003804, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587332322, "stewardId": 402, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1886\\"]}}}, "name": "(042)-R3: B-Male@16:02:11"}}}, {"name": "Caffe-R3: B-Male@16:02:29", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 4379134945899344034, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587350694, "stewardId": 403, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1886\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Caffe-R3: B-Male@16:02:29"}}}, {"name": "(042)-R3: B-Male@16:02:53", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 8443382336139319396, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587374744, "stewardId": 404, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1191\\"]}}}, "name": "(042)-R3: B-Male@16:02:53"}}}, {"name": "Aspir-R3: B-Male@16:03:07", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 6855029845924574963, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587388451, "stewardId": 405, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1191\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Aspir-R3: B-Male@16:03:07"}}}, {"name": "(042)-R3: B-Male@16:03:39", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 898006392648873638, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587420556, "stewardId": 406, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\"]}}}, "name": "(042)-R3: B-Male@16:03:39"}}}, {"name": "Acarb-R3: B-Male@16:03:47", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 5208850828805054186, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587429005, "stewardId": 407, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Acarb-R3: B-Male@16:03:47"}}}, {"name": "(042)-R3: B-Male@16:04:09", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 7425009544481708263, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587450208, "stewardId": 408, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\"]}}}, "name": "(042)-R3: B-Male@16:04:09"}}}, {"name": "migli-R3: B-Male@16:04:18", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 1750973852506559058, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587459931, "stewardId": 409, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "migli-R3: B-Male@16:04:18"}}}, {"name": "(042)-R3: B-Male@16:04:47", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 4360123140814019270, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587488444, "stewardId": 410, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\"]}}}, "name": "(042)-R3: B-Male@16:04:47"}}}, {"name": "Metfo-R3: B-Male@16:04:54", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 4898171657067570139, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587495732, "stewardId": 411, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Metfo-R3: B-Male@16:04:54"}}}, {"name": "(042)-R3: B-Male@16:05:17", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 3855008654492067286, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587518220, "stewardId": 412, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\"]}}}, "name": "(042)-R3: B-Male@16:05:17"}}}, {"name": "Diabe-R3: B-Male@16:05:31", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 9110192329738944196, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587532485, "stewardId": 413, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Diabe-R3: B-Male@16:05:31"}}}, {"name": "(042)-R3: B-Male@16:05:50", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 5009858272093989240, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587552142, "stewardId": 414, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\"]}}}, "name": "(042)-R3: B-Male@16:05:50"}}}, {"name": "Diabe-R3: B-Male@16:06:05", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 188134167455130135, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587566709, "stewardId": 415, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Diabe-R3: B-Male@16:06:05"}}}, {"name": "(042)-R3: B-Male@16:06:28", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 479399567250300927, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587589642, "stewardId": 416, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\"]}}}, "name": "(042)-R3: B-Male@16:06:28"}}}, {"name": "Essen-R3: B-Male@16:06:37", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 1393339008803755226, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587598597, "stewardId": 417, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Essen-R3: B-Male@16:06:37"}}}, {"name": "(042)-R3: B-Male@16:07:11", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 2736226359752105487, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587632763, "stewardId": 418, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS501\\"]}}}, "name": "(042)-R3: B-Male@16:07:11"}}}, {"name": "INSUL-R3: B-Male@16:07:18", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 34685365704959618, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587640042, "stewardId": 419, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS501\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "INSUL-R3: B-Male@16:07:18"}}}, {"name": "(042)-R3: B-Male@16:07:43", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 5930491999925735746, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587665091, "stewardId": 420, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE100\\RE102\\237159\\"]}}}, "name": "(042)-R3: B-Male@16:07:43"}}}, {"name": "(042)-R3: B-Male@16:08:16", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 5294063509653529393, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587698142, "stewardId": 421, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE100\\RE104\\689\\"]}}}, "name": "(042)-R3: B-Male@16:08:16"}}}, {"name": "Amino-R3: B-Male@16:08:24", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 7522843409011142797, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587705729, "stewardId": 422, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\RE000\\RE100\\RE104\\689\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Amino-R3: B-Male@16:08:24"}}}, {"name": "Leval-R3: B-Male@16:09:05", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 5042684938741359724, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587747285, "stewardId": 423, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\RE000\\RE100\\RE102\\237159\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Leval-R3: B-Male@16:09:05"}}}, {"name": "(042)-R3: B-Male@16:09:42", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 1637595037100529031, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587783673, "stewardId": 424, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV800\\29046\\"]}}}, "name": "(042)-R3: B-Male@16:09:42"}}}, {"name": "Lisin-R3: B-Male@16:09:49", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 7935695322045969509, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587790761, "stewardId": 425, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\CV000\\CV800\\29046\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Lisin-R3: B-Male@16:09:49"}}}, {"name": "(042)-R3: B-Male@16:10:12", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 7947266617644567801, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587814104, "stewardId": 426, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\1202\\"]}}}, "name": "(042)-R3: B-Male@16:10:12"}}}, {"name": "Ateno-R3: B-Male@16:10:27", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 8517141999061339732, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587828743, "stewardId": 427, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\zzzz\\1202\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Ateno-R3: B-Male@16:10:27"}}}, {"name": "(042)-R3: B-Male@16:11:14", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 5452747806402491855, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587875878, "stewardId": 428, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV100\\6918\\"]}}}, "name": "(042)-R3: B-Male@16:11:14"}}}, {"name": "Metop-R3: B-Male@16:11:24", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 8916567601376587417, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587886065, "stewardId": 429, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\CV000\\CV100\\6918\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Metop-R3: B-Male@16:11:24"}}}, {"name": "(042)-R3: B-Male@16:11:51", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 8426729552377400851, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587912843, "stewardId": 430, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\"]}}}, "name": "(042)-R3: B-Male@16:11:51"}}}, {"name": "Chlor-R3: B-Male@16:11:57", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 2193440541060065083, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587918833, "stewardId": 431, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Chlor-R3: B-Male@16:11:57"}}}, {"name": "(042)-R3: B-Male@16:12:30", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 2979489468483322948, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587951792, "stewardId": 432, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\5487\\"]}}}, "name": "(042)-R3: B-Male@16:12:30"}}}, {"name": "Hydro-R3: B-Male@16:12:41", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 4838763513260705509, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587963185, "stewardId": 433, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\5487\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Hydro-R3: B-Male@16:12:41"}}}, {"name": "(042)-R3: B-Male@16:13:12", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 9109188960241130983, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587993669, "stewardId": 434, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2409\\"]}}}, "name": "(042)-R3: B-Male@16:13:12"}}}, {"name": "Chlor-R3: B-Male@16:13:20", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 5257445899619620687, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472588001549, "stewardId": 435, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2409\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Chlor-R3: B-Male@16:13:20"}}}, {"name": "Micro-R3: B-Male@16:16:02", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 4838907723642462383, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472588164188, "stewardId": 436, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE500\\RE515\\161\\"]}}}, "name": "Micro-R3: B-Male@16:16:02"}}}, {"name": "Aceta-R3: B-Male@16:16:15", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 4000856234841888811, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472588176634, "stewardId": 437, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\RE000\\RE500\\RE515\\161\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Aceta-R3: B-Male@16:16:15"}}}, {"name": "Micro-R3: B-Male@16:16:54", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 4856772701929267242, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472588216305, "stewardId": 438, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1886\\"]}}}, "name": "Micro-R3: B-Male@16:16:54"}}}, {"name": "Caffe-R3: B-Male@16:17:05", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 5364490122711025417, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472588226273, "stewardId": 439, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1886\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Caffe-R3: B-Male@16:17:05"}}}, {"name": "Micro-R3: B-Male@16:17:42", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 1639997933354043907, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472588263531, "stewardId": 440, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1191\\"]}}}, "name": "Micro-R3: B-Male@16:17:42"}}}, {"name": "Aspir-R3: B-Male@16:59:09", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 1051554701594800060, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472590750632, "stewardId": 441, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1191\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Aspir-R3: B-Male@16:59:09"}}}, {"name": "Micro-R3: B-Male@17:02:05", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 114209128242947284, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472590926927, "stewardId": 442, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\"]}}}, "name": "Micro-R3: B-Male@17:02:05"}}}, {"name": "Acarb-R3: B-Male@17:02:13", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 5911637414172083145, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472590934318, "stewardId": 443, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Acarb-R3: B-Male@17:02:13"}}}, {"name": "Micro-R3: B-Male@17:02:41", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 3571439681561867514, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472590962252, "stewardId": 444, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\"]}}}, "name": "Micro-R3: B-Male@17:02:41"}}}, {"name": "migli-R3: B-Male@17:02:49", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 372697878341761973, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472590970798, "stewardId": 445, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "migli-R3: B-Male@17:02:49"}}}, {"name": "Micro-R3: B-Male@17:03:17", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 3210074767580567445, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472590998950, "stewardId": 446, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\"]}}}, "name": "Micro-R3: B-Male@17:03:17"}}}, {"name": "Metfo-R3: B-Male@17:03:23", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 3687986857788966187, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591005320, "stewardId": 447, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Metfo-R3: B-Male@17:03:23"}}}, {"name": "Micro-R3: B-Male@17:03:45", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 6061304924090295649, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591026100, "stewardId": 448, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\"]}}}, "name": "Micro-R3: B-Male@17:03:45"}}}, {"name": "Diabe-R3: B-Male@17:03:51", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 2183514302797849167, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591033257, "stewardId": 449, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Diabe-R3: B-Male@17:03:51"}}}, {"name": "Micro-R3: B-Male@17:04:15", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 1561488252968533795, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591056617, "stewardId": 450, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\"]}}}, "name": "Micro-R3: B-Male@17:04:15"}}}, {"name": "Diabe-R3: B-Male@17:04:25", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 4679957608524541399, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591066354, "stewardId": 451, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Diabe-R3: B-Male@17:04:25"}}}, {"name": "Micro-R3: B-Male@17:04:47", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 8234213651826662449, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591088484, "stewardId": 452, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\"]}}}, "name": "Micro-R3: B-Male@17:04:47"}}}, {"name": "Essen-R3: B-Male@17:04:52", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 755249573696607065, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591093801, "stewardId": 453, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Essen-R3: B-Male@17:04:52"}}}, {"name": "Micro-R3: B-Male@17:05:13", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 5552648692199691952, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591114898, "stewardId": 454, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS501\\"]}}}, "name": "Micro-R3: B-Male@17:05:13"}}}, {"name": "INSUL-R3: B-Male@17:05:23", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 7989341800224959369, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591125020, "stewardId": 455, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS501\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "INSUL-R3: B-Male@17:05:23"}}}, {"name": "Micro-R3: B-Male@17:07:06", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 1046266465768393777, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591228221, "stewardId": 456, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE100\\RE102\\237159\\"]}}}, "name": "Micro-R3: B-Male@17:07:06"}}}, {"name": "Leval-R3: B-Male@17:07:13", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 9200595174307939802, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591235506, "stewardId": 457, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\RE000\\RE100\\RE102\\237159\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Leval-R3: B-Male@17:07:13"}}}, {"name": "Micro-R3: B-Male@17:08:09", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 6959851371434112548, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591290721, "stewardId": 458, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV800\\29046\\"]}}}, "name": "Micro-R3: B-Male@17:08:09"}}}, {"name": "Lisin-R3: B-Male@17:08:22", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 4746832498654789977, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591303552, "stewardId": 459, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\CV000\\CV800\\29046\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Lisin-R3: B-Male@17:08:22"}}}, {"name": "Micro-R3: B-Male@17:08:38", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 8108079039160737023, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591319854, "stewardId": 460, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\1202\\"]}}}, "name": "Micro-R3: B-Male@17:08:38"}}}, {"name": "Ateno-R3: B-Male@17:08:48", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 554635661664219990, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591330029, "stewardId": 461, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\zzzz\\1202\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Ateno-R3: B-Male@17:08:48"}}}, {"name": "Micro-R3: B-Male@17:09:03", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 2606299132071671313, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591344970, "stewardId": 462, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV100\\6918\\"]}}}, "name": "Micro-R3: B-Male@17:09:03"}}}, {"name": "Metop-R3: B-Male@17:09:21", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 6077163558340746294, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591362864, "stewardId": 463, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\CV000\\CV100\\6918\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Metop-R3: B-Male@17:09:21"}}}, {"name": "Micro-R3: B-Male@17:09:37", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 8677293011945386124, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591378441, "stewardId": 464, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\"]}}}, "name": "Micro-R3: B-Male@17:09:37"}}}, {"name": "Chlor-R3: B-Male@17:10:53", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 5435806514319896029, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591454643, "stewardId": 465, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Chlor-R3: B-Male@17:10:53"}}}, {"name": "Chlor-R3: B-Male@17:11:13", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 6269112036394442108, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591474992, "stewardId": 466, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Anophthalmos (743.0)\\"]}}}, "name": "Chlor-R3: B-Male@17:11:13"}}}, {"name": "Anoph-R3: B-Male@17:11:24", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 3610131085635691220, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591486085, "stewardId": 467, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Anophthalmos (743.0)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Anoph-R3: B-Male@17:11:24"}}}, {"name": "Anoph-R3: B-Male@17:11:55", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 5782621780167804633, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591521346, "stewardId": 468, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Anophthalmos (743.0)\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV100\\6918\\"]}}}, "name": "Anoph-R3: B-Male@17:11:55"}}}, {"name": "Anoph-R3: B-Male@17:13:07", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 8089643168948492294, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591588373, "stewardId": 469, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Anophthalmos (743.0)\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV800\\29046\\"]}}}, "name": "Anoph-R3: B-Male@17:13:07"}}}, {"name": "Other maternal @10:03:20", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 438512722186084961, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472652202338, "stewardId": 470, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Other maternal and fetal complications (678-679.99)\\"}, "name": "Other maternal @10:03:20"}}}, {"name": "Hypertonic, inc@10:03:57", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 2331305839012106062, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472652238459, "stewardId": 471, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Hypertonic, incoordinate, or prolonged uterine contractions (661.4)\\"}, "name": "Hypertonic, inc@10:03:57"}}}, {"name": "Precipitate lab@10:04:32", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 4535951599211583429, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472652273378, "stewardId": 472, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Precipitate labor (661.3)\\"}, "name": "Precipitate lab@10:04:32"}}}, {"name": "Cesarean delive@10:04:52", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 5643525925200359244, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472652293830, "stewardId": 473, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Other complications of labor and delivery, not elsewhere classified (669)\\Cesarean delivery, without mention of indication (669.7)\\"}, "name": "Cesarean delive@10:04:52"}}}, {"name": "Forceps or vacu@10:05:33", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 1084347425622270993, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472652335220, "stewardId": 474, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Other complications of labor and delivery, not elsewhere classified (669)\\Forceps or vacuum extractor delivery without mention of indication (669.5)\\"}, "name": "Forceps or vacu@10:05:33"}}}, {"name": "Abnormality of @10:08:51", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 254709938122300093, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472652536448, "stewardId": 475, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\"}, "name": "Abnormality of @10:08:51"}}}, {"name": "Hypertonic, inc@10:09:28", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 9139901314049343764, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472652570019, "stewardId": 476, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Hypertonic, incoordinate, or prolonged uterine contractions (661.4)\\"}, "name": "Hypertonic, inc@10:09:28"}}}, {"name": "Other and unspe@10:11:07", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 1712052448050515701, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472652669754, "stewardId": 477, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Other and unspecified uterine inertia (661.2)\\"}, "name": "Other and unspe@10:11:07"}}}, {"name": "Primary uterine@10:11:24", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 932158704885655628, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472652686393, "stewardId": 478, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Primary uterine inertia (661.0)\\"}, "name": "Primary uterine@10:11:24"}}}, {"name": "Unspecified abn@10:53:04", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 2171121096823526348, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655186002, "stewardId": 479, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Unspecified abnormality of labor (661.9)\\"}, "name": "Unspecified abn@10:53:04"}}}, {"name": "Pulmonary compl@10:53:20", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 6612011731372599443, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655201353, "stewardId": 480, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Complications of the administration of anesthetic or other sedation in labor and delivery (668)\\Pulmonary complications of anesthesia or other sedation in labor and delivery (668.0)\\"}, "name": "Pulmonary compl@10:53:20"}}}, {"name": "Deep transverse@10:53:33", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 8925248588560705491, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655215214, "stewardId": 481, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Obstructed labor (660)\\Deep transverse arrest and persistent occipitoposterior position during labor and delivery (660.3)\\"}, "name": "Deep transverse@10:53:33"}}}, {"name": "Failed forceps @10:53:47", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 6509861914548584297, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655228979, "stewardId": 482, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Obstructed labor (660)\\Failed forceps or vacuum extractor, unspecified (660.7)\\"}, "name": "Failed forceps @10:53:47"}}}, {"name": "Legally induced@10:54:01", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 7842474969127688299, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655242650, "stewardId": 483, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Other pregnancy with abortive outcome (634-639.99)\\Legally induced abortion (635)\\"}, "name": "Legally induced@10:54:01"}}}, {"name": "Obstruction cau@10:54:17", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 4990482595786845002, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655258742, "stewardId": 484, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Obstructed labor (660)\\Obstruction caused by malposition of fetus at onset of labor (660.0)\\"}, "name": "Obstruction cau@10:54:17"}}}, {"name": "Complications o@10:54:30", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 8329894087176016412, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655271406, "stewardId": 485, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\"}, "name": "Complications o@10:54:30"}}}, {"name": "Failed trial of@10:54:53", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 4737401618560282720, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655294806, "stewardId": 486, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Obstructed labor (660)\\Failed trial of labor, unspecified (660.6)\\"}, "name": "Failed trial of@10:54:53"}}}, {"name": "Hypertonic, inc@10:56:30", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 2406312754995701454, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655391454, "stewardId": 487, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Hypertonic, incoordinate, or prolonged uterine contractions (661.4)\\"}, "name": "Hypertonic, inc@10:56:30"}}}, {"name": "Precipitate lab@10:56:45", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 6399485767031026748, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655406680, "stewardId": 488, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Precipitate labor (661.3)\\"}, "name": "Precipitate lab@10:56:45"}}}, {"name": "Precipi-(754.0)@10:57:17", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 7087687189896824207, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655439442, "stewardId": 489, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Precipitate labor (661.3)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "Precipi-(754.0)@10:57:17"}}}, {"name": "(754.0)-Cesarea@10:57:54", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 1488614521209287834, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655475772, "stewardId": 490, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Other complications of labor and delivery, not elsewhere classified (669)\\Cesarean delivery, without mention of indication (669.7)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Cesarea@10:57:54"}}}, {"name": "(754.0)-Forceps@10:58:16", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 6904170937440497764, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655497924, "stewardId": 491, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Other complications of labor and delivery, not elsewhere classified (669)\\Forceps or vacuum extractor delivery without mention of indication (669.5)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Forceps@10:58:16"}}}, {"name": "(754.0)-Abnorma@11:05:14", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 6582492753700411830, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655915653, "stewardId": 492, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Abnorma@11:05:14"}}}, {"name": "(754.0)-Hyperto@11:05:45", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 1220904381720167210, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655946565, "stewardId": 493, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Hypertonic, incoordinate, or prolonged uterine contractions (661.4)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Hyperto@11:05:45"}}}, {"name": "(754.0)-Other a@11:06:01", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 1662792196575435192, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655962257, "stewardId": 494, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Other and unspecified uterine inertia (661.2)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Other a@11:06:01"}}}, {"name": "(754.0)-Primary@11:06:13", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 9211643082625991431, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655975182, "stewardId": 495, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Primary uterine inertia (661.0)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Primary@11:06:13"}}}, {"name": "(754.0)-Unspeci@11:06:27", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 7801071439031281460, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655988783, "stewardId": 496, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Unspecified abnormality of labor (661.9)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Unspeci@11:06:27"}}}, {"name": "(754.0)-Pulmona@11:06:42", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 5625673261436757322, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656003521, "stewardId": 497, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Complications of the administration of anesthetic or other sedation in labor and delivery (668)\\Pulmonary complications of anesthesia or other sedation in labor and delivery (668.0)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Pulmona@11:06:42"}}}, {"name": "(754.0)-Deep tr@11:07:15", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 3292028783225861116, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656036696, "stewardId": 498, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Obstructed labor (660)\\Deep transverse arrest and persistent occipitoposterior position during labor and delivery (660.3)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Deep tr@11:07:15"}}}, {"name": "(754.0)-Failed @11:07:37", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 8170097484723196535, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656059037, "stewardId": 499, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Obstructed labor (660)\\Failed forceps or vacuum extractor, unspecified (660.7)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Failed @11:07:37"}}}, {"name": "(740.0)-Cesarea@11:08:15", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 5380239451968470447, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656096682, "stewardId": 500, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Other complications of labor and delivery, not elsewhere classified (669)\\Cesarean delivery, without mention of indication (669.7)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.0) Anencephalus\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.1) Craniorachischisis\\"]}}}, "name": "(740.0)-Cesarea@11:08:15"}}}, {"name": "(740.0)-Forceps@11:08:28", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 7811264639452279144, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656110090, "stewardId": 501, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Other complications of labor and delivery, not elsewhere classified (669)\\Forceps or vacuum extractor delivery without mention of indication (669.5)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.0) Anencephalus\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.1) Craniorachischisis\\"]}}}, "name": "(740.0)-Forceps@11:08:28"}}}, {"name": "(740.0)-Abnorma@11:08:40", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 4977265009312036640, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656121445, "stewardId": 502, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.0) Anencephalus\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.1) Craniorachischisis\\"]}}}, "name": "(740.0)-Abnorma@11:08:40"}}}, {"name": "(740.0)-Hyperto@11:08:52", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 8360282821525601865, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656133635, "stewardId": 503, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Hypertonic, incoordinate, or prolonged uterine contractions (661.4)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.0) Anencephalus\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.1) Craniorachischisis\\"]}}}, "name": "(740.0)-Hyperto@11:08:52"}}}, {"name": "(740.0)-Other a@11:09:05", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 5494801448121929467, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656146743, "stewardId": 504, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Other and unspecified uterine inertia (661.2)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.0) Anencephalus\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.1) Craniorachischisis\\"]}}}, "name": "(740.0)-Other a@11:09:05"}}}, {"name": "(740.0)-Primary@11:09:17", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 8470228705289450818, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656158815, "stewardId": 505, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Primary uterine inertia (661.0)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.0) Anencephalus\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.1) Craniorachischisis\\"]}}}, "name": "(740.0)-Primary@11:09:17"}}}, {"name": "(740.0)-Unspeci@11:09:28", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 1567726413236597662, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656169392, "stewardId": 506, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Unspecified abnormality of labor (661.9)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.0) Anencephalus\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.1) Craniorachischisis\\"]}}}, "name": "(740.0)-Unspeci@11:09:28"}}}, {"name": "Diabe-R3: B-Aceta@11:16:06", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 5174467797346328639, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656568233, "stewardId": 507, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE500\\RE515\\161\\"]}}, "name": "Diabe-R3: B-Aceta@11:16:06"}}}, {"name": "Diabe-R3: B-Aspir@11:16:23", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 2794376855051886671, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656585152, "stewardId": 508, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1191\\"]}}, "name": "Diabe-R3: B-Aspir@11:16:23"}}}, {"name": "Diabe-R3: B-Caffe@11:16:55", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 1044427930106308584, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656616859, "stewardId": 509, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1886\\"]}}, "name": "Diabe-R3: B-Caffe@11:16:55"}}}, {"name": "Diabe-R3: B-Acarb@11:17:12", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 7418005693351346004, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656633476, "stewardId": 510, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\"]}}, "name": "Diabe-R3: B-Acarb@11:17:12"}}}, {"name": "Diabe-R3: B-migli@11:17:28", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 695924216927184648, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656649645, "stewardId": 511, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\"]}}, "name": "Diabe-R3: B-migli@11:17:28"}}}, {"name": "Diabe-R3: B-Metfo@11:17:46", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 7888563857135844160, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656667842, "stewardId": 512, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\"]}}, "name": "Diabe-R3: B-Metfo@11:17:46"}}}, {"name": "Diabe-R3: B-Aceta@11:18:32", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 5168650818913981331, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656713441, "stewardId": 513, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE500\\RE515\\161\\"]}}, "name": "Diabe-R3: B-Aceta@11:18:32"}}}, {"name": "Diabe-R3: B-Aspir@11:18:58", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 7844324309055147065, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656739015, "stewardId": 514, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1191\\"]}}, "name": "Diabe-R3: B-Aspir@11:18:58"}}}, {"name": "Diabe-R3: B-Caffe@11:19:10", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 794591131061932003, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656751714, "stewardId": 515, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1886\\"]}}, "name": "Diabe-R3: B-Caffe@11:19:10"}}}, {"name": "Diabe-R3: B-Acarb@11:19:26", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 8280687518804420793, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656767637, "stewardId": 516, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\"]}}, "name": "Diabe-R3: B-Acarb@11:19:26"}}}, {"name": "Diabe-R3: B-migli@11:20:08", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 589945805809937476, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656810210, "stewardId": 517, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\"]}}, "name": "Diabe-R3: B-migli@11:20:08"}}}, {"name": "Diabe-R3: B-Metfo@11:22:35", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 583418508499046891, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656956754, "stewardId": 518, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\"]}}, "name": "Diabe-R3: B-Metfo@11:22:35"}}}, {"name": "Diabe-R3: B-Chlor@11:23:40", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 7203792399675073634, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657021649, "stewardId": 519, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\"]}}, "name": "Diabe-R3: B-Chlor@11:23:40"}}}, {"name": "Essen-R3: B-Chlor@11:24:29", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 7362147804788597147, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657071188, "stewardId": 520, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\"]}}, "name": "Essen-R3: B-Chlor@11:24:29"}}}, {"name": "Essen-R3: B-Lisin@11:24:46", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 8037011930011168290, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657087340, "stewardId": 521, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV800\\29046\\"]}}, "name": "Essen-R3: B-Lisin@11:24:46"}}}, {"name": "Essen-R3: B-Ateno@11:24:57", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 6472709426375569353, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657099117, "stewardId": 522, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\1202\\"]}}, "name": "Essen-R3: B-Ateno@11:24:57"}}}, {"name": "Essen-R3: B-Metop@11:25:11", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 872766527547291191, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657112978, "stewardId": 523, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV100\\6918\\"]}}, "name": "Essen-R3: B-Metop@11:25:11"}}}, {"name": "Essen-R3: B-Hydro@11:25:26", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 3586929774720426387, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657127365, "stewardId": 524, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\5487\\"]}}, "name": "Essen-R3: B-Hydro@11:25:26"}}}, {"name": "Essen-R3: B-Chlor@11:25:40", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 470833085071994268, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657142257, "stewardId": 525, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2409\\"]}}, "name": "Essen-R3: B-Chlor@11:25:40"}}}, {"name": "Ess-R3:-mig-Chl@11:26:10", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 4945624012203499502, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657171564, "stewardId": 526, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\"]}}, "name": "Ess-R3:-mig-Chl@11:26:10"}}}, {"name": "Ess-R3:-mig-Lis@11:26:24", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 6287374830168751593, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657185503, "stewardId": 527, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV800\\29046\\"]}}, "name": "Ess-R3:-mig-Lis@11:26:24"}}}, {"name": "Ess-R3:-mig-Ate@11:26:36", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 1179439889529872745, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657198049, "stewardId": 528, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\1202\\"]}}, "name": "Ess-R3:-mig-Ate@11:26:36"}}}, {"name": "Ess-R3:-mig-Met@11:26:57", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 4711862544928567957, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657219348, "stewardId": 529, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV100\\6918\\"]}}, "name": "Ess-R3:-mig-Met@11:26:57"}}}, {"name": "Ess-R3:-mig-Hyd@11:27:16", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 8632446537331239933, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657237911, "stewardId": 530, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\5487\\"]}}, "name": "Ess-R3:-mig-Hyd@11:27:16"}}}, {"name": "Ess-R3:-mig-Chl@11:27:33", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 4730527150986487561, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657254820, "stewardId": 531, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2409\\"]}}, "name": "Ess-R3:-mig-Chl@11:27:33"}}}, {"name": "Ess-R3:-Met-Chl@11:28:04", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 4419356379262081231, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657286321, "stewardId": 532, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\"]}}, "name": "Ess-R3:-Met-Chl@11:28:04"}}}, {"name": "Ess-R3:-Met-Lis@11:28:20", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 1021398333063898808, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657301814, "stewardId": 533, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV800\\29046\\"]}}, "name": "Ess-R3:-Met-Lis@11:28:20"}}}, {"name": "Ess-R3:-Met-Ate@11:28:36", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 2899915565944317229, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657317705, "stewardId": 534, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\1202\\"]}}, "name": "Ess-R3:-Met-Ate@11:28:36"}}}, {"name": "Ess-R3:-Met-Met@11:28:53", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 452016522880455733, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657334572, "stewardId": 535, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV100\\6918\\"]}}, "name": "Ess-R3:-Met-Met@11:28:53"}}}, {"name": "Ess-R3:-Met-Hyd@11:29:08", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 224710722022226306, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657349518, "stewardId": 536, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\5487\\"]}}, "name": "Ess-R3:-Met-Hyd@11:29:08"}}}, {"name": "Ess-R3:-Met-Chl@11:29:22", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 3935574894037045539, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657364111, "stewardId": 537, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2409\\"]}}, "name": "Ess-R3:-Met-Chl@11:29:22"}}}, {"name": "Ess-R3:-Met-Chl@11:29:42", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 597323415907564732, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657384315, "stewardId": 538, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\"]}}, "name": "Ess-R3:-Met-Chl@11:29:42"}}}, {"name": "Ess-R3:-Aca-Lis@11:36:14", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 5944191251893031620, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657775334, "stewardId": 539, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV800\\29046\\"]}}, "name": "Ess-R3:-Aca-Lis@11:36:14"}}}, {"name": "Ess-R3:-Aca-Ate@11:36:28", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 6680919560460432205, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657789699, "stewardId": 540, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\1202\\"]}}, "name": "Ess-R3:-Aca-Ate@11:36:28"}}}, {"name": "Ess-R3:-Aca-Met@11:36:42", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 9091099492570587120, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657804003, "stewardId": 541, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV100\\6918\\"]}}, "name": "Ess-R3:-Aca-Met@11:36:42"}}}, {"name": "Ess-R3:-Aca-Hyd@11:36:57", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 8707285113581419320, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657818752, "stewardId": 542, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\5487\\"]}}, "name": "Ess-R3:-Aca-Hyd@11:36:57"}}}, {"name": "Ess-R3:-Aca-Chl@11:37:11", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 5475903983983046083, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657833014, "stewardId": 543, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2409\\"]}}, "name": "Ess-R3:-Aca-Chl@11:37:11"}}}]} \ No newline at end of file diff --git a/apps/steward-app/src/test/resources/drevil_topic_18.json b/apps/steward-app/src/test/resources/drevil_topic_18.json new file mode 100644 index 000000000..0943b8ef2 --- /dev/null +++ b/apps/steward-app/src/test/resources/drevil_topic_18.json @@ -0,0 +1 @@ +{"totalCount": 36, "skipped": 0, "queryRecords": [{"name": "(042)-R3: B-Male@16:00:47", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 2648707654509368797, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587248893, "stewardId": 400, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE500\\RE515\\161\\"]}}}, "name": "(042)-R3: B-Male@16:00:47"}}}, {"name": "Aceta-R3: B-Male@16:01:13", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 3600437163069893043, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587275020, "stewardId": 401, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\RE000\\RE500\\RE515\\161\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Aceta-R3: B-Male@16:01:13"}}}, {"name": "(042)-R3: B-Male@16:02:11", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 6263453912784003804, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587332322, "stewardId": 402, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1886\\"]}}}, "name": "(042)-R3: B-Male@16:02:11"}}}, {"name": "Caffe-R3: B-Male@16:02:29", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 4379134945899344034, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587350694, "stewardId": 403, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1886\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Caffe-R3: B-Male@16:02:29"}}}, {"name": "(042)-R3: B-Male@16:02:53", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 8443382336139319396, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587374744, "stewardId": 404, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1191\\"]}}}, "name": "(042)-R3: B-Male@16:02:53"}}}, {"name": "Aspir-R3: B-Male@16:03:07", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 6855029845924574963, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587388451, "stewardId": 405, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1191\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Aspir-R3: B-Male@16:03:07"}}}, {"name": "(042)-R3: B-Male@16:03:39", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 898006392648873638, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587420556, "stewardId": 406, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\"]}}}, "name": "(042)-R3: B-Male@16:03:39"}}}, {"name": "Acarb-R3: B-Male@16:03:47", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 5208850828805054186, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587429005, "stewardId": 407, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Acarb-R3: B-Male@16:03:47"}}}, {"name": "(042)-R3: B-Male@16:04:09", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 7425009544481708263, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587450208, "stewardId": 408, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\"]}}}, "name": "(042)-R3: B-Male@16:04:09"}}}, {"name": "migli-R3: B-Male@16:04:18", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 1750973852506559058, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587459931, "stewardId": 409, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "migli-R3: B-Male@16:04:18"}}}, {"name": "(042)-R3: B-Male@16:04:47", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 4360123140814019270, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587488444, "stewardId": 410, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\"]}}}, "name": "(042)-R3: B-Male@16:04:47"}}}, {"name": "Metfo-R3: B-Male@16:04:54", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 4898171657067570139, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587495732, "stewardId": 411, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Metfo-R3: B-Male@16:04:54"}}}, {"name": "(042)-R3: B-Male@16:05:17", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 3855008654492067286, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587518220, "stewardId": 412, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\"]}}}, "name": "(042)-R3: B-Male@16:05:17"}}}, {"name": "Diabe-R3: B-Male@16:05:31", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 9110192329738944196, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587532485, "stewardId": 413, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Diabe-R3: B-Male@16:05:31"}}}, {"name": "(042)-R3: B-Male@16:05:50", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 5009858272093989240, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587552142, "stewardId": 414, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\"]}}}, "name": "(042)-R3: B-Male@16:05:50"}}}, {"name": "Diabe-R3: B-Male@16:06:05", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 188134167455130135, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587566709, "stewardId": 415, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Diabe-R3: B-Male@16:06:05"}}}, {"name": "(042)-R3: B-Male@16:06:28", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 479399567250300927, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587589642, "stewardId": 416, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\"]}}}, "name": "(042)-R3: B-Male@16:06:28"}}}, {"name": "Essen-R3: B-Male@16:06:37", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 1393339008803755226, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587598597, "stewardId": 417, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Essen-R3: B-Male@16:06:37"}}}, {"name": "(042)-R3: B-Male@16:07:11", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 2736226359752105487, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587632763, "stewardId": 418, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS501\\"]}}}, "name": "(042)-R3: B-Male@16:07:11"}}}, {"name": "INSUL-R3: B-Male@16:07:18", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 34685365704959618, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587640042, "stewardId": 419, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS501\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "INSUL-R3: B-Male@16:07:18"}}}, {"name": "(042)-R3: B-Male@16:07:43", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 5930491999925735746, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587665091, "stewardId": 420, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE100\\RE102\\237159\\"]}}}, "name": "(042)-R3: B-Male@16:07:43"}}}, {"name": "(042)-R3: B-Male@16:08:16", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 5294063509653529393, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587698142, "stewardId": 421, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE100\\RE104\\689\\"]}}}, "name": "(042)-R3: B-Male@16:08:16"}}}, {"name": "Amino-R3: B-Male@16:08:24", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 7522843409011142797, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587705729, "stewardId": 422, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\RE000\\RE100\\RE104\\689\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Amino-R3: B-Male@16:08:24"}}}, {"name": "Leval-R3: B-Male@16:09:05", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 5042684938741359724, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587747285, "stewardId": 423, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\RE000\\RE100\\RE102\\237159\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Leval-R3: B-Male@16:09:05"}}}, {"name": "(042)-R3: B-Male@16:09:42", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 1637595037100529031, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587783673, "stewardId": 424, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV800\\29046\\"]}}}, "name": "(042)-R3: B-Male@16:09:42"}}}, {"name": "Lisin-R3: B-Male@16:09:49", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 7935695322045969509, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587790761, "stewardId": 425, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\CV000\\CV800\\29046\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Lisin-R3: B-Male@16:09:49"}}}, {"name": "(042)-R3: B-Male@16:10:12", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 7947266617644567801, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587814104, "stewardId": 426, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\1202\\"]}}}, "name": "(042)-R3: B-Male@16:10:12"}}}, {"name": "Ateno-R3: B-Male@16:10:27", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 8517141999061339732, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587828743, "stewardId": 427, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\zzzz\\1202\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Ateno-R3: B-Male@16:10:27"}}}, {"name": "(042)-R3: B-Male@16:11:14", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 5452747806402491855, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587875878, "stewardId": 428, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV100\\6918\\"]}}}, "name": "(042)-R3: B-Male@16:11:14"}}}, {"name": "Metop-R3: B-Male@16:11:24", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 8916567601376587417, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587886065, "stewardId": 429, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\CV000\\CV100\\6918\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Metop-R3: B-Male@16:11:24"}}}, {"name": "(042)-R3: B-Male@16:11:51", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 8426729552377400851, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587912843, "stewardId": 430, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\"]}}}, "name": "(042)-R3: B-Male@16:11:51"}}}, {"name": "Chlor-R3: B-Male@16:11:57", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 2193440541060065083, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587918833, "stewardId": 431, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Chlor-R3: B-Male@16:11:57"}}}, {"name": "(042)-R3: B-Male@16:12:30", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 2979489468483322948, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587951792, "stewardId": 432, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\5487\\"]}}}, "name": "(042)-R3: B-Male@16:12:30"}}}, {"name": "Hydro-R3: B-Male@16:12:41", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 4838763513260705509, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587963185, "stewardId": 433, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\5487\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Hydro-R3: B-Male@16:12:41"}}}, {"name": "(042)-R3: B-Male@16:13:12", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 9109188960241130983, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472587993669, "stewardId": 434, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Infectious and parasitic diseases (001-139.99)\\Human immunodeficiency virus [hiv] infection (042-042.99)\\(042) Human immunodeficiency virus [HIV] disease\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2409\\"]}}}, "name": "(042)-R3: B-Male@16:13:12"}}}, {"name": "Chlor-R3: B-Male@16:13:20", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Understanding potential health impacts of increasingly prevalent chronic dietary related health conditions, on patients with HIV, specifically among African American populations - where prevalence of such chronic conditions is particularly high; and understanding any impact of medications for chronic conditions in management of HIV.", "createDate": 1472586655712, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 18, "changeDate": 1472587022939, "name": "Health impact of dietary related chronic diseases on HIV positive patients"}, "stewardResponse": "Approved", "externalId": 5257445899619620687, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472588001549, "stewardId": 435, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2409\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Chlor-R3: B-Male@16:13:20"}}}]} \ No newline at end of file diff --git a/apps/steward-app/src/test/resources/drevil_topic_19.json b/apps/steward-app/src/test/resources/drevil_topic_19.json new file mode 100644 index 000000000..34786a62f --- /dev/null +++ b/apps/steward-app/src/test/resources/drevil_topic_19.json @@ -0,0 +1 @@ +{"totalCount": 34, "skipped": 0, "queryRecords": [{"name": "Micro-R3: B-Male@16:16:02", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 4838907723642462383, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472588164188, "stewardId": 436, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE500\\RE515\\161\\"]}}}, "name": "Micro-R3: B-Male@16:16:02"}}}, {"name": "Aceta-R3: B-Male@16:16:15", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 4000856234841888811, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472588176634, "stewardId": 437, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\RE000\\RE500\\RE515\\161\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Aceta-R3: B-Male@16:16:15"}}}, {"name": "Micro-R3: B-Male@16:16:54", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 4856772701929267242, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472588216305, "stewardId": 438, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1886\\"]}}}, "name": "Micro-R3: B-Male@16:16:54"}}}, {"name": "Caffe-R3: B-Male@16:17:05", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 5364490122711025417, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472588226273, "stewardId": 439, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1886\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Caffe-R3: B-Male@16:17:05"}}}, {"name": "Micro-R3: B-Male@16:17:42", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 1639997933354043907, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472588263531, "stewardId": 440, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1191\\"]}}}, "name": "Micro-R3: B-Male@16:17:42"}}}, {"name": "Aspir-R3: B-Male@16:59:09", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 1051554701594800060, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472590750632, "stewardId": 441, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1191\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Aspir-R3: B-Male@16:59:09"}}}, {"name": "Micro-R3: B-Male@17:02:05", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 114209128242947284, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472590926927, "stewardId": 442, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\"]}}}, "name": "Micro-R3: B-Male@17:02:05"}}}, {"name": "Acarb-R3: B-Male@17:02:13", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 5911637414172083145, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472590934318, "stewardId": 443, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Acarb-R3: B-Male@17:02:13"}}}, {"name": "Micro-R3: B-Male@17:02:41", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 3571439681561867514, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472590962252, "stewardId": 444, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\"]}}}, "name": "Micro-R3: B-Male@17:02:41"}}}, {"name": "migli-R3: B-Male@17:02:49", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 372697878341761973, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472590970798, "stewardId": 445, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "migli-R3: B-Male@17:02:49"}}}, {"name": "Micro-R3: B-Male@17:03:17", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 3210074767580567445, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472590998950, "stewardId": 446, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\"]}}}, "name": "Micro-R3: B-Male@17:03:17"}}}, {"name": "Metfo-R3: B-Male@17:03:23", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 3687986857788966187, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591005320, "stewardId": 447, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Metfo-R3: B-Male@17:03:23"}}}, {"name": "Micro-R3: B-Male@17:03:45", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 6061304924090295649, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591026100, "stewardId": 448, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\"]}}}, "name": "Micro-R3: B-Male@17:03:45"}}}, {"name": "Diabe-R3: B-Male@17:03:51", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 2183514302797849167, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591033257, "stewardId": 449, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Diabe-R3: B-Male@17:03:51"}}}, {"name": "Micro-R3: B-Male@17:04:15", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 1561488252968533795, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591056617, "stewardId": 450, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\"]}}}, "name": "Micro-R3: B-Male@17:04:15"}}}, {"name": "Diabe-R3: B-Male@17:04:25", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 4679957608524541399, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591066354, "stewardId": 451, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Diabe-R3: B-Male@17:04:25"}}}, {"name": "Micro-R3: B-Male@17:04:47", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 8234213651826662449, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591088484, "stewardId": 452, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\"]}}}, "name": "Micro-R3: B-Male@17:04:47"}}}, {"name": "Essen-R3: B-Male@17:04:52", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 755249573696607065, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591093801, "stewardId": 453, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Essen-R3: B-Male@17:04:52"}}}, {"name": "Micro-R3: B-Male@17:05:13", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 5552648692199691952, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591114898, "stewardId": 454, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS501\\"]}}}, "name": "Micro-R3: B-Male@17:05:13"}}}, {"name": "INSUL-R3: B-Male@17:05:23", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 7989341800224959369, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591125020, "stewardId": 455, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS501\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "INSUL-R3: B-Male@17:05:23"}}}, {"name": "Micro-R3: B-Male@17:07:06", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 1046266465768393777, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591228221, "stewardId": 456, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE100\\RE102\\237159\\"]}}}, "name": "Micro-R3: B-Male@17:07:06"}}}, {"name": "Leval-R3: B-Male@17:07:13", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 9200595174307939802, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591235506, "stewardId": 457, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\RE000\\RE100\\RE102\\237159\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Leval-R3: B-Male@17:07:13"}}}, {"name": "Micro-R3: B-Male@17:08:09", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 6959851371434112548, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591290721, "stewardId": 458, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV800\\29046\\"]}}}, "name": "Micro-R3: B-Male@17:08:09"}}}, {"name": "Lisin-R3: B-Male@17:08:22", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 4746832498654789977, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591303552, "stewardId": 459, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\CV000\\CV800\\29046\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Lisin-R3: B-Male@17:08:22"}}}, {"name": "Micro-R3: B-Male@17:08:38", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 8108079039160737023, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591319854, "stewardId": 460, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\1202\\"]}}}, "name": "Micro-R3: B-Male@17:08:38"}}}, {"name": "Ateno-R3: B-Male@17:08:48", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 554635661664219990, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591330029, "stewardId": 461, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\zzzz\\1202\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Ateno-R3: B-Male@17:08:48"}}}, {"name": "Micro-R3: B-Male@17:09:03", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 2606299132071671313, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591344970, "stewardId": 462, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV100\\6918\\"]}}}, "name": "Micro-R3: B-Male@17:09:03"}}}, {"name": "Metop-R3: B-Male@17:09:21", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 6077163558340746294, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591362864, "stewardId": 463, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\CV000\\CV100\\6918\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Metop-R3: B-Male@17:09:21"}}}, {"name": "Micro-R3: B-Male@17:09:37", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 8677293011945386124, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591378441, "stewardId": 464, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Microphthalmos (743.1)\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\"]}}}, "name": "Micro-R3: B-Male@17:09:37"}}}, {"name": "Chlor-R3: B-Male@17:10:53", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 5435806514319896029, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591454643, "stewardId": 465, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Chlor-R3: B-Male@17:10:53"}}}, {"name": "Chlor-R3: B-Male@17:11:13", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 6269112036394442108, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591474992, "stewardId": 466, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Anophthalmos (743.0)\\"]}}}, "name": "Chlor-R3: B-Male@17:11:13"}}}, {"name": "Anoph-R3: B-Male@17:11:24", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 3610131085635691220, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591486085, "stewardId": 467, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Anophthalmos (743.0)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\"]}}, "name": "Anoph-R3: B-Male@17:11:24"}}}, {"name": "Anoph-R3: B-Male@17:11:55", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 5782621780167804633, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591521346, "stewardId": 468, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Anophthalmos (743.0)\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV100\\6918\\"]}}}, "name": "Anoph-R3: B-Male@17:11:55"}}}, {"name": "Anoph-R3: B-Male@17:13:07", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying potential correlations between congenital anomalies and increased risk of highly prevalent chronic health conditions diabetes mellitus type 2 and hypertension, and effectiveness of standard medications for chronic conditions in patients with congenital anomalies.", "createDate": 1472586681523, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 19, "changeDate": 1472587037082, "name": "Identifying correlations between congenital anomalies and increased risk for chronic health conditions"}, "stewardResponse": "Approved", "externalId": 8089643168948492294, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472591588373, "stewardId": 469, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Demographics\\Gender\\Male\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Congenital anomalies of eye (743)\\Anophthalmos (743.0)\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV800\\29046\\"]}}}, "name": "Anoph-R3: B-Male@17:13:07"}}}]} \ No newline at end of file diff --git a/apps/steward-app/src/test/resources/drevil_topic_20.json b/apps/steward-app/src/test/resources/drevil_topic_20.json new file mode 100644 index 000000000..31031f52a --- /dev/null +++ b/apps/steward-app/src/test/resources/drevil_topic_20.json @@ -0,0 +1 @@ +{"totalCount": 37, "skipped": 0, "queryRecords": [{"name": "Diabe-R3: B-Aceta@11:16:06", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 5174467797346328639, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656568233, "stewardId": 507, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE500\\RE515\\161\\"]}}, "name": "Diabe-R3: B-Aceta@11:16:06"}}}, {"name": "Diabe-R3: B-Aspir@11:16:23", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 2794376855051886671, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656585152, "stewardId": 508, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1191\\"]}}, "name": "Diabe-R3: B-Aspir@11:16:23"}}}, {"name": "Diabe-R3: B-Caffe@11:16:55", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 1044427930106308584, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656616859, "stewardId": 509, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1886\\"]}}, "name": "Diabe-R3: B-Caffe@11:16:55"}}}, {"name": "Diabe-R3: B-Acarb@11:17:12", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 7418005693351346004, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656633476, "stewardId": 510, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\"]}}, "name": "Diabe-R3: B-Acarb@11:17:12"}}}, {"name": "Diabe-R3: B-migli@11:17:28", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 695924216927184648, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656649645, "stewardId": 511, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\"]}}, "name": "Diabe-R3: B-migli@11:17:28"}}}, {"name": "Diabe-R3: B-Metfo@11:17:46", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 7888563857135844160, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656667842, "stewardId": 512, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes mellitus without mention of complication (250.0)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\"]}}, "name": "Diabe-R3: B-Metfo@11:17:46"}}}, {"name": "Diabe-R3: B-Aceta@11:18:32", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 5168650818913981331, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656713441, "stewardId": 513, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE500\\RE515\\161\\"]}}, "name": "Diabe-R3: B-Aceta@11:18:32"}}}, {"name": "Diabe-R3: B-Aspir@11:18:58", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 7844324309055147065, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656739015, "stewardId": 514, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1191\\"]}}, "name": "Diabe-R3: B-Aspir@11:18:58"}}}, {"name": "Diabe-R3: B-Caffe@11:19:10", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 794591131061932003, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656751714, "stewardId": 515, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\RE000\\RE599\\1886\\"]}}, "name": "Diabe-R3: B-Caffe@11:19:10"}}}, {"name": "Diabe-R3: B-Acarb@11:19:26", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 8280687518804420793, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656767637, "stewardId": 516, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\"]}}, "name": "Diabe-R3: B-Acarb@11:19:26"}}}, {"name": "Diabe-R3: B-migli@11:20:08", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 589945805809937476, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656810210, "stewardId": 517, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\"]}}, "name": "Diabe-R3: B-migli@11:20:08"}}}, {"name": "Diabe-R3: B-Metfo@11:22:35", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 583418508499046891, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656956754, "stewardId": 518, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\"]}}, "name": "Diabe-R3: B-Metfo@11:22:35"}}}, {"name": "Diabe-R3: B-Chlor@11:23:40", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 7203792399675073634, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657021649, "stewardId": 519, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Endocrine, nutritional and metabolic diseases, and immunity disorders (240-279.99)\\Diseases of other endocrine glands (249-259.99)\\Diabetes mellitus (250)\\Diabetes with ketoacidosis (250.1)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\"]}}, "name": "Diabe-R3: B-Chlor@11:23:40"}}}, {"name": "Essen-R3: B-Chlor@11:24:29", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 7362147804788597147, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657071188, "stewardId": 520, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\"]}}, "name": "Essen-R3: B-Chlor@11:24:29"}}}, {"name": "Essen-R3: B-Lisin@11:24:46", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 8037011930011168290, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657087340, "stewardId": 521, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV800\\29046\\"]}}, "name": "Essen-R3: B-Lisin@11:24:46"}}}, {"name": "Essen-R3: B-Ateno@11:24:57", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 6472709426375569353, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657099117, "stewardId": 522, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\1202\\"]}}, "name": "Essen-R3: B-Ateno@11:24:57"}}}, {"name": "Essen-R3: B-Metop@11:25:11", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 872766527547291191, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657112978, "stewardId": 523, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV100\\6918\\"]}}, "name": "Essen-R3: B-Metop@11:25:11"}}}, {"name": "Essen-R3: B-Hydro@11:25:26", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 3586929774720426387, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657127365, "stewardId": 524, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\5487\\"]}}, "name": "Essen-R3: B-Hydro@11:25:26"}}}, {"name": "Essen-R3: B-Chlor@11:25:40", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 470833085071994268, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657142257, "stewardId": 525, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2409\\"]}}, "name": "Essen-R3: B-Chlor@11:25:40"}}}, {"name": "Ess-R3:-mig-Chl@11:26:10", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 4945624012203499502, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657171564, "stewardId": 526, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\"]}}, "name": "Ess-R3:-mig-Chl@11:26:10"}}}, {"name": "Ess-R3:-mig-Lis@11:26:24", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 6287374830168751593, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657185503, "stewardId": 527, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV800\\29046\\"]}}, "name": "Ess-R3:-mig-Lis@11:26:24"}}}, {"name": "Ess-R3:-mig-Ate@11:26:36", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 1179439889529872745, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657198049, "stewardId": 528, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\1202\\"]}}, "name": "Ess-R3:-mig-Ate@11:26:36"}}}, {"name": "Ess-R3:-mig-Met@11:26:57", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 4711862544928567957, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657219348, "stewardId": 529, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV100\\6918\\"]}}, "name": "Ess-R3:-mig-Met@11:26:57"}}}, {"name": "Ess-R3:-mig-Hyd@11:27:16", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 8632446537331239933, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657237911, "stewardId": 530, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\5487\\"]}}, "name": "Ess-R3:-mig-Hyd@11:27:16"}}}, {"name": "Ess-R3:-mig-Chl@11:27:33", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 4730527150986487561, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657254820, "stewardId": 531, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\30009\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2409\\"]}}, "name": "Ess-R3:-mig-Chl@11:27:33"}}}, {"name": "Ess-R3:-Met-Chl@11:28:04", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 4419356379262081231, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657286321, "stewardId": 532, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\"]}}, "name": "Ess-R3:-Met-Chl@11:28:04"}}}, {"name": "Ess-R3:-Met-Lis@11:28:20", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 1021398333063898808, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657301814, "stewardId": 533, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV800\\29046\\"]}}, "name": "Ess-R3:-Met-Lis@11:28:20"}}}, {"name": "Ess-R3:-Met-Ate@11:28:36", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 2899915565944317229, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657317705, "stewardId": 534, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\1202\\"]}}, "name": "Ess-R3:-Met-Ate@11:28:36"}}}, {"name": "Ess-R3:-Met-Met@11:28:53", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 452016522880455733, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657334572, "stewardId": 535, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV100\\6918\\"]}}, "name": "Ess-R3:-Met-Met@11:28:53"}}}, {"name": "Ess-R3:-Met-Hyd@11:29:08", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 224710722022226306, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657349518, "stewardId": 536, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\5487\\"]}}, "name": "Ess-R3:-Met-Hyd@11:29:08"}}}, {"name": "Ess-R3:-Met-Chl@11:29:22", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 3935574894037045539, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657364111, "stewardId": 537, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2409\\"]}}, "name": "Ess-R3:-Met-Chl@11:29:22"}}}, {"name": "Ess-R3:-Met-Chl@11:29:42", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 597323415907564732, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657384315, "stewardId": 538, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\HS000\\HS500\\HS502\\6809\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2396\\"]}}, "name": "Ess-R3:-Met-Chl@11:29:42"}}}, {"name": "Ess-R3:-Aca-Lis@11:36:14", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 5944191251893031620, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657775334, "stewardId": 539, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV800\\29046\\"]}}, "name": "Ess-R3:-Aca-Lis@11:36:14"}}}, {"name": "Ess-R3:-Aca-Ate@11:36:28", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 6680919560460432205, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657789699, "stewardId": 540, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\1202\\"]}}, "name": "Ess-R3:-Aca-Ate@11:36:28"}}}, {"name": "Ess-R3:-Aca-Met@11:36:42", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 9091099492570587120, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657804003, "stewardId": 541, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV100\\6918\\"]}}, "name": "Ess-R3:-Aca-Met@11:36:42"}}}, {"name": "Ess-R3:-Aca-Hyd@11:36:57", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 8707285113581419320, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657818752, "stewardId": 542, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\5487\\"]}}, "name": "Ess-R3:-Aca-Hyd@11:36:57"}}}, {"name": "Ess-R3:-Aca-Chl@11:37:11", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Comparing impact and effectiveness of medications for patients with multiple dietary related chronic health conditions, specifically diabetes mellitus type 2 and hypertension", "createDate": 1472586703950, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 20, "changeDate": 1472587157443, "name": "Comparing impact of medications for patients with multiple chronic health conditions"}, "stewardResponse": "Approved", "externalId": 5475903983983046083, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472657833014, "stewardId": 543, "queryContents": {"queryDefinition": {"expr": {"and": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Diseases of the circulatory system (390-459.99)\\Hypertensive disease (401-405.99)\\Essential hypertension (401)\\", "\\\\SHRINE\\SHRINE\\Demographics\\Race\\Black or African American\\", "\\\\SHRINE\\SHRINE\\medications\\zzzz\\16681\\", "\\\\SHRINE\\SHRINE\\medications\\CV000\\CV400\\2409\\"]}}, "name": "Ess-R3:-Aca-Chl@11:37:11"}}}]} \ No newline at end of file diff --git a/apps/steward-app/src/test/resources/drevil_topic_21.json b/apps/steward-app/src/test/resources/drevil_topic_21.json new file mode 100644 index 000000000..3619a6051 --- /dev/null +++ b/apps/steward-app/src/test/resources/drevil_topic_21.json @@ -0,0 +1 @@ +{"totalCount": 37, "skipped": 0, "queryRecords": [{"name": "Other maternal @10:03:20", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 438512722186084961, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472652202338, "stewardId": 470, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Other maternal and fetal complications (678-679.99)\\"}, "name": "Other maternal @10:03:20"}}}, {"name": "Hypertonic, inc@10:03:57", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 2331305839012106062, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472652238459, "stewardId": 471, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Hypertonic, incoordinate, or prolonged uterine contractions (661.4)\\"}, "name": "Hypertonic, inc@10:03:57"}}}, {"name": "Precipitate lab@10:04:32", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 4535951599211583429, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472652273378, "stewardId": 472, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Precipitate labor (661.3)\\"}, "name": "Precipitate lab@10:04:32"}}}, {"name": "Cesarean delive@10:04:52", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 5643525925200359244, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472652293830, "stewardId": 473, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Other complications of labor and delivery, not elsewhere classified (669)\\Cesarean delivery, without mention of indication (669.7)\\"}, "name": "Cesarean delive@10:04:52"}}}, {"name": "Forceps or vacu@10:05:33", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 1084347425622270993, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472652335220, "stewardId": 474, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Other complications of labor and delivery, not elsewhere classified (669)\\Forceps or vacuum extractor delivery without mention of indication (669.5)\\"}, "name": "Forceps or vacu@10:05:33"}}}, {"name": "Abnormality of @10:08:51", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 254709938122300093, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472652536448, "stewardId": 475, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\"}, "name": "Abnormality of @10:08:51"}}}, {"name": "Hypertonic, inc@10:09:28", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 9139901314049343764, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472652570019, "stewardId": 476, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Hypertonic, incoordinate, or prolonged uterine contractions (661.4)\\"}, "name": "Hypertonic, inc@10:09:28"}}}, {"name": "Other and unspe@10:11:07", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 1712052448050515701, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472652669754, "stewardId": 477, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Other and unspecified uterine inertia (661.2)\\"}, "name": "Other and unspe@10:11:07"}}}, {"name": "Primary uterine@10:11:24", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 932158704885655628, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472652686393, "stewardId": 478, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Primary uterine inertia (661.0)\\"}, "name": "Primary uterine@10:11:24"}}}, {"name": "Unspecified abn@10:53:04", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 2171121096823526348, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655186002, "stewardId": 479, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Unspecified abnormality of labor (661.9)\\"}, "name": "Unspecified abn@10:53:04"}}}, {"name": "Pulmonary compl@10:53:20", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 6612011731372599443, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655201353, "stewardId": 480, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Complications of the administration of anesthetic or other sedation in labor and delivery (668)\\Pulmonary complications of anesthesia or other sedation in labor and delivery (668.0)\\"}, "name": "Pulmonary compl@10:53:20"}}}, {"name": "Deep transverse@10:53:33", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 8925248588560705491, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655215214, "stewardId": 481, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Obstructed labor (660)\\Deep transverse arrest and persistent occipitoposterior position during labor and delivery (660.3)\\"}, "name": "Deep transverse@10:53:33"}}}, {"name": "Failed forceps @10:53:47", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 6509861914548584297, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655228979, "stewardId": 482, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Obstructed labor (660)\\Failed forceps or vacuum extractor, unspecified (660.7)\\"}, "name": "Failed forceps @10:53:47"}}}, {"name": "Legally induced@10:54:01", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 7842474969127688299, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655242650, "stewardId": 483, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Other pregnancy with abortive outcome (634-639.99)\\Legally induced abortion (635)\\"}, "name": "Legally induced@10:54:01"}}}, {"name": "Obstruction cau@10:54:17", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 4990482595786845002, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655258742, "stewardId": 484, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Obstructed labor (660)\\Obstruction caused by malposition of fetus at onset of labor (660.0)\\"}, "name": "Obstruction cau@10:54:17"}}}, {"name": "Complications o@10:54:30", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 8329894087176016412, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655271406, "stewardId": 485, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\"}, "name": "Complications o@10:54:30"}}}, {"name": "Failed trial of@10:54:53", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 4737401618560282720, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655294806, "stewardId": 486, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Obstructed labor (660)\\Failed trial of labor, unspecified (660.6)\\"}, "name": "Failed trial of@10:54:53"}}}, {"name": "Hypertonic, inc@10:56:30", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 2406312754995701454, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655391454, "stewardId": 487, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Hypertonic, incoordinate, or prolonged uterine contractions (661.4)\\"}, "name": "Hypertonic, inc@10:56:30"}}}, {"name": "Precipitate lab@10:56:45", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 6399485767031026748, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655406680, "stewardId": 488, "queryContents": {"queryDefinition": {"expr": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Precipitate labor (661.3)\\"}, "name": "Precipitate lab@10:56:45"}}}, {"name": "Precipi-(754.0)@10:57:17", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 7087687189896824207, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655439442, "stewardId": 489, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Precipitate labor (661.3)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "Precipi-(754.0)@10:57:17"}}}, {"name": "(754.0)-Cesarea@10:57:54", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 1488614521209287834, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655475772, "stewardId": 490, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Other complications of labor and delivery, not elsewhere classified (669)\\Cesarean delivery, without mention of indication (669.7)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Cesarea@10:57:54"}}}, {"name": "(754.0)-Forceps@10:58:16", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 6904170937440497764, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655497924, "stewardId": 491, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Other complications of labor and delivery, not elsewhere classified (669)\\Forceps or vacuum extractor delivery without mention of indication (669.5)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Forceps@10:58:16"}}}, {"name": "(754.0)-Abnorma@11:05:14", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 6582492753700411830, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655915653, "stewardId": 492, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Abnorma@11:05:14"}}}, {"name": "(754.0)-Hyperto@11:05:45", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 1220904381720167210, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655946565, "stewardId": 493, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Hypertonic, incoordinate, or prolonged uterine contractions (661.4)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Hyperto@11:05:45"}}}, {"name": "(754.0)-Other a@11:06:01", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 1662792196575435192, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655962257, "stewardId": 494, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Other and unspecified uterine inertia (661.2)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Other a@11:06:01"}}}, {"name": "(754.0)-Primary@11:06:13", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 9211643082625991431, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655975182, "stewardId": 495, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Primary uterine inertia (661.0)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Primary@11:06:13"}}}, {"name": "(754.0)-Unspeci@11:06:27", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 7801071439031281460, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472655988783, "stewardId": 496, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Unspecified abnormality of labor (661.9)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Unspeci@11:06:27"}}}, {"name": "(754.0)-Pulmona@11:06:42", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 5625673261436757322, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656003521, "stewardId": 497, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Complications of the administration of anesthetic or other sedation in labor and delivery (668)\\Pulmonary complications of anesthesia or other sedation in labor and delivery (668.0)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Pulmona@11:06:42"}}}, {"name": "(754.0)-Deep tr@11:07:15", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 3292028783225861116, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656036696, "stewardId": 498, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Obstructed labor (660)\\Deep transverse arrest and persistent occipitoposterior position during labor and delivery (660.3)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Deep tr@11:07:15"}}}, {"name": "(754.0)-Failed @11:07:37", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 8170097484723196535, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656059037, "stewardId": 499, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Obstructed labor (660)\\Failed forceps or vacuum extractor, unspecified (660.7)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.0) Congenital musculoskeletal deformities of skull, face, and jaw\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.1) Congenital musculoskeletal deformities of sternocleidomastoid muscle\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Certain congenital musculoskeletal deformities (754)\\(754.2) Congenital musculoskeletal deformities of spine\\"]}}}, "name": "(754.0)-Failed @11:07:37"}}}, {"name": "(740.0)-Cesarea@11:08:15", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 5380239451968470447, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656096682, "stewardId": 500, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Other complications of labor and delivery, not elsewhere classified (669)\\Cesarean delivery, without mention of indication (669.7)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.0) Anencephalus\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.1) Craniorachischisis\\"]}}}, "name": "(740.0)-Cesarea@11:08:15"}}}, {"name": "(740.0)-Forceps@11:08:28", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 7811264639452279144, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656110090, "stewardId": 501, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Other complications of labor and delivery, not elsewhere classified (669)\\Forceps or vacuum extractor delivery without mention of indication (669.5)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.0) Anencephalus\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.1) Craniorachischisis\\"]}}}, "name": "(740.0)-Forceps@11:08:28"}}}, {"name": "(740.0)-Abnorma@11:08:40", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 4977265009312036640, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656121445, "stewardId": 502, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.0) Anencephalus\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.1) Craniorachischisis\\"]}}}, "name": "(740.0)-Abnorma@11:08:40"}}}, {"name": "(740.0)-Hyperto@11:08:52", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 8360282821525601865, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656133635, "stewardId": 503, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Hypertonic, incoordinate, or prolonged uterine contractions (661.4)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.0) Anencephalus\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.1) Craniorachischisis\\"]}}}, "name": "(740.0)-Hyperto@11:08:52"}}}, {"name": "(740.0)-Other a@11:09:05", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 5494801448121929467, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656146743, "stewardId": 504, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Other and unspecified uterine inertia (661.2)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.0) Anencephalus\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.1) Craniorachischisis\\"]}}}, "name": "(740.0)-Other a@11:09:05"}}}, {"name": "(740.0)-Primary@11:09:17", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 8470228705289450818, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656158815, "stewardId": 505, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Primary uterine inertia (661.0)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.0) Anencephalus\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.1) Craniorachischisis\\"]}}}, "name": "(740.0)-Primary@11:09:17"}}}, {"name": "(740.0)-Unspeci@11:09:28", "topic": {"changedBy": {"userName": "dave", "fullName": "Steward Test Steward Dave", "roles": ["DataSteward", "Researcher"]}, "description": "Identifying correlations between labor and birth complications, and specific congenital conditions related to musculoskeletal defects of the spine, skull and neck.", "createDate": 1472586727997, "state": "Approved", "createdBy": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "id": 21, "changeDate": 1472587313302, "name": "identifying Correlations between congenital anomalies and complications of labor and birth."}, "stewardResponse": "Approved", "externalId": 1567726413236597662, "user": {"userName": "drevil", "fullName": "Mal Ishus", "roles": ["Researcher"]}, "date": 1472656169392, "stewardId": 506, "queryContents": {"queryDefinition": {"expr": {"and": {"term": "\\\\SHRINE\\SHRINE\\Diagnoses\\Complications of pregnancy, childbirth, and the puerperium (630-679.99)\\Complications occurring mainly in the course of labor and delivery (660-669.99)\\Abnormality of forces of labor (661)\\Unspecified abnormality of labor (661.9)\\", "or": {"term": ["\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.0) Anencephalus\\", "\\\\SHRINE\\SHRINE\\Diagnoses\\Congenital anomalies (740-759.99)\\Anencephalus and similar anomalies (740)\\(740.1) Craniorachischisis\\"]}}}, "name": "(740.0)-Unspeci@11:09:28"}}}]} \ No newline at end of file diff --git a/apps/steward-app/src/test/resources/get-json-data.py b/apps/steward-app/src/test/resources/get-json-data.py new file mode 100644 index 000000000..3a0c6adbf --- /dev/null +++ b/apps/steward-app/src/test/resources/get-json-data.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +import json +import sys +import subprocess + +def json_print(json_thing): + print json.dumps(json_thing) + +def curl(url_after_steward, user, password): + input = subprocess.check_output([ + "curl", + "https://shrine-dev1.catalyst:6443/steward/" + url_after_steward, + "-k", + "--user", + "%s:%s" % (user, password) + ]) + return json.loads(input.decode("utf-8")) + +def topic_ids(user, password): + json_input = curl("researcher/topics/", user, password) + topics = json_input["topics"] + result = [] + for topic in topics: + result.append(topic["id"]) + result.sort() + return result + + +def query_history_for_topic(user, password, topic): + return curl("researcher/queryHistory/topic/%s" % topic, user, password) + +def query_history_for_all_topics(user, password): + return curl("researcher/queryHistory", user, password) + +def write_topic_history_to_file(user, password, topic): + result = query_history_for_topic(user, password, topic) + if result["totalCount"] > 0: + json_file = open('%s_topic_%d.json' % (user, topic), 'w') + json_file.write(json.dumps(result)) + json_file.close() + +def write_histories_to_file(user, password): + topics = topic_ids(user, password) + for topic in topics: + write_topic_history_to_file(user, password, topic) + json_file = open('%s_all_topics.json' % user, 'w') + json_file.write(json.dumps(query_history_for_all_topics(user, password))) + json_file.close() + +user = sys.argv[1] +password = sys.argv[2] + +if '-topics' in sys.argv: + json_print(topic_ids(user, password)) +if '-t' in sys.argv: + topic = sys.argv[1 + sys.argv.index('-t')] + json_print(query_history_for_topic(user, password, topic)) +if '-h' in sys.argv: + json_print(query_history_for_all_topics(user, password)) +if '-f' in sys.argv: + write_histories_to_file(user, password) diff --git a/apps/steward-app/src/test/scala/net/shrine/steward/StewardServiceSpec.scala b/apps/steward-app/src/test/scala/net/shrine/steward/StewardServiceSpec.scala index c5e252099..4c2345033 100644 --- a/apps/steward-app/src/test/scala/net/shrine/steward/StewardServiceSpec.scala +++ b/apps/steward-app/src/test/scala/net/shrine/steward/StewardServiceSpec.scala @@ -1,1338 +1,1381 @@ package net.shrine.steward -import net.shrine.authorization.steward.{TopicsPerState, QueriesPerUser, InboundTopicRequest, InboundShrineQuery, QueryHistory, StewardsTopics, ResearchersTopics, OutboundShrineQuery, TopicState, OutboundUser, OutboundTopic, stewardRole} +import net.shrine.authorization.steward._ import net.shrine.i2b2.protocol.pm.User import net.shrine.protocol.Credential -import net.shrine.steward.db.{QueryParameters, UserRecord, StewardDatabase} +import net.shrine.steward.db.{QueryParameters, StewardDatabase, UserRecord} import org.json4s.native.JsonMethods.parse import org.junit.runner.RunWith import org.scalatest.junit.JUnitRunner -import org.scalatest.{Suite, BeforeAndAfterEach, FlatSpec} +import org.scalatest.{BeforeAndAfterEach, FlatSpec, Suite} import spray.http.BasicHttpCredentials import spray.routing.MalformedRequestContentRejection - import spray.testkit.ScalatestRouteTest -import spray.http.StatusCodes.{OK,UnavailableForLegalReasons,Accepted,Unauthorized,UnprocessableEntity,NotFound,Forbidden,PermanentRedirect} +import spray.http.StatusCodes._ + +import scala.xml.{Elem, NodeSeq} @RunWith(classOf[JUnitRunner]) class StewardServiceTest extends FlatSpec with ScalatestRouteTest with TestWithDatabase with StewardService { def actorRefFactory = system import scala.concurrent.duration._ implicit val routeTestTimeout = RouteTestTimeout(10 seconds) val researcherUserName = "ben" val researcherFullName = researcherUserName val stewardUserName = "dave" val stewardFullName = stewardUserName /** * to run these tests with I2B2 * add a user named qep, to be the qep * add a Boolean parameter for qep, qep, true * add a user named ben, to be a researcher * add a user named dave, to be the data steward * add a Boolean parameter for dave, DataSteward, true * add all three users to the i2b2 project */ val stewardCredentials = BasicHttpCredentials(stewardUserName,"kablam") val researcherCredentials = BasicHttpCredentials(researcherUserName,"kapow") val qepCredentials = BasicHttpCredentials("qep","trustme") val badCredentials = BasicHttpCredentials("qep","wrongPassword") val researcherUser = User( fullName = researcherUserName, username = researcherFullName, domain = "domain", credential = new Credential("ben's password",false), params = Map(), rolesByProject = Map() ) val stewardUser = User( fullName = stewardUserName, username = stewardFullName, domain = "domain", credential = new Credential("dave's password",false), params = Map(stewardRole -> "true"), rolesByProject = Map() ) val researcherOutboundUser = OutboundUser.createFromUser(researcherUser) val stewardOutboundUser = OutboundUser.createFromUser(stewardUser) val uncontroversialTopic = OutboundTopic(1,"UncontroversialKidneys","Study kidneys without controversy",researcherOutboundUser,0L,TopicState.pending.name,researcherOutboundUser,0L) val forbiddenTopicId = 0 + private val queryContent: QueryContents = "18-34 years old@18:31:51\\\\SHRINE\\SHRINE\\Demographics\\Age\\18-34 years old\\" + val diffs = List.empty + val getItBack:NodeSeq = scala.xml.XML.loadString(queryContent) "StewardService" should "return an OK and the correct createTopicsMode name" in { StewardConfigSource.configForBlock(StewardConfigSource.createTopicsModeConfigKey,CreateTopicsMode.Pending.name,s"${CreateTopicsMode.Pending.name} test") { { Get(s"/about/createTopicsMode") ~> route ~> check { assertResult(OK)(status) val createTopicsModeName = new String(body.data.toByteArray) assertResult(s""""${StewardConfigSource.createTopicsInState.name}"""")(createTopicsModeName) assertResult(s""""${CreateTopicsMode.Pending.name}"""")(createTopicsModeName) } } } StewardConfigSource.configForBlock(StewardConfigSource.createTopicsModeConfigKey,CreateTopicsMode.Approved.name,s"${CreateTopicsMode.Approved.name} test") { Get(s"/about/createTopicsMode") ~> route ~> check { assertResult(OK)(status) val createTopicsModeName = new String(body.data.toByteArray) assertResult( s""""${StewardConfigSource.createTopicsInState.name}"""")(createTopicsModeName) assertResult( s""""${CreateTopicsMode.Approved.name}"""")(createTopicsModeName) } } StewardConfigSource.configForBlock(StewardConfigSource.createTopicsModeConfigKey,CreateTopicsMode.TopicsIgnoredJustLog.name,s"${CreateTopicsMode.TopicsIgnoredJustLog.name} test") { Get(s"/about/createTopicsMode") ~> route ~> check { assertResult(OK)(status) val createTopicsModeName = new String(body.data.toByteArray) assertResult( s""""${StewardConfigSource.createTopicsInState.name}"""")(createTopicsModeName) assertResult( s""""${CreateTopicsMode.TopicsIgnoredJustLog.name}"""")(createTopicsModeName) } } } "StewardService" should "return an OK and a valid outbound user for a user/whoami request" in { Get(s"/user/whoami") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val userJson = new String(body.data.toByteArray) val outboundUser = parse(userJson).extract[OutboundUser] assertResult(researcherOutboundUser)(outboundUser) } } "StewardService" should "return a 200 for a user/whoami request with bad credentials, with a body of 'AuthenticationFailed'" in { // "StewardService" should "return an TemporaryRedirect for a user/whoami request with bad credentials" in { Get(s"/user/whoami") ~> addCredentials(badCredentials) ~> sealRoute(route) ~> check { assertResult(OK)(status) assertResult(""""AuthenticationFailed"""")(new String(body.data.toByteArray)) } } /* todo "StewardService" should "return a 200 for a user/whoami request with bad credentials, with a body of 'AuthenticationFailed' even wiht a back-tick" in { // "StewardService" should "return an TemporaryRedirect for a user/whoami request with bad credentials" in { val badCredentials = BasicHttpCredentials("o`brien","wrongPassword") Get(s"/user/whoami") ~> addCredentials(badCredentials) ~> sealRoute(route) ~> check { assertResult(OK)(status) assertResult(""""AuthenticationFailed"""")(new String(body.data.toByteArray)) } } */ "StewardService" should "return an OK for an approved request" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) val topicInDb = StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) val dbFound = StewardDatabase.db.changeTopicState(uncontroversialTopic.id,TopicState.approved,stewardUserName) Post(s"/qep/requestQueryAccess/user/${researcherUserName}/topic/${uncontroversialTopic.id}",InboundShrineQuery(1,"test query","crazy syntax")) ~> addCredentials(qepCredentials) ~> route ~> check { assertResult(OK)(status) } } "StewardService" should "complain about bad http credentials from the QEP" in { Post(s"/qep/requestQueryAccess/user/${researcherUserName}/topic/${uncontroversialTopic.id}",InboundShrineQuery(2,"test query","too bad about your password")) ~> addCredentials(badCredentials) ~> sealRoute(route) ~> check { assertResult(Unauthorized)(status) } } "StewardService" should "complain about unexpected json" in { Post(s"/qep/requestQueryAccess/user/${researcherUserName}/topic/${uncontroversialTopic.id}","""{"field":"not in ShrineQuery"}""") ~> addCredentials(qepCredentials) ~> route ~> check { assertResult(false)(handled) assertResult(true)(rejection.isInstanceOf[MalformedRequestContentRejection]) } } "StewardService" should "return a rejection for an unacceptable request" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) StewardDatabase.db.changeTopicState(1,TopicState.rejected,stewardUserName) Post(s"/qep/requestQueryAccess/user/${researcherUserName}/topic/${uncontroversialTopic.id}",InboundShrineQuery(3,"test query","too bad about your topic")) ~> addCredentials(qepCredentials) ~> route ~> check { assertResult(UnavailableForLegalReasons)(status) } } "StewardService" should "return a rejection for a pending topic" in { StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) Post(s"/qep/requestQueryAccess/user/${researcherUserName}/topic/${uncontroversialTopic.id}",InboundShrineQuery(4,"test query","topic still pending")) ~> addCredentials(qepCredentials) ~> route ~> check { assertResult(UnavailableForLegalReasons)(status) } } "StewardService" should "return an UnprocessableEntity for an unknown topic" in { Post(s"/qep/requestQueryAccess/user/${researcherUserName}/topic/${forbiddenTopicId}",InboundShrineQuery(5,"test query","no one knows about your topic")) ~> addCredentials(qepCredentials) ~> route ~> check { assertResult(UnprocessableEntity)(status) } } "StewardService" should " return an UnprocessableEntity for query requests with no topic" in { Post(s"/qep/requestQueryAccess/user/${researcherUserName}",InboundShrineQuery(5,"test query","Not even using a topic")) ~> addCredentials(qepCredentials) ~> route ~> check { assertResult(UnprocessableEntity)(status) } } "StewardService" should " accept query requests with no topic in 'just log and approve everything' mode " in { StewardConfigSource.configForBlock(StewardConfigSource.createTopicsModeConfigKey,CreateTopicsMode.TopicsIgnoredJustLog.name,s"${CreateTopicsMode.TopicsIgnoredJustLog.name} test") { Post(s"/qep/requestQueryAccess/user/${researcherUserName}", InboundShrineQuery(5, "test query", "Not even using a topic")) ~> addCredentials(qepCredentials) ~> route ~> check { assertResult(OK)(status) } } } "StewardService" should "return approved topics for the qep" in { StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) Get(s"/qep/approvedTopics/user/${researcherUserName}") ~> addCredentials(qepCredentials) ~> route ~> check { status === OK val topicsJson = new String(body.data.toByteArray) val topics = parse(topicsJson).extract[ResearchersTopics] ResearchersTopics(researcherUserName,1,0,Seq(uncontroversialTopic)).sameExceptForTimes(topics) === true } } "StewardService" should "return the list of a researcher's Topics as Json" in { StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) Get(s"/researcher/topics") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val topicsJson = new String(body.data.toByteArray) val topics = parse(topicsJson).extract[ResearchersTopics] assertResult(true)(ResearchersTopics(researcherUserName,1,0,Seq(uncontroversialTopic)).sameExceptForTimes(topics)) } } "StewardService" should "return the list of a researcher's Topics as Json for various states" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) Get(s"/researcher/topics?state=Pending") ~> addCredentials(researcherCredentials) ~> route ~> check { val topicsJson = new String(body.data.toByteArray) val topics = parse(topicsJson).extract[ResearchersTopics] assertResult(true)(ResearchersTopics(researcherUserName,1,0,Seq(uncontroversialTopic)).sameExceptForTimes(topics)) assertResult(OK)(status) } StewardDatabase.db.changeTopicState(1,TopicState.approved,stewardUserName) Get(s"/researcher/topics?state=Approved") ~> addCredentials(researcherCredentials) ~> route ~> check { val topicsJson = new String(body.data.toByteArray) val topics = parse(topicsJson).extract[ResearchersTopics] assertResult(true)(ResearchersTopics(researcherUserName,1,0,Seq( OutboundTopic(1,uncontroversialTopic.name,uncontroversialTopic.description,researcherOutboundUser,0,TopicState.approved.name,stewardOutboundUser,0))).sameExceptForTimes(topics)) assertResult(OK)(status) } StewardDatabase.db.changeTopicState(1,TopicState.rejected,stewardUserName) Get(s"/researcher/topics?state=Rejected") ~> addCredentials(researcherCredentials) ~> route ~> check { val topicsJson = new String(body.data.toByteArray) val topics = parse(topicsJson).extract[ResearchersTopics] assertResult(true)(ResearchersTopics(researcherUserName,1,0,Seq( OutboundTopic(1,uncontroversialTopic.name,uncontroversialTopic.description,researcherOutboundUser,0,TopicState.rejected.name,stewardOutboundUser,0))).sameExceptForTimes(topics)) assertResult(OK)(status) } } "DataStewardService" should "reject nonsense topic request states" in { Get(s"/researcher/topics?state=nonsense") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(UnprocessableEntity)(status) } } "StewardService" should "return the list of a researcher's Topics as Json with skip and limit set" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) createFiveTopics() Get(s"/researcher/topics?skip=0&limit=2") ~> addCredentials(researcherCredentials) ~> route ~> check { val topicsJson = new String(body.data.toByteArray) val topics = parse(topicsJson).extract[ResearchersTopics] assertResult(2)(topics.topics.size) assertResult(OK)(status) } Get(s"/researcher/topics?skip=2&limit=2") ~> addCredentials(researcherCredentials) ~> route ~> check { val topicsJson = new String(body.data.toByteArray) val topics = parse(topicsJson).extract[ResearchersTopics] assertResult(2)(topics.topics.size) assertResult(OK)(status) } Get(s"/researcher/topics?skip=4&limit=2") ~> addCredentials(researcherCredentials) ~> route ~> check { val topicsJson = new String(body.data.toByteArray) val topics = parse(topicsJson).extract[ResearchersTopics] assertResult(1)(topics.topics.size) assertResult(OK)(status) } } "StewardService" should "return the list of a researcher's Topics as Json with different sorting and ordering options" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) createFiveTopics() Get(s"/researcher/topics?sortBy=id") ~> addCredentials(researcherCredentials) ~> route ~> check { val topicsJson = new String(body.data.toByteArray) val topics = parse(topicsJson).extract[ResearchersTopics] assertResult(5)(topics.topics.size) assertResult(OK)(status) assertResult(topics.topics.sortBy(_.id))(topics.topics) } Get(s"/researcher/topics?sortBy=name") ~> addCredentials(researcherCredentials) ~> route ~> check { val topicsJson = new String(body.data.toByteArray) val topics = parse(topicsJson).extract[ResearchersTopics] assertResult(5)(topics.topics.size) assertResult(OK)(status) assertResult(topics.topics.sortBy(_.createdBy.userName))(topics.topics) } Get(s"/researcher/topics?sortBy=name&sortDirection=descending") ~> addCredentials(researcherCredentials) ~> route ~> check { val topicsJson = new String(body.data.toByteArray) val topics = parse(topicsJson).extract[ResearchersTopics] assertResult(5)(topics.topics.size) assertResult(OK)(status) assertResult(topics.topics.sortBy(_.name).reverse)(topics.topics) } Get(s"/researcher/topics?sortBy=name&sortDirection=ascending") ~> addCredentials(researcherCredentials) ~> route ~> check { val topicsJson = new String(body.data.toByteArray) val topics = parse(topicsJson).extract[ResearchersTopics] assertResult(5)(topics.topics.size) assertResult(OK)(status) assertResult(topics.topics.sortBy(_.createdBy.userName))(topics.topics) } } + "StewardService" should "return the list of a researcher's query history as Json" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) StewardDatabase.db.changeTopicState(1,TopicState.approved,stewardUserName) - StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(1),InboundShrineQuery(0,"test query","Can we get it back?")) + StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(1),InboundShrineQuery(0,"test query",queryContent)) + Get(s"/researcher/queryHistory") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] - assertResult(List.empty)(QueryHistory(1,0,List( - OutboundShrineQuery(1,0,"test query",researcherOutboundUser,Some( - OutboundTopic(1,uncontroversialTopic.name,uncontroversialTopic.description,researcherOutboundUser,0,TopicState.approved.name,stewardOutboundUser,0) - ),"Can we get it back?",TopicState.approved.name,0) - )).differencesExceptTimes(queries)) + val history: QueryHistory = QueryHistory(1, 0, List( + OutboundShrineQuery(1, 0, "test query", researcherOutboundUser, Some( + OutboundTopic(1, uncontroversialTopic.name, uncontroversialTopic.description, researcherOutboundUser, 0, TopicState.approved.name, stewardOutboundUser, 0) + ), getItBack, TopicState.approved.name, 0) + )) + assertResult(diffs)(history.differencesExceptTimes(queries)) } } + "StewardService" should "return the list of a researcher's query history as Json, with query bodies as Json" in { + + StewardDatabase.db.upsertUser(researcherUser) + StewardDatabase.db.upsertUser(stewardUser) + + StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) + StewardDatabase.db.changeTopicState(1,TopicState.approved,stewardUserName) + StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(1),InboundShrineQuery(0,"test query",queryContent)) + + Get(s"/researcher/queryHistory?asJson=true") ~> + addCredentials(researcherCredentials) ~> route ~> check { + assertResult(OK)(status) + + val queriesJson = new String(body.data.toByteArray) + val queries = parse(queriesJson).extract[QueryHistory] + println(queries) + + assertResult(diffs)(QueryHistory(1,0,List( + OutboundShrineQuery(1,0,"test query",researcherOutboundUser,Some( + OutboundTopic(1,uncontroversialTopic.name,uncontroversialTopic.description,researcherOutboundUser,0,TopicState.approved.name,stewardOutboundUser,0) + ),getItBack,TopicState.approved.name,0) + )).differencesExceptTimes(queries)) + } + } + "StewardService" should "return the list of a researcher's query history as Json, filtered by state" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) StewardDatabase.db.changeTopicState(1,TopicState.approved,stewardUserName) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest("Forbidden topic","No way is the data steward going for this")) StewardDatabase.db.changeTopicState(2,TopicState.rejected,stewardUserName) - StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(1),InboundShrineQuery(0,"test query","Can we get it back?")) - StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(2),InboundShrineQuery(1,"forbidden query","Can we get it back?")) + StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(1),InboundShrineQuery(0,"test query",queryContent)) + StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(2),InboundShrineQuery(1,"forbidden query",queryContent)) Get(s"/researcher/queryHistory?state=Approved") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] - assertResult(List.empty)(QueryHistory(1,0,List(OutboundShrineQuery(1,0,"test query",researcherOutboundUser,Some(OutboundTopic(1,uncontroversialTopic.name,uncontroversialTopic.description,researcherOutboundUser,0,TopicState.approved.name,stewardOutboundUser,0)),"Can we get it back?",TopicState.approved.name,0))).differencesExceptTimes(queries)) + assertResult(diffs)( + QueryHistory(1,0,List(OutboundShrineQuery(1,0,"test query",researcherOutboundUser,Some(OutboundTopic( + 1,uncontroversialTopic.name,uncontroversialTopic.description,researcherOutboundUser,0, + TopicState.approved.name,stewardOutboundUser,0)),getItBack,TopicState.approved.name,0))) + .differencesExceptTimes(queries)) } } "StewardService" should "return the list of a researcher's query history as Json, with skip and limit parameters" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) createSixQueries() Get(s"/researcher/queryHistory?") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(6)(queries.queryRecords.size) } Get(s"/researcher/queryHistory?skip=0&limit=5") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(5)(queries.queryRecords.size) } Get(s"/researcher/queryHistory?skip=1&limit=4") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(4)(queries.queryRecords.size) } Get(s"/researcher/queryHistory?skip=4&limit=5") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(2)(queries.queryRecords.size) } } "StewardService" should "return the list of a researcher's query history as Json, using parameters for sorting" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) createSixQueries() Get(s"/researcher/queryHistory?sortBy=externalId") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(6)(queries.queryRecords.size) assertResult(queries.queryRecords.sortBy(_.externalId))(queries.queryRecords) } Get(s"/researcher/queryHistory?sortBy=externalId&sortDirection=ascending") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(6)(queries.queryRecords.size) assertResult(queries.queryRecords.sortBy(_.externalId))(queries.queryRecords) } Get(s"/researcher/queryHistory?sortBy=externalId&sortDirection=descending") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(6)(queries.queryRecords.size) assertResult(queries.queryRecords.sortBy(_.externalId).reverse)(queries.queryRecords) } Get(s"/researcher/queryHistory?sortBy=stewardResponse") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(6)(queries.queryRecords.size) assertResult(queries.queryRecords.sortBy(_.stewardResponse))(queries.queryRecords) } Get(s"/researcher/queryHistory?sortBy=name") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(6)(queries.queryRecords.size) assertResult(queries.queryRecords.sortBy(_.name))(queries.queryRecords) } } "StewardService" should "return the list of a researcher's query history, using parameters for sorting, skip, and limit" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) createSixQueries() val sixQueries:Seq[OutboundShrineQuery] = { Get(s"/researcher/queryHistory") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries:QueryHistory = parse(queriesJson).extract[QueryHistory] assertResult(6)(queries.queryRecords.size) assertResult(queries.queryRecords.sortBy(_.externalId))(queries.queryRecords) queries.queryRecords } } Get(s"/researcher/queryHistory?skip=0&limit=3&sortBy=name") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(3)(queries.queryRecords.size) assertResult(queries.queryRecords)(sixQueries.sortBy(_.name).take(3)) } Get(s"/researcher/queryHistory?skip=2&limit=3&sortBy=name") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(3)(queries.queryRecords.size) assertResult(queries.queryRecords)(sixQueries.sortBy(_.name).drop(2).take(3)) } } "StewardService" should "return the list of a researcher's query history as Json, with filtered by topic" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) createSixQueries() Get(s"/researcher/queryHistory") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(6)(queries.queryRecords.size) } Get(s"/researcher/queryHistory/topic/1") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(3)(queries.queryRecords.size) } Get(s"/researcher/queryHistory/topic/1?skip=1&limit=2") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(2)(queries.queryRecords.size) } Get(s"/researcher/queryHistory/topic/2") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(3)(queries.queryRecords.size) } } "StewardService" should "return the list of a researcher's query history as Json, filtered by date" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) val startTime = System.currentTimeMillis() Thread.sleep(10) createSixQueries() Thread.sleep(10) val finishTime = System.currentTimeMillis() Get(s"/researcher/queryHistory") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(6)(queries.queryRecords.size) } Get(s"/researcher/queryHistory?minDate=$startTime&maxDate=$finishTime") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(6)(queries.queryRecords.size) } Get(s"/researcher/queryHistory?minDate=$startTime&maxDate=$startTime") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(0)(queries.queryRecords.size) } Get(s"/researcher/queryHistory?minDate=$finishTime&maxDate=$finishTime") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(0)(queries.queryRecords.size) } } "StewardService" should "return the list of a researcher's query history as Json, even including an unknown topic id" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) - StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(1),InboundShrineQuery(0,"test query for unknown topic","Can we get it back?")) + StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(1),InboundShrineQuery(0,"test query for unknown topic",queryContent)) Get(s"/researcher/queryHistory") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] - assertResult(List.empty)(QueryHistory(1,0,List(OutboundShrineQuery(1,0,"test query for unknown topic",researcherOutboundUser,None,"Can we get it back?",TopicState.unknownForUser.name,0))).differencesExceptTimes(queries)) + assertResult(diffs)(QueryHistory(1,0,List(OutboundShrineQuery(1,0,"test query for unknown topic",researcherOutboundUser,None,getItBack,TopicState.unknownForUser.name,0))).differencesExceptTimes(queries)) } } "StewardService" should "return the list of a researcher's query history as Json, sorted by topic name" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) createSixQueries() Get(s"/researcher/queryHistory?sortBy=topicName") ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(6)(queries.queryRecords.size) // assertResult(queries.queryRecords.sortBy(_.externalId))(queries.queryRecords) } } "StewardService" should "return counts of topics, total and by state" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) createFiveTopics() Get(s"/steward/statistics/topicsPerState") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) val statisticsJson = new String(body.data.toByteArray) val statistics = parse(statisticsJson).extract[TopicsPerState] assertResult(5)(statistics.total) assertResult(TopicsPerState(5,Seq((TopicState.pending.name,5))))(statistics) } } "StewardService" should "injest a researcher's request to study a topic" in { Post(s"/researcher/requestTopicAccess",InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(Accepted)(status) assertResult(1)(StewardDatabase.db.selectUsers.size) assertResult(UserRecord(researcherUserName,researcherUserName,false))(StewardDatabase.db.selectUsers.head) val topics = StewardDatabase.db.selectTopics(QueryParameters()) assertResult(1)(topics.size) val topic = topics.head assertResult(uncontroversialTopic.name)(topic.name) assertResult(uncontroversialTopic.description)(topic.description) assertResult(researcherUserName)(topic.createdBy) assertResult(TopicState.pending)(topic.state) } } "StewardService" should "injest a second request from a researcher request to study a new topic" in { val secondTopicName = "Liver" val secondTopicDescription = "Liver Study" StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) Post(s"/researcher/requestTopicAccess",InboundTopicRequest(secondTopicName,secondTopicDescription)) ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(Accepted)(status) assertResult(1)(StewardDatabase.db.selectUsers.size) assertResult(UserRecord(researcherUserName,researcherUserName,false))(StewardDatabase.db.selectUsers.head) val topics = StewardDatabase.db.selectTopics(QueryParameters()) assertResult(2)(topics.size) val firstTopic = topics(0) assertResult(uncontroversialTopic.name)(firstTopic.name) assertResult(uncontroversialTopic.description)(firstTopic.description) assertResult(researcherUserName)(firstTopic.createdBy) assertResult(TopicState.pending)(firstTopic.state) val secondTopic = topics(1) assertResult(secondTopicName)(secondTopic.name) assertResult(secondTopicDescription)(secondTopic.description) assertResult(researcherUserName)(secondTopic.createdBy) assertResult(TopicState.pending)(secondTopic.state) } } "StewardService" should " place new topics in the Approved state in auto-approve mode" in { StewardConfigSource.configForBlock(StewardConfigSource.createTopicsModeConfigKey,CreateTopicsMode.TopicsIgnoredJustLog.name,s"${CreateTopicsMode.TopicsIgnoredJustLog.name} test") { Post(s"/researcher/requestTopicAccess",InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(Accepted)(status) assertResult(1)(StewardDatabase.db.selectUsers.size) assertResult(UserRecord(researcherUserName, researcherUserName, false))(StewardDatabase.db.selectUsers.head) val topics = StewardDatabase.db.selectTopics(QueryParameters()) assertResult(1)(topics.size) val topic = topics.head assertResult(uncontroversialTopic.name)(topic.name) assertResult(uncontroversialTopic.description)(topic.description) assertResult(researcherUserName)(topic.createdBy) assertResult(TopicState.approved)(topic.state) } } } "StewardService" should "injest a researcher's request to edit a topic" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) val updatedDescription = "Really you should accept this" Post(s"/researcher/editTopicRequest/1",InboundTopicRequest(uncontroversialTopic.name,updatedDescription)) ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(Accepted)(status) val topics = StewardDatabase.db.selectTopics(QueryParameters()) assertResult(1)(topics.size) val topic = topics.head assertResult(uncontroversialTopic.name)(topic.name) assertResult(updatedDescription)(topic.description) assertResult(researcherUserName)(topic.createdBy) assertResult(researcherUserName)(topic.changedBy) assertResult(TopicState.pending)(topic.state) } } "StewardService" should "reject a researcher's request to edit a topic that does not exist" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) val updatedDescription = "Really you should accept this" Post(s"/researcher/editTopicRequest/2",InboundTopicRequest(uncontroversialTopic.name,updatedDescription)) ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(NotFound)(status) val topics = StewardDatabase.db.selectTopics(QueryParameters()) assertResult(1)(topics.size) val topic = topics.head assertResult(uncontroversialTopic.name)(topic.name) assertResult(uncontroversialTopic.description)(topic.description) assertResult(researcherUserName)(topic.createdBy) assertResult(researcherUserName)(topic.changedBy) assertResult(TopicState.pending)(topic.state) } } "StewardService" should "reject a researcher's request to edit an approved topic" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) StewardDatabase.db.changeTopicState(1,TopicState.approved,stewardUserName) val updatedDescription = "Really you should accept this" Post(s"/researcher/editTopicRequest/1",InboundTopicRequest(uncontroversialTopic.name,updatedDescription)) ~> addCredentials(researcherCredentials) ~> route ~> check { assertResult(Forbidden)(status) val topics = StewardDatabase.db.selectTopics(QueryParameters()) assertResult(1)(topics.size) val topic = topics.head assertResult(uncontroversialTopic.name)(topic.name) assertResult(uncontroversialTopic.description)(topic.description) assertResult(researcherUserName)(topic.createdBy) assertResult(stewardUserName)(topic.changedBy) assertResult(TopicState.approved)(topic.state) } } "StewardService" should "reject an attempt to edit a topic owned by a different user" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) val updatedDescription = "Really you should accept this" Post(s"/researcher/editTopicRequest/1",InboundTopicRequest(uncontroversialTopic.name,updatedDescription)) ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(Forbidden)(status) val topics = StewardDatabase.db.selectTopics(QueryParameters()) assertResult(1)(topics.size) val topic = topics.head assertResult(uncontroversialTopic.name)(topic.name) assertResult(uncontroversialTopic.description)(topic.description) assertResult(researcherUserName)(topic.createdBy) assertResult(TopicState.pending)(topic.state) } } "StewardService" should "return the full history of queries as Json" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) StewardDatabase.db.changeTopicState(1,TopicState.approved,stewardUserName) - StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(1),InboundShrineQuery(0,"test query","Can we get it back?")) + StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(1),InboundShrineQuery(0,"test query",queryContent)) Get(s"/steward/queryHistory") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] - assertResult(List.empty)(QueryHistory(1,0,List(OutboundShrineQuery(1,0,"test query",researcherOutboundUser,Some( - OutboundTopic(1,uncontroversialTopic.name,uncontroversialTopic.description,researcherOutboundUser,0,TopicState.approved.name,stewardOutboundUser,0)),"Can we get it back?",TopicState.approved.name,0))).differencesExceptTimes(queries)) + assertResult(diffs) ( + QueryHistory(1,0,List(OutboundShrineQuery(1,0,"test query",researcherOutboundUser,Some( + OutboundTopic(1,uncontroversialTopic.name,uncontroversialTopic.description,researcherOutboundUser,0, + TopicState.approved.name,stewardOutboundUser,0)),getItBack,TopicState.approved.name,0))) + .differencesExceptTimes(queries)) } } "StewardService" should "return the history of queries for a specific user as Json" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) StewardDatabase.db.changeTopicState(1,TopicState.approved,stewardUserName) - StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(1),InboundShrineQuery(0,"test query","Can we get it back?")) + StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(1),InboundShrineQuery(0,"test query",queryContent)) Get(s"/steward/queryHistory/user/${researcherUserName}") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] - assertResult(List.empty)(QueryHistory(1,0,List(OutboundShrineQuery(1,0,"test query",researcherOutboundUser,Some(OutboundTopic(1,uncontroversialTopic.name,uncontroversialTopic.description,researcherOutboundUser,0,TopicState.approved.name,stewardOutboundUser,0)),"Can we get it back?",TopicState.approved.name,0))).differencesExceptTimes(queries)) + assertResult(diffs)( + QueryHistory(1,0,List(OutboundShrineQuery(1,0,"test query",researcherOutboundUser,Some(OutboundTopic( + 1,uncontroversialTopic.name,uncontroversialTopic.description,researcherOutboundUser,0, + TopicState.approved.name,stewardOutboundUser,0)),getItBack,TopicState.approved.name,0))) + .differencesExceptTimes(queries)) } } "StewardService" should "return the query history as Json, filtered by topic" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) createSixQueries() Get(s"/steward/queryHistory") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(6)(queries.queryRecords.size) } Get(s"/steward/queryHistory/user/ben") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(6)(queries.queryRecords.size) } Get(s"/steward/queryHistory/topic/1") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(3)(queries.queryRecords.size) } Get(s"/steward/queryHistory/user/ben/topic/1") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(3)(queries.queryRecords.size) } Get(s"/steward/queryHistory/topic/1?skip=1&limit=2") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(2)(queries.queryRecords.size) } Get(s"/steward/queryHistory/topic/2") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) val queriesJson = new String(body.data.toByteArray) val queries = parse(queriesJson).extract[QueryHistory] assertResult(3)(queries.queryRecords.size) } } "StewardService" should "return counts of queries, total and by user" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) createSixQueries() Get(s"/steward/statistics/queriesPerUser") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) val statisticsJson = new String(body.data.toByteArray) val statistics = parse(statisticsJson).extract[QueriesPerUser] assertResult(6)(statistics.total) assertResult(QueriesPerUser(6,Seq((researcherOutboundUser,6))))(statistics) } } "StewardService" should "return the topics for a specific user as Json" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) Get(s"/steward/topics/user/${researcherUserName}") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) val topicsJson = new String(body.data.toByteArray) val topics:StewardsTopics = parse(topicsJson).extract[StewardsTopics] assertResult(true)(StewardsTopics(1,0,Seq(uncontroversialTopic)).sameExceptForTimes(topics)) } } "StewardService" should "return the topics for a specific user as Json, given skip and limit parameters" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) createFiveTopics() Get(s"/steward/topics/user/${researcherUserName}") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) val topicsJson = new String(body.data.toByteArray) val topics:StewardsTopics = parse(topicsJson).extract[StewardsTopics] assertResult(5)(topics.topics.size) } Get(s"/steward/topics/user/${researcherUserName}?skip=0&limit=3") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) val topicsJson = new String(body.data.toByteArray) val topics:StewardsTopics = parse(topicsJson).extract[StewardsTopics] assertResult(3)(topics.topics.size) } Get(s"/steward/topics/user/${researcherUserName}?skip=2&limit=3") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) val topicsJson = new String(body.data.toByteArray) val topics:StewardsTopics = parse(topicsJson).extract[StewardsTopics] assertResult(3)(topics.topics.size) } Get(s"/steward/topics/user/${researcherUserName}?skip=3&limit=4") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) val topicsJson = new String(body.data.toByteArray) val topics:StewardsTopics = parse(topicsJson).extract[StewardsTopics] assertResult(2)(topics.topics.size) } } "StewardService" should "return all of the topics" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) Get(s"/steward/topics") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) val topicsJson = new String(body.data.toByteArray) val topics = parse(topicsJson).extract[StewardsTopics] assertResult(true)(StewardsTopics(1,0,Seq(uncontroversialTopic)).sameExceptForTimes(topics)) } } "StewardService" should "return all of the pending topics" in { StewardDatabase.db.upsertUser(researcherUser) StewardDatabase.db.upsertUser(stewardUser) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) Get(s"/steward/topics?state=Pending") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) val topicsJson = new String(body.data.toByteArray) val topics = parse(topicsJson).extract[StewardsTopics] assertResult(true)(StewardsTopics(1,0,Seq(uncontroversialTopic)).sameExceptForTimes(topics)) } } "StewardService" should "approve a researcher's request to study a topic" in { StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) Post(s"/steward/approveTopic/topic/${uncontroversialTopic.id}") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) } } "StewardService" should "reject a researcher's request to study a topic" in { StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) Post(s"/steward/rejectTopic/topic/${uncontroversialTopic.id}") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(OK)(status) } } "A steward's attempt to approve or reject a topic that doesn't exist" should "report an error" in { Post(s"/steward/approveTopic/topic/${uncontroversialTopic.id}") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(UnprocessableEntity)(status) } Post(s"/steward/rejectTopic/topic/${uncontroversialTopic.id}") ~> addCredentials(stewardCredentials) ~> route ~> check { assertResult(UnprocessableEntity)(status) } } "StewardService" should "redirect several urls to client/index.html" in { Get() ~> route ~> check { status === PermanentRedirect header("Location") === "client/index.html" } Get("/") ~> route ~> check { status === PermanentRedirect header("Location") === "client/index.html" } Get("/index.html") ~> route ~> check { status === PermanentRedirect header("Location") === "client/index.html" } Get("/client") ~> route ~> check { status === PermanentRedirect header("Location") === "client/index.html" } Get("/client/") ~> route ~> check { status === PermanentRedirect header("Location") === "client/index.html" } } /* //CORS won't be turned on in the real server, so this test won't normally pass. "DataStewardService" should "support a CORS OPTIONS request" in { Options(s"/steward/topics") ~> //No credentials for Options, so no addCredentials(testCredentials) ~> route ~> check { assertResult(OK)(status) } } */ /* For some reason, the test engine isn't finding the static resources. I suspect it is testing with raw .class files , not an actual .war or .jar. Works on the actual server. "StewardService" should "serve up static files from resources/client" in { Get("/steward/client/test.txt") ~> route ~> check { assertResult(OK)(status) assertResult("Test file")(body.asString) } } */ def createFiveTopics(): Unit ={ StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest("slightlyControversial","who cares?")) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest("controversial","controversial")) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest("moderatelyControversial","more controversial than that")) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest("veryControversial","Just say no")) } def createSixQueries() = { StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest(uncontroversialTopic.name,uncontroversialTopic.description)) StewardDatabase.db.changeTopicState(1,TopicState.approved,stewardUserName) StewardDatabase.db.createRequestForTopicAccess(researcherUser,InboundTopicRequest("Forbidden topic","No way is the data steward going for this")) StewardDatabase.db.changeTopicState(2,TopicState.rejected,stewardUserName) - StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(1),InboundShrineQuery(0,"A test query","Can we get it back?")) - StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(2),InboundShrineQuery(1,"B forbidden query","Can we get it back?")) - StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(1),InboundShrineQuery(2," C test query","Can we get it back?")) - StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(2),InboundShrineQuery(3,"4 forbidden query","Can we get it back?")) - StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(1),InboundShrineQuery(4,"7 test query","Can we get it back?")) - StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(2),InboundShrineQuery(5,"% forbidden query","Can we get it back?")) + StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(1),InboundShrineQuery(0,"A test query",queryContent)) + StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(2),InboundShrineQuery(1,"B forbidden query",queryContent)) + StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(1),InboundShrineQuery(2," C test query",queryContent)) + StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(2),InboundShrineQuery(3,"4 forbidden query",queryContent)) + StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(1),InboundShrineQuery(4,"7 test query",queryContent)) + StewardDatabase.db.logAndCheckQuery(researcherUserName,Some(2),InboundShrineQuery(5,"% forbidden query",queryContent)) } } trait TestWithDatabase extends BeforeAndAfterEach { this:Suite => override def beforeEach() = { StewardDatabase.db.createTables() } override def afterEach() = { StewardDatabase.db.nextTopicId.set(1) StewardDatabase.db.dropTables() } } /* object OutboundTopic { val uncontroversialTopic = OutboundTopic(1,"UncontroversialKidneys","Study kidneys without controversy",OutboundUser.someOutboundResearcher,0L,TopicState.pending.name,OutboundUser.someOutboundResearcher,0L) val forbiddenTopicId = 0 } object OutboundShrineQuery extends (( StewardQueryId, ExternalQueryId, String, OutboundUser, Option[OutboundTopic], QueryContents, TopicStateName, Date) => OutboundShrineQuery) { val notSureAboutQueryContents:QueryContents = "Appropriate query contents" val someQueryRecord = OutboundShrineQuery(-5,-2,"Kidney Query",OutboundUser.someOutboundResearcher,Some(uncontroversialTopic),OutboundShrineQuery.notSureAboutQueryContents,TopicState.approved.name,System.currentTimeMillis()) } */ \ No newline at end of file diff --git a/commons/auth/src/main/scala/net/shrine/authentication/NotAuthenticatedException.scala b/commons/auth/src/main/scala/net/shrine/authentication/NotAuthenticatedException.scala index 53746ce1b..d981a9125 100644 --- a/commons/auth/src/main/scala/net/shrine/authentication/NotAuthenticatedException.scala +++ b/commons/auth/src/main/scala/net/shrine/authentication/NotAuthenticatedException.scala @@ -1,37 +1,36 @@ package net.shrine.authentication import net.shrine.authentication.AuthenticationResult.NotAuthenticated import net.shrine.problem.{AbstractProblem, ProblemSources} import scala.xml.NodeSeq /** * @author clint * @since Dec 13, 2013 */ final case class NotAuthenticatedException(domain: String, username: String,message: String, cause: Throwable) extends RuntimeException(message, cause) { def problem = NotAuthenticatedProblem(this) } object NotAuthenticatedException { def apply(na:NotAuthenticated):NotAuthenticatedException = NotAuthenticatedException(na.domain,na.username,na.message,na.cause.getOrElse(null)) } case class NotAuthenticatedProblem(nax:NotAuthenticatedException) extends AbstractProblem(ProblemSources.Qep){ override val summary = s"Can not authenticate ${nax.domain}:${nax.username}." override val throwable = Some(nax) override val description = s"Can not authenticate ${nax.domain}:${nax.username}. ${nax.getLocalizedMessage}" override val detailsXml: NodeSeq = NodeSeq.fromSeq(

{throwableDetail.getOrElse("")}
) - createAndLog } \ No newline at end of file diff --git a/commons/auth/src/main/scala/net/shrine/authorization/PmAuthorizerComponent.scala b/commons/auth/src/main/scala/net/shrine/authorization/PmAuthorizerComponent.scala index a4f5ee329..f1df2acca 100644 --- a/commons/auth/src/main/scala/net/shrine/authorization/PmAuthorizerComponent.scala +++ b/commons/auth/src/main/scala/net/shrine/authorization/PmAuthorizerComponent.scala @@ -1,115 +1,112 @@ package net.shrine.authorization import net.shrine.log.Loggable import scala.util.{Failure, Success, Try} import net.shrine.client.HttpResponse import net.shrine.i2b2.protocol.pm.GetUserConfigurationRequest import net.shrine.i2b2.protocol.pm.User import net.shrine.problem._ import net.shrine.protocol.AuthenticationInfo import net.shrine.protocol.ErrorResponse import scala.util.control.NonFatal /** * @author clint * @since Apr 5, 2013 */ trait PmAuthorizerComponent { self: PmHttpClientComponent with Loggable => import PmAuthorizerComponent._ //noinspection RedundantBlock object Pm { def parsePmResult(authn: AuthenticationInfo)(httpResponse: HttpResponse): Try[Either[ErrorResponse, User]] = { User.fromI2b2(httpResponse.body).map(Right(_)).recoverWith { case NonFatal(e) => { debug(s"Couldn't extract a User from '$httpResponse'") Try(Left(ErrorResponse.fromI2b2(httpResponse.body))) } }.recover { case NonFatal(e) => { val problem = CouldNotInterpretResponseFromPmCell(pmPoster.url,authn,httpResponse,e) LoggingProblemHandler.handleProblem(problem) Left(ErrorResponse(problem)) } } } def authorize(projectId: String, neededRoles: Set[String], authn: AuthenticationInfo): AuthorizationStatus = { val request = GetUserConfigurationRequest(authn) val responseAttempt: Try[HttpResponse] = Try { debug(s"Authorizing with PM cell at ${pmPoster.url}") pmPoster.post(request.toI2b2String) } val authStatusAttempt: Try[AuthorizationStatus with Product with Serializable] = responseAttempt.flatMap(parsePmResult(authn)).map { case Right(user) => { val managerUserOption = for { roles <- user.rolesByProject.get(projectId) if neededRoles.forall(roles.contains) } yield user managerUserOption.map(Authorized).getOrElse { NotAuthorized(MissingRequiredRoles(projectId,neededRoles,authn)) } } case Left(errorResponse) => { //todo remove when ErrorResponse gets its message info(s"ErrorResponse message '${errorResponse.errorMessage}' may not have carried through to the NotAuthorized object") NotAuthorized(errorResponse.problemDigest) } } authStatusAttempt match { case Success(s) => s case Failure(x) => NotAuthorized(CouldNotReachPmCell(pmPoster.url,authn,x)) } } } } object PmAuthorizerComponent { sealed trait AuthorizationStatus case class Authorized(user: User) extends AuthorizationStatus case class NotAuthorized(problemDigest: ProblemDigest) extends AuthorizationStatus { def toErrorResponse = ErrorResponse(problemDigest.summary,problemDigest) } object NotAuthorized { def apply(problem:Problem):NotAuthorized = NotAuthorized(problem.toDigest) } } case class MissingRequiredRoles(projectId: String, neededRoles: Set[String], authn: AuthenticationInfo) extends AbstractProblem(ProblemSources.Qep) { override val summary: String = s"User ${authn.domain}:${authn.username} is missing roles in project '$projectId'" override val description:String = s"User ${authn.domain}:${authn.username} does not have all the needed roles: ${neededRoles.map("'" + _ + "'").mkString(", ")} in the project '$projectId'" - createAndLog } case class CouldNotReachPmCell(pmUrl:String,authn: AuthenticationInfo,x:Throwable) extends AbstractProblem(ProblemSources.Qep) { override val throwable = Some(x) override val summary: String = s"Could not reach PM cell." override val description:String = s"Shrine encountered ${throwable.get} while attempting to reach the PM cell at $pmUrl for ${authn.domain}:${authn.username}." - createAndLog } case class CouldNotInterpretResponseFromPmCell(pmUrl:String,authn: AuthenticationInfo,httpResponse: HttpResponse,x:Throwable) extends AbstractProblem(ProblemSources.Qep) { override val throwable = Some(x) override def summary: String = s"Could not interpret response from PM cell." override def description: String = s"Shrine could not interpret the response from the PM cell at ${pmUrl} for ${authn.domain}:${authn.username}: due to ${throwable.get}" override val detailsXml =
Response is {httpResponse} {throwableDetail.getOrElse("")}
- createAndLog } \ No newline at end of file diff --git a/commons/auth/src/main/scala/net/shrine/authorization/StewardQueryAuthorizationService.scala b/commons/auth/src/main/scala/net/shrine/authorization/StewardQueryAuthorizationService.scala index cf5560a09..790ba61ba 100644 --- a/commons/auth/src/main/scala/net/shrine/authorization/StewardQueryAuthorizationService.scala +++ b/commons/auth/src/main/scala/net/shrine/authorization/StewardQueryAuthorizationService.scala @@ -1,236 +1,235 @@ package net.shrine.authorization import java.net.URL import javax.net.ssl.{KeyManager, SSLContext, X509TrustManager} import java.security.cert.X509Certificate import akka.io.IO import com.typesafe.config.{Config, ConfigFactory} import net.shrine.authorization.AuthorizationResult.{Authorized, NotAuthorized} import net.shrine.authorization.steward.{InboundShrineQuery, ResearchersTopics, TopicIdAndName} import net.shrine.log.Loggable import net.shrine.protocol.{ApprovedTopic, AuthenticationInfo, ErrorResponse, ReadApprovedQueryTopicsRequest, ReadApprovedQueryTopicsResponse, RunQueryRequest} import net.shrine.config.ConfigExtensions import org.json4s.native.JsonMethods.parse import org.json4s.{DefaultFormats, Formats} import akka.actor.ActorSystem import akka.util.Timeout import akka.pattern.ask import net.shrine.problem.{AbstractProblem, ProblemSources} import spray.can.Http import spray.can.Http.{HostConnectorInfo, HostConnectorSetup} import spray.http.{BasicHttpCredentials, HttpRequest, HttpResponse} import spray.http.StatusCodes.{OK, Unauthorized, UnavailableForLegalReasons} import spray.httpx.TransformerPipelineSupport.WithTransformation import spray.httpx.Json4sSupport import spray.client.pipelining.{Get, Post, addCredentials, sendReceive} import spray.io.{ClientSSLEngineProvider, PipelineContext, SSLContextProvider} import scala.concurrent.duration.{Duration, DurationInt, FiniteDuration} import scala.concurrent.{Await, Future} import scala.language.postfixOps /** * A QueryAuthorizationService that talks to the standard data steward application to learn about topics (intents) and check that a * shrine query can be run * * @author david * @since 4/2/15 */ final case class StewardQueryAuthorizationService(qepUserName:String, qepPassword:String, stewardBaseUrl:URL, defaultTimeout:FiniteDuration = 10 seconds) extends QueryAuthorizationService with Loggable with Json4sSupport { import system.dispatcher // execution context for futures implicit val system = ActorSystem("AuthorizationServiceActors",ConfigFactory.load("shrine")) //todo use shrine's config implicit val timeout:Timeout = Timeout.durationToTimeout(defaultTimeout)//10 seconds implicit def json4sFormats: Formats = DefaultFormats val qepCredentials = BasicHttpCredentials(qepUserName,qepPassword) def sendHttpRequest(httpRequest: HttpRequest):Future[HttpResponse] = { // Place a special SSLContext in scope here to be used by HttpClient. // It trusts all server certificates. // Most important - it will encrypt all of the traffic on the wire. implicit def trustfulSslContext: SSLContext = { object BlindFaithX509TrustManager extends X509TrustManager { def checkClientTrusted(chain: Array[X509Certificate], authType: String) = (info(s"Client asked BlindFaithX509TrustManager to check $chain for $authType")) def checkServerTrusted(chain: Array[X509Certificate], authType: String) = (info(s"Server asked BlindFaithX509TrustManager to check $chain for $authType")) def getAcceptedIssuers = Array[X509Certificate]() } val context = SSLContext.getInstance("TLS") context.init(Array[KeyManager](), Array(BlindFaithX509TrustManager), null) context } implicit def trustfulSslContextProvider: SSLContextProvider = { SSLContextProvider.forContext(trustfulSslContext) } class CustomClientSSLEngineProvider extends ClientSSLEngineProvider { def apply(pc: PipelineContext) = ClientSSLEngineProvider.default(trustfulSslContextProvider).apply(pc) } implicit def sslEngineProvider: ClientSSLEngineProvider = new CustomClientSSLEngineProvider val requestWithCredentials = httpRequest ~> addCredentials(qepCredentials) val responseFuture: Future[HttpResponse] = for { HostConnectorInfo(hostConnector, _) <- { val hostConnectorSetup = new HostConnectorSetup(httpRequest.uri.authority.host.address, httpRequest.uri.authority.port, sslEncryption = httpRequest.uri.scheme=="https")( sslEngineProvider = sslEngineProvider) IO(Http) ask hostConnectorSetup } response <- sendReceive(hostConnector).apply(requestWithCredentials) _ <- hostConnector ask Http.CloseAll } yield response responseFuture } /* todo to recycle connections with http://spray.io/documentation/1.2.3/spray-client/ if needed def sendHttpRequest(httpRequest: HttpRequest):Future[HttpResponse] = { import akka.io.IO import akka.pattern.ask import spray.can.Http val requestWithCredentials = httpRequest ~> addCredentials(qepCredentials) //todo failures via onFailure callbacks for{ sendR:SendReceive <- connectorSource response:HttpResponse <- sendR(requestWithCredentials) } yield response } val connectorSource: Future[SendReceive] = //Future[HttpRequest => Future[HttpResponse]] for ( //keep asking for a connector until you get one //todo correct URL // Http.HostConnectorInfo(connector, _) <- IO(Http) ? Http.HostConnectorSetup("www.spray.io", port = 8080) Http.HostConnectorInfo(connector, _) <- IO(Http) ? Http.HostConnectorSetup("localhost", port = 6060) ) yield sendReceive(connector) */ def sendAndReceive(httpRequest: HttpRequest,timeout:Duration = defaultTimeout):HttpResponse = { info("StewardQueryAuthorizationService will request "+httpRequest.uri) //todo someday log request and response val responseFuture = sendHttpRequest(httpRequest) val response:HttpResponse = Await.result(responseFuture,timeout) info("StewardQueryAuthorizationService received response with status "+response.status) response } //Contact a data steward and either return an Authorized or a NotAuthorized or throw an exception override def authorizeRunQueryRequest(runQueryRequest: RunQueryRequest): AuthorizationResult = { debug(s"authorizeRunQueryRequest started for ${runQueryRequest.queryDefinition.name}") val interpreted = runQueryRequest.topicId.fold( authorizeRunQueryRequestNoTopic(runQueryRequest) )( authorizeRunQueryRequestForTopic(runQueryRequest,_) ) debug(s"authorizeRunQueryRequest completed with $interpreted) for ${runQueryRequest.queryDefinition.name}") interpreted } def authorizeRunQueryRequestNoTopic(runQueryRequest: RunQueryRequest): AuthorizationResult = { val userName = runQueryRequest.authn.username val queryId = runQueryRequest.queryDefinition.name //xml's .text returns something that looks like xquery with backwards slashes. toString() returns xml. val queryForJson = InboundShrineQuery(runQueryRequest.networkQueryId,queryId,runQueryRequest.queryDefinition.toXml.toString()) val request = Post(s"$stewardBaseUrl/steward/qep/requestQueryAccess/user/$userName", queryForJson) val response:HttpResponse = sendAndReceive(request,runQueryRequest.waitTime) interpretAuthorizeRunQueryResponse(response) } def authorizeRunQueryRequestForTopic(runQueryRequest: RunQueryRequest,topicIdString:String): AuthorizationResult = { val userName = runQueryRequest.authn.username val queryId = runQueryRequest.queryDefinition.name //xml's .text returns something that looks like xquery with backwards slashes. toString() returns xml. val queryForJson = InboundShrineQuery(runQueryRequest.networkQueryId,queryId,runQueryRequest.queryDefinition.toXml.toString()) val request = Post(s"$stewardBaseUrl/steward/qep/requestQueryAccess/user/$userName/topic/$topicIdString", queryForJson) val response:HttpResponse = sendAndReceive(request,runQueryRequest.waitTime) debug(s"authorizeRunQueryRequestForTopic response is $response") interpretAuthorizeRunQueryResponse(response) } /** Interpret the response from the steward app. Primarily here for testing. */ def interpretAuthorizeRunQueryResponse(response:HttpResponse):AuthorizationResult = { response.status match { case OK => { val topicJson = new String(response.entity.data.toByteArray) debug(s"topicJson is $topicJson") val topic:Option[TopicIdAndName] = parse(topicJson).extractOpt[TopicIdAndName] debug(s"topic is $topic") Authorized(topic.map(x => (x.id,x.name))) } case UnavailableForLegalReasons => NotAuthorized(response.entity.asString) case Unauthorized => throw new AuthorizationException(s"steward rejected qep's login credentials. $response") case _ => throw new AuthorizationException(s"QueryAuthorizationService detected a problem: $response") } } //Either read the approved topics from a data steward or have an error response. override def readApprovedEntries(readTopicsRequest: ReadApprovedQueryTopicsRequest): Either[ErrorResponse, ReadApprovedQueryTopicsResponse] = { val userName = readTopicsRequest.authn.username val request = Get(s"$stewardBaseUrl/steward/qep/approvedTopics/user/$userName") val response:HttpResponse = sendAndReceive(request,readTopicsRequest.waitTime) if(response.status == OK) { val topicsJson = new String(response.entity.data.toByteArray) val topicsFromSteward: ResearchersTopics = parse(topicsJson).extract[ResearchersTopics] val topics: Seq[ApprovedTopic] = topicsFromSteward.topics.map(topic => ApprovedTopic(topic.id, topic.name)) Right(ReadApprovedQueryTopicsResponse(topics)) } else Left(ErrorResponse(ErrorStatusFromDataStewardApp(response,stewardBaseUrl))) } override def toString() = { super.toString().replaceAll(qepPassword,"REDACTED") } } object StewardQueryAuthorizationService { def apply(config:Config):StewardQueryAuthorizationService = StewardQueryAuthorizationService ( qepUserName = config.getString("qepUserName"), qepPassword = config.getString("qepPassword"), stewardBaseUrl = config.get("stewardBaseUrl", new URL(_)) ) } case class ErrorStatusFromDataStewardApp(response:HttpResponse,stewardBaseUrl:URL) extends AbstractProblem(ProblemSources.Qep) { override val summary: String = s"Data Steward App responded with status ${response.status}" override val description:String = s"The Data Steward App at ${stewardBaseUrl} responded with status ${response.status}, not OK." override val detailsXml =
Response is {response} {throwableDetail.getOrElse("")}
- createAndLog } \ No newline at end of file diff --git a/commons/auth/src/main/scala/net/shrine/authorization/steward/StewardModel.scala b/commons/auth/src/main/scala/net/shrine/authorization/steward/StewardModel.scala index 9f482580a..f1cdeb244 100644 --- a/commons/auth/src/main/scala/net/shrine/authorization/steward/StewardModel.scala +++ b/commons/auth/src/main/scala/net/shrine/authorization/steward/StewardModel.scala @@ -1,224 +1,231 @@ package net.shrine.authorization.steward import java.lang.reflect.Field import net.shrine.i2b2.protocol.pm.User import net.shrine.log.Loggable import net.shrine.protocol.Credential +import net.shrine.serialization.NodeSeqSerializer +import org.json4s.{DefaultFormats, Formats} import spray.http.{StatusCode, StatusCodes} +import spray.httpx.Json4sSupport import scala.util.{Failure, Try} +import scala.xml.NodeSeq /** * Data model for the data steward. * * @author dwalend * @since 1.19 */ //http response json case class OutboundTopic(id:TopicId, name:String, description:String, createdBy:OutboundUser, createDate:Date, state:TopicStateName, changedBy:OutboundUser, changeDate:Date ) { def differences(other:OutboundTopic):Seq[(String,Any,Any)] = { if (this == other) List() else { val fields = getClass.getDeclaredFields val names = fields.map(_.getName) def getFromField(field:Field,thing:OutboundTopic):Any = { field.setAccessible(true) field.get(thing) } val thisUnapplied = fields.map(getFromField(_,this)) val otherUnapplied = fields.map(getFromField(_,other)) val tuples = names.zip(thisUnapplied.zip(otherUnapplied)) def difference(name:String,one:Any,other:Any):Option[(String,Any,Any)] = { if(one == other) None else { Some((name,one,other)) } } tuples.map(x => difference(x._1,x._2._1,x._2._2)).to[Seq].flatten } } def differencesExceptTimes(other:OutboundTopic):Seq[(String,Any,Any)] = { differences(other).filterNot(x => x._1 == "createDate").filterNot(x => x._1 == "changeDate") } } case class OutboundUser(userName:UserName,fullName:String,roles:Set[Role] = Set(researcherRole)) {} object OutboundUser extends Loggable { def createResearcher(userName:UserName,fullName:String) = OutboundUser(userName,fullName) def createSteward(userName:UserName,fullName:String) = OutboundUser(userName,fullName,Set(researcherRole,stewardRole)) def createFromUser(user:User) = if( user.params.toList.contains((stewardRole,"true"))) createSteward(user.username,user.fullName) else createResearcher(user.username,user.fullName) /** If the user is unknown but has a user id, best effort is to guess this user is a researcher with userName as their full name*/ def createUnknownUser(userName:UserName) = { info(s"Creating an OutboundUser for unknown userName $userName") createResearcher(userName,userName) } } sealed case class TopicState(name:TopicStateName,statusCode:StatusCode,message:String) //todo split out TopicResponse from TopicState object TopicState { val pending = TopicState("Pending",StatusCodes.UnavailableForLegalReasons,"Topic pending data steward's approval") val approved = TopicState("Approved",StatusCodes.OK,"OK") val rejected = TopicState("Rejected",StatusCodes.UnavailableForLegalReasons,"Topic rejected by data steward") val unknownForUser = TopicState("Unknown For User",StatusCodes.UnprocessableEntity,"Topic unknown for user") val createTopicsModeRequiresTopic = TopicState("Required But Not Supplied",StatusCodes.UnprocessableEntity,"Topic required but not supplied") val namesToStates = Seq(pending,approved,rejected,unknownForUser,createTopicsModeRequiresTopic).map(x => x.name -> x).toMap def stateForStringOption(stringOption: Option[TopicStateName]): Try[Option[TopicState]] = { val notPresent: Try[Option[TopicState]] = Try(None) stringOption.fold(notPresent)((string: TopicStateName) => { val notInTheMap: Try[Option[TopicState]] = Failure(new IllegalArgumentException(s"No value for $string in $namesToStates")) namesToStates.get(string).fold(notInTheMap)(x => Try(Some(x))) }) } } case class OutboundShrineQuery( stewardId:StewardQueryId, externalId:ExternalQueryId, name:String, user:OutboundUser, topic:Option[OutboundTopic], - queryContents: QueryContents, + queryContents: NodeSeq, stewardResponse:TopicStateName, - date:Date) { + date:Date) +extends Json4sSupport +{ def differences(other:OutboundShrineQuery):Seq[(String,Any,Any)] = { if (this == other) List() else { val fields = getClass.getDeclaredFields val names = fields.map(_.getName) def getFromField(field:Field,thing:OutboundShrineQuery):Any = { field.setAccessible(true) field.get(thing) } val thisUnapplied = fields.map(getFromField(_,this)) val otherUnapplied = fields.map(getFromField(_,other)) val tuples = names.zip(thisUnapplied.zip(otherUnapplied)) def difference(name:String,one:Any,other:Any):Option[(String,Any,Any)] = { - if(one == other) None + // TODO: Remove this horrible string equality hack. + if(one == other || one.isInstanceOf[NodeSeq] && other.isInstanceOf[NodeSeq] && one.toString == other.toString) + None else { Some((name,one,other)) } } tuples.map(x => difference(x._1,x._2._1,x._2._2)).to[Seq].flatten } } def differencesExceptTimes(other:OutboundShrineQuery):Seq[(String,Any,Any)] = { - val diffWihtoutTimes = differences(other).filterNot(x => x._1 == "date").filterNot(x => x._1 == "topic") - + val diffWithoutTimes = differences(other).filterNot(x => x._1 == "date").filterNot(x => x._1 == "topic") val topicDiffs:Seq[(String,Any,Any)] = (topic,other.topic) match { case (None,None) => Seq.empty[(String,Any,Any)] case (Some(thisTopic),Some(otherTopic)) => thisTopic.differencesExceptTimes(otherTopic) case _ => Seq(("topic",this.topic,other.topic)) } - diffWihtoutTimes ++ topicDiffs + diffWithoutTimes ++ topicDiffs } - + override implicit def json4sFormats: Formats = DefaultFormats + new NodeSeqSerializer } case class QueriesPerUser(total:Int,queriesPerUser:Seq[(OutboundUser,Int)]) //todo rename QueriesPerResearcher case class TopicsPerState(total:Int,topicsPerState:Seq[(TopicStateName,Int)]) case class ResearchersTopics(userId:UserName,totalCount:Int,skipped:Int,topics:Seq[OutboundTopic]) { def sameExceptForTimes(researchersTopics: ResearchersTopics):Boolean = { (totalCount == researchersTopics.totalCount) && (skipped == researchersTopics.skipped) && (userId == researchersTopics.userId) && (topics.size == researchersTopics.topics.size) && topics.zip(researchersTopics.topics).forall(x => x._1.id == x._2.id) } } case class StewardsTopics(totalCount:Int,skipped:Int,topics:Seq[OutboundTopic]) { def sameExceptForTimes(stewardsTopics: StewardsTopics):Boolean = { (totalCount == stewardsTopics.totalCount) && (skipped == stewardsTopics.skipped) && (topics.size == stewardsTopics.topics.size) && topics.zip(stewardsTopics.topics).forall(x => x._1.id == x._2.id) } } -case class QueryHistory(totalCount:Int,skipped:Int,queryRecords:Seq[OutboundShrineQuery]){ +case class QueryHistory(totalCount:Int,skipped:Int,queryRecords:Seq[OutboundShrineQuery]) extends Json4sSupport { + def sameExceptForTimes(queryResponse: QueryHistory):Boolean = { (totalCount == queryResponse.totalCount) && (skipped == queryResponse.skipped) && (queryRecords.size == queryResponse.queryRecords.size) && queryRecords.zip(queryResponse.queryRecords).forall(x => x._1.differencesExceptTimes(x._2) == List.empty) } def differences(other:QueryHistory):Seq[(String,Any,Any)] = { if (this == other) List() else { val fields = getClass.getDeclaredFields val names = fields.map(_.getName) def getFromField(field:Field,thing:QueryHistory):Any = { field.setAccessible(true) field.get(thing) } val thisUnapplied = fields.map(getFromField(_,this)) val otherUnapplied = fields.map(getFromField(_,other)) val tuples = names.zip(thisUnapplied.zip(otherUnapplied)) def difference(name:String,one:Any,other:Any):Option[(String,Any,Any)] = { if(one == other) None else { Some((name,one,other)) } } tuples.map(x => difference(x._1,x._2._1,x._2._2)).to[Seq].flatten } } def differencesExceptTimes(other:QueryHistory):Seq[(String,Any,Any)] = { val normalDiffs:Seq[(String,Any,Any)] = differences(other).filterNot(x => x._1 == "queryRecords") val timeDiffs:Seq[(String,Any,Any)] = queryRecords.zip(other.queryRecords).flatMap(x => x._1.differencesExceptTimes(x._2)) - normalDiffs ++ timeDiffs } - + override implicit def json4sFormats: Formats = DefaultFormats + new NodeSeqSerializer } case class TopicIdAndName(id:String,name:String) //http request Json case class InboundShrineQuery( externalId:ExternalQueryId, name:String, queryContents: QueryContents) case class InboundTopicRequest(name:String,description:String) \ No newline at end of file diff --git a/commons/auth/src/main/scala/net/shrine/authorization/steward/package.scala b/commons/auth/src/main/scala/net/shrine/authorization/steward/package.scala index 192f4e096..b82f370c7 100644 --- a/commons/auth/src/main/scala/net/shrine/authorization/steward/package.scala +++ b/commons/auth/src/main/scala/net/shrine/authorization/steward/package.scala @@ -1,21 +1,22 @@ package net.shrine.authorization /** * @author david * @since 2/5/15 */ package object steward { type StewardQueryId = Long type ExternalQueryId = Long + type NetworkId = Long type QueryContents = String type TopicId = Int type TopicStateName = String type Date = Long type UserName = String type Role = String val researcherRole = "Researcher" val stewardRole = "DataSteward" val qepRole = "qep" } diff --git a/commons/auth/src/test/resources/dashboard.conf b/commons/auth/src/test/resources/dashboard.conf index 5d0008424..fce79054a 100644 --- a/commons/auth/src/test/resources/dashboard.conf +++ b/commons/auth/src/test/resources/dashboard.conf @@ -1,24 +1,28 @@ shrine { authenticate { usersource { //Bogus security for testing type = "ConfigUserSource" //Must be ConfigUserSource (for isolated testing) or PmUserSource (for everything else) researcher { username = "ben" password = "kapow" } steward { username = "dave" password = "kablam" } qep{ username = "qep" password = "trustme" } admin{ username = "keith" password = "shh!" } } } + + problem { + problemHandler = "net.shrine.problem.LoggingProblemHandler$" + } } \ No newline at end of file diff --git a/commons/auth/src/test/scala/net/shrine/authorization/PmAuthorizerComponentTest.scala b/commons/auth/src/test/scala/net/shrine/authorization/PmAuthorizerComponentTest.scala index 34d653ccc..1f7ff09b1 100644 --- a/commons/auth/src/test/scala/net/shrine/authorization/PmAuthorizerComponentTest.scala +++ b/commons/auth/src/test/scala/net/shrine/authorization/PmAuthorizerComponentTest.scala @@ -1,199 +1,198 @@ package net.shrine.authorization import net.shrine.log.Loggable import org.junit.Test import net.shrine.util.ShouldMatchersForJUnit import net.shrine.client.HttpClient import net.shrine.i2b2.protocol.pm.User import net.shrine.protocol.AuthenticationInfo import net.shrine.protocol.Credential import net.shrine.util.XmlUtil import net.shrine.client.Poster -import net.shrine.problem.TurnOffProblemConnector /** * @author clint * @since Apr 5, 2013 */ //noinspection UnitMethodIsParameterless,UnitMethodIsParameterless,EmptyParenMethodAccessedAsParameterless,ScalaUnnecessaryParentheses -final class PmAuthorizerComponentTest extends ShouldMatchersForJUnit with TurnOffProblemConnector { +final class PmAuthorizerComponentTest extends ShouldMatchersForJUnit { import PmAuthorizerComponentTest._ import PmAuthorizerComponent._ //Adds a bogus URL, for convenience private[this] def poster(httpClient: HttpClient) = Poster("", httpClient) @Test def testGoodResponseNotManager { val component = new TestPmAuthorizerComponent(poster(new LazyMockHttpClient(validUserResponseXml.toString))) val NotAuthorized(problemDigest) = component.Pm.authorize(projectId1, Set(User.Roles.Manager), authn) problemDigest should not be(null) problemDigest.codec should be (classOf[MissingRequiredRoles].getName) } @Test def testGoodResponseIsManager { val component = new TestPmAuthorizerComponent(poster(new LazyMockHttpClient(validUserResponseXml.toString))) val Authorized(user) = component.Pm.authorize(projectId2, Set(User.Roles.Manager), authn) user should not be(null) user.fullName should be(fullName) user.username should be(username) user.domain should be(domain) user.credential should be(authn.credential) } @Test def testErrorResponse { val component = new TestPmAuthorizerComponent(poster(new LazyMockHttpClient(i2b2ErrorXml.toString))) val NotAuthorized(problemDigest) = component.Pm.authorize(projectId1, Set.empty, authn) problemDigest should not be(null) problemDigest.codec should be (classOf[CouldNotInterpretResponseFromPmCell].getName) } @Test def testJunkResponse { val component = new TestPmAuthorizerComponent(poster(new LazyMockHttpClient("jahfskajhkjashfdkjashkfd"))) val NotAuthorized(problemDigest) = component.Pm.authorize(projectId1, Set.empty, authn) problemDigest should not be(null) problemDigest.codec should be (classOf[CouldNotReachPmCell].getName) } @Test def testResponseThatBlowsUp() { val component = new TestPmAuthorizerComponent(poster(new LazyMockHttpClient(throw new Exception with scala.util.control.NoStackTrace))) val NotAuthorized(problemDigest) = component.Pm.authorize(projectId1, Set.empty, authn) problemDigest should not be(null) problemDigest.codec should be (classOf[CouldNotReachPmCell].getName) } private val fullName = "Some Person" private val username = "some-user" private val domain = "some-place" private val password = "sal;dk;aslkd;" private val errorMessage = "Something blew up" private lazy val authn = AuthenticationInfo(domain, username, Credential(password, isToken = true)) private val projectId1 = "foo" private val projectId2 = "bar" private val params1 = Map("x" -> "1", "y" -> "2") private val params2 = Map("y" -> "2", "z" -> "3") private val roles1 = Set("a", "b", "c") private val roles2 = Set("MANAGER", "x", "y") private lazy val projects = Seq((projectId1, params1, roles1), (projectId2, params2, roles2)) private lazy val validUserResponseXml = XmlUtil.stripWhitespace { 1.1 2.4 SHRINE 1.3-compatible SHRINE 2011-04-08T16:21:12.251-04:00 DONE DEVELOPMENT http://www.i2b2.org {fullName} {username} {password} {domain} { projects.map { case (projectId, params, roles) => Demo Group { projectId } http://www.i2b2.org { params.map { case (name, value) => { value } } } { roles.map(r => { r }) } } } Data Repository http://localhost/crc REST Ontology Cell http://localhost/ont REST false 200 false } private val i2b2ErrorXml = XmlUtil.stripWhitespace { 1.1 2.4 SHRINE 1.3-compatible SHRINE 2011-04-08T16:21:12.251-04:00 { errorMessage } } } object PmAuthorizerComponentTest { final class TestPmAuthorizerComponent(override val pmPoster: Poster) extends PmAuthorizerComponent with PmHttpClientComponent with Loggable } \ No newline at end of file diff --git a/commons/protocol/src/main/scala/net/shrine/protocol/NonI2b2ableResponse.scala b/commons/protocol/src/main/scala/net/shrine/protocol/NonI2b2ableResponse.scala index d99b9a238..7fc8a38ff 100644 --- a/commons/protocol/src/main/scala/net/shrine/protocol/NonI2b2ableResponse.scala +++ b/commons/protocol/src/main/scala/net/shrine/protocol/NonI2b2ableResponse.scala @@ -1,23 +1,22 @@ package net.shrine.protocol import net.shrine.problem.{AbstractProblem, ProblemSources} import scala.xml.NodeSeq /** * @author clint * @since Apr 30, 2013 */ trait NonI2b2ableResponse { self: ShrineResponse => //Fail loudly here protected override def i2b2MessageBody: NodeSeq = ??? override def toI2b2: NodeSeq = ErrorResponse(NoI2b2AnalogExists(this.getClass)).toI2b2 } case class NoI2b2AnalogExists(claz:Class[_ <: NonI2b2ableResponse]) extends AbstractProblem(ProblemSources.Unknown) { override def summary: String = s"${ claz.getSimpleName } can't be marshalled to i2b2 XML, as it has no i2b2 analog" override def description: String = s"${ claz.getSimpleName } can't be marshalled to i2b2 XML, as it has no i2b2 analog" - createAndLog } \ No newline at end of file diff --git a/commons/protocol/src/main/scala/net/shrine/protocol/QueryResult.scala b/commons/protocol/src/main/scala/net/shrine/protocol/QueryResult.scala index 7c229768f..069ef0197 100644 --- a/commons/protocol/src/main/scala/net/shrine/protocol/QueryResult.scala +++ b/commons/protocol/src/main/scala/net/shrine/protocol/QueryResult.scala @@ -1,401 +1,400 @@ package net.shrine.protocol import javax.xml.datatype.XMLGregorianCalendar import net.shrine.log.Loggable import net.shrine.problem.{AbstractProblem, Problem, ProblemDigest, ProblemSources} import net.shrine.protocol.QueryResult.StatusType import scala.xml.NodeSeq import net.shrine.util.{NodeSeqEnrichments, OptionEnrichments, SEnum, Tries, XmlDateHelper, XmlUtil} import net.shrine.serialization.{I2b2Marshaller, XmlMarshaller} import scala.util.Try /** * @author Bill Simons * @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 * * NB: this is a case class to get a structural equality contract in hashCode and equals, mostly for testing */ final case class QueryResult ( resultId: Long, instanceId: Long, resultType: Option[ResultOutputType], setSize: Long, startDate: Option[XMLGregorianCalendar], endDate: Option[XMLGregorianCalendar], description: Option[String], statusType: StatusType, statusMessage: Option[String], problemDigest: Option[ProblemDigest] = None, breakdowns: Map[ResultOutputType,I2b2ResultEnvelope] = Map.empty ) extends XmlMarshaller with I2b2Marshaller with Loggable { //only used in tests def this( resultId: Long, instanceId: Long, resultType: ResultOutputType, setSize: Long, startDate: XMLGregorianCalendar, endDate: XMLGregorianCalendar, statusType: QueryResult.StatusType) = { this( resultId, instanceId, Option(resultType), setSize, Option(startDate), Option(endDate), None, //description statusType, None) //statusMessage } def this( resultId: Long, instanceId: Long, resultType: ResultOutputType, setSize: Long, startDate: XMLGregorianCalendar, endDate: XMLGregorianCalendar, description: String, statusType: QueryResult.StatusType) = { this( resultId, instanceId, Option(resultType), setSize, Option(startDate), Option(endDate), Option(description), statusType, None) //statusMessage } def resultTypeIs(testedResultType: ResultOutputType): Boolean = resultType match { case Some(rt) => rt == testedResultType case _ => false } import QueryResult._ //NB: Fragile, non-type-safe == def isError = statusType == StatusType.Error def elapsed: Option[Long] = { def inMillis(xmlGc: XMLGregorianCalendar) = xmlGc.toGregorianCalendar.getTimeInMillis for { start <- startDate end <- endDate } yield inMillis(end) - inMillis(start) } //Sorting isn't strictly necessary, but makes deterministic unit testing easier. //The number of breakdowns will be at most 4, so performance should not be an issue. private def sortedBreakdowns: Seq[I2b2ResultEnvelope] = { breakdowns.values.toSeq.sortBy(_.resultType.name) } override def toI2b2: NodeSeq = { import OptionEnrichments._ XmlUtil.stripWhitespace { { resultId } { instanceId } { description.toXml() } { resultType.fold( ResultOutputType.ERROR.toI2b2NameOnly("") ){ rt => if(rt.isBreakdown) rt.toI2b2NameOnly() else if (rt.isError) rt.toI2b2NameOnly() //The result type can be an error else if (statusType.isError) rt.toI2b2NameOnly() //Or the status type can be an error else rt.toI2b2 } } { setSize } { startDate.toXml() } { endDate.toXml() } { statusType } { statusType.toI2b2(this) } { //NB: Deliberately use Shrine XML format instead of the i2b2 one. Adding breakdowns to i2b2-format XML here is deviating from the i2b2 XSD schema in any case, //so if we're going to do that, let's produce saner XML. sortedBreakdowns.map(_.toXml.head).map(XmlUtil.renameRootTag("breakdown_data")) } } } override def toXml: NodeSeq = XmlUtil.stripWhitespace { import OptionEnrichments._ { resultId } { instanceId } { resultType.toXml(_.toXml) } { setSize } { startDate.toXml() } { endDate.toXml() } { description.toXml() } { statusType } { statusMessage.toXml() } { //Sorting isn't strictly necessary, but makes deterministic unit testing easier. //The number of breakdowns will be at most 4, so performance should not be an issue. sortedBreakdowns.map(_.toXml) } { problemDigest.map(_.toXml).getOrElse("") } } def withId(id: Long): QueryResult = copy(resultId = id) def withInstanceId(id: Long): QueryResult = copy(instanceId = id) def modifySetSize(f: Long => Long): QueryResult = withSetSize(f(setSize)) def withSetSize(size: Long): QueryResult = copy(setSize = size) def withDescription(desc: String): QueryResult = copy(description = Option(desc)) def withResultType(resType: ResultOutputType): QueryResult = copy(resultType = Option(resType)) def withBreakdown(breakdownData: I2b2ResultEnvelope) = copy(breakdowns = breakdowns + (breakdownData.resultType -> breakdownData)) def withBreakdowns(newBreakdowns: Map[ResultOutputType, I2b2ResultEnvelope]) = copy(breakdowns = newBreakdowns) } object QueryResult { final case class StatusType( name: String, isDone: Boolean, i2b2Id: Option[Int] = Some(-1), private val doToI2b2:(QueryResult => NodeSeq) = StatusType.defaultToI2b2) extends StatusType.Value { def isError = this == StatusType.Error def toI2b2(queryResult: QueryResult): NodeSeq = doToI2b2(queryResult) } object StatusType extends SEnum[StatusType] { private val defaultToI2b2: QueryResult => NodeSeq = { queryResult => val i2b2Id: Int = queryResult.statusType.i2b2Id.getOrElse{ throw new IllegalStateException(s"queryResult.statusType ${queryResult.statusType} has no i2b2Id") } { i2b2Id }{ queryResult.statusType.name } } val noMessage:NodeSeq = null val Error = StatusType("ERROR", isDone = true, None, { queryResult => (queryResult.statusMessage, queryResult.problemDigest) match { case (Some(msg),Some(pd)) => { if(msg != "ERROR") msg else pd.summary } ++ pd.toXml case (Some(msg),None) => { msg } case (None,Some(pd)) => { pd.summary } ++ pd.toXml case (None, None) => noMessage } }) val Finished = StatusType("FINISHED", isDone = true, Some(3)) //TODO: Can we use the same for Queued, Processing, and Incomplete? val Processing = StatusType("PROCESSING", isDone = false, Some(2)) //todo only used in tests val Queued = StatusType("QUEUED", isDone = false, Some(2)) val Incomplete = StatusType("INCOMPLETE", isDone = false, Some(2)) //TODO: What s should these have? Does anyone care? val Held = StatusType("HELD", isDone = false) val SmallQueue = StatusType("SMALL_QUEUE", isDone = false) val MediumQueue = StatusType("MEDIUM_QUEUE", isDone = false) val LargeQueue = StatusType("LARGE_QUEUE", isDone = false) val NoMoreQueue = StatusType("NO_MORE_QUEUE", isDone = false) } def extractLong(nodeSeq: NodeSeq)(elemName: String): Long = (nodeSeq \ elemName).text.toLong private def parseDate(lexicalRep: String): Option[XMLGregorianCalendar] = XmlDateHelper.parseXmlTime(lexicalRep).toOption def elemAt(path: String*)(xml: NodeSeq): NodeSeq = path.foldLeft(xml)(_ \ _) def asText(path: String*)(xml: NodeSeq): String = elemAt(path: _*)(xml).text.trim def asResultOutputTypeOption(elemNames: String*)(breakdownTypes: Set[ResultOutputType], xml: NodeSeq): Option[ResultOutputType] = { import ResultOutputType.valueOf val typeName = asText(elemNames: _*)(xml) valueOf(typeName) orElse valueOf(breakdownTypes)(typeName) } def extractResultOutputType(xml: NodeSeq)(parse: NodeSeq => Try[ResultOutputType]): Option[ResultOutputType] = { val attempt = parse(xml) attempt.toOption } def extractProblemDigest(xml: NodeSeq):Option[ProblemDigest] = { val subXml = xml \ "problem" if(subXml.nonEmpty) Some(ProblemDigest.fromXml(xml)) else None } def fromXml(breakdownTypes: Set[ResultOutputType])(xml: NodeSeq): QueryResult = { def extract(elemName: String): Option[String] = { Option((xml \ elemName).text.trim).filter(!_.isEmpty) } def extractDate(elemName: String): Option[XMLGregorianCalendar] = extract(elemName).flatMap(parseDate) val asLong = extractLong(xml) _ import NodeSeqEnrichments.Strictness._ import Tries.sequence def extractBreakdowns(elemName: String): Map[ResultOutputType, I2b2ResultEnvelope] = { //noinspection ScalaUnnecessaryParentheses val mapAttempt = for { subXml <- xml.withChild(elemName) envelopes <- sequence(subXml.map(I2b2ResultEnvelope.fromXml(breakdownTypes))) mappings = envelopes.map(envelope => (envelope.resultType -> envelope)) } yield Map.empty ++ mappings mapAttempt.getOrElse(Map.empty) } QueryResult( resultId = asLong("resultId"), instanceId = asLong("instanceId"), resultType = extractResultOutputType(xml \ "resultType")(ResultOutputType.fromXml), setSize = asLong("setSize"), startDate = extractDate("startDate"), endDate = extractDate("endDate"), description = extract("description"), statusType = StatusType.valueOf(asText("status")(xml)).get, //TODO: Avoid fragile .get call statusMessage = extract("statusMessage"), problemDigest = extractProblemDigest(xml), breakdowns = extractBreakdowns("resultEnvelope") ) } def fromI2b2(breakdownTypes: Set[ResultOutputType])(xml: NodeSeq): QueryResult = { def asLong = extractLong(xml) _ def asTextOption(path: String*): Option[String] = elemAt(path: _*)(xml).headOption.map(_.text.trim) def asXmlGcOption(path: String): Option[XMLGregorianCalendar] = asTextOption(path).filter(!_.isEmpty).flatMap(parseDate) val statusType = StatusType.valueOf(asText("query_status_type", "name")(xml)).get //TODO: Avoid fragile .get call val statusMessage: Option[String] = asTextOption("query_status_type", "description") val encodedProblemDigest = extractProblemDigest(xml \ "query_status_type") val problemDigest = if (encodedProblemDigest.isDefined) encodedProblemDigest else if (statusType.isError) Some(ErrorStatusFromCrc(statusMessage,xml.text).toDigest) else None case class Filling( resultType:Option[ResultOutputType], setSize:Long, startDate:Option[XMLGregorianCalendar], endDate:Option[XMLGregorianCalendar] ) val filling = if(!statusType.isError) { val resultType: Option[ResultOutputType] = extractResultOutputType(xml \ "query_result_type")(ResultOutputType.fromI2b2) val setSize = asLong("set_size") val startDate = asXmlGcOption("start_date") val endDate = asXmlGcOption("end_date") Filling(resultType,setSize,startDate,endDate) } else { val resultType = None val setSize = 0L val startDate = None val endDate = None Filling(resultType,setSize,startDate,endDate) } QueryResult( resultId = asLong("result_instance_id"), instanceId = asLong("query_instance_id"), resultType = filling.resultType, setSize = filling.setSize, startDate = filling.startDate, endDate = filling.endDate, description = asTextOption("description"), statusType = statusType, statusMessage = statusMessage, problemDigest = problemDigest ) } def errorResult(description: Option[String], statusMessage: String,problemDigest:ProblemDigest):QueryResult = { QueryResult( resultId = 0L, instanceId = 0L, resultType = None, setSize = 0L, startDate = None, endDate = None, description = description, statusType = StatusType.Error, statusMessage = Option(statusMessage), problemDigest = Option(problemDigest)) } def errorResult(description: Option[String], statusMessage: String,problem:Problem):QueryResult = { val problemDigest = problem.toDigest QueryResult( resultId = 0L, instanceId = 0L, resultType = None, setSize = 0L, startDate = None, endDate = None, description = description, statusType = StatusType.Error, statusMessage = Option(statusMessage), problemDigest = Option(problemDigest)) } /** * For reconstituting errorResults from a database */ def errorResult(description:Option[String], statusMessage:String, codec:String,stampText:String, summary:String, digestDescription:String,detailsXml:NodeSeq): QueryResult = { // This would require parsing the stamp text to change, and without a standard locale that's nigh impossible. // If this is replaced with real problems, then this can be addressed then. For now, passing on zero is the best bet. val problemDigest = ProblemDigest(codec,stampText,summary,digestDescription,detailsXml,0) QueryResult( resultId = 0L, instanceId = 0L, resultType = None, setSize = 0L, startDate = None, endDate = None, description = description, statusType = StatusType.Error, statusMessage = Option(statusMessage), problemDigest = Option(problemDigest)) } } case class ErrorStatusFromCrc(messageFromCrC:Option[String], xmlResponseFromCrc: String) extends AbstractProblem(ProblemSources.Adapter) { override val summary: String = "The I2B2 CRC reported an internal error." override val description:String = s"The I2B2 CRC responded with status type ERROR ${messageFromCrC.fold(" but no message")(message => s"and a message of '$message'")}" override val detailsXml =

CRC's Response is {xmlResponseFromCrc}
- createAndLog } diff --git a/commons/protocol/src/test/resources/dashboard.conf b/commons/protocol/src/test/resources/dashboard.conf new file mode 100644 index 000000000..e280c18e3 --- /dev/null +++ b/commons/protocol/src/test/resources/dashboard.conf @@ -0,0 +1,5 @@ +shrine { + problem { + problemHandler = "net.shrine.problem.LoggingProblemHandler$" + } +} \ No newline at end of file diff --git a/commons/protocol/src/test/scala/net/shrine/protocol/AggregatedRunQueryResponseTest.scala b/commons/protocol/src/test/scala/net/shrine/protocol/AggregatedRunQueryResponseTest.scala index 13f5fdfe3..8659e3968 100644 --- a/commons/protocol/src/test/scala/net/shrine/protocol/AggregatedRunQueryResponseTest.scala +++ b/commons/protocol/src/test/scala/net/shrine/protocol/AggregatedRunQueryResponseTest.scala @@ -1,316 +1,316 @@ package net.shrine.protocol -import net.shrine.problem.{TestProblem, TurnOffProblemConnector} +import net.shrine.problem.TestProblem import scala.xml.NodeSeq import org.junit.Test import net.shrine.protocol.query.QueryDefinition import net.shrine.protocol.query.Term import net.shrine.util.XmlDateHelper import net.shrine.util.XmlUtil /** * * * @author Justin Quan * @see http://chip.org * @since 8/12/11 */ //noinspection EmptyParenMethodOverridenAsParameterless,EmptyParenMethodAccessedAsParameterless,UnitMethodIsParameterless -final class AggregatedRunQueryResponseTest extends ShrineResponseI2b2SerializableValidator with TurnOffProblemConnector { +final class AggregatedRunQueryResponseTest extends ShrineResponseI2b2SerializableValidator { private val queryId = 1L private val queryName = "queryName" private val userId = "user" private val groupId = "group" private val createDate = XmlDateHelper.now private val requestQueryDef = QueryDefinition(queryName, Term("""\\i2b2\i2b2\Demographics\Age\0-9 years old\""")) private val queryInstanceId = 2L private val resultId = 3L private val setSize = 10L private val startDate = createDate private val endDate = createDate private val resultId2 = 4L private val resultType1 = ResultOutputType.PATIENT_COUNT_XML private val resultType2 = ResultOutputType.PATIENT_COUNT_XML private val statusType = QueryResult.StatusType.Finished override def messageBody = { DONE { queryId } { queryName } { userId } { groupId } { createDate } { requestQueryDef.toI2b2 } { queryInstanceId } { queryId } { userId } { groupId } 6 COMPLETED COMPLETED { resultId } { queryInstanceId } 4 { resultType1 } CATNUM LA Number of patients { setSize } { startDate } { endDate } { statusType } 3 FINISHED { resultId2 } { queryInstanceId } 4 { resultType2 } CATNUM LA Number of patients { setSize } { startDate } { endDate } { statusType } 3 FINISHED } private val qr1 = QueryResult( resultId = resultId, instanceId = queryInstanceId, resultType = Option(resultType1), setSize = setSize, startDate = Option(createDate), endDate = Option(createDate), description = None, statusType = statusType, statusMessage = Some(statusType.name), problemDigest = None, breakdowns = Map.empty) private val qr2 = QueryResult( resultId = resultId2, instanceId = queryInstanceId, resultType = Option(resultType2), setSize = setSize, startDate = Option(createDate), endDate = Option(createDate), description = None, statusType = statusType, statusMessage = Some(statusType.name), problemDigest = None, breakdowns = Map.empty) private val runQueryResponse = XmlUtil.stripWhitespace { { queryId } { queryInstanceId } { userId } { groupId } { requestQueryDef.toXml } { createDate } { Seq(qr1, qr2).map(_.toXml) } } import DefaultBreakdownResultOutputTypes.{ values => breakdownTypes } @Test def testFromXml { val actual = AggregatedRunQueryResponse.fromXml(breakdownTypes.toSet)(runQueryResponse).get actual.queryId should equal(queryId) actual.createDate should equal(createDate) actual.userId should equal(userId) actual.groupId should equal(groupId) actual.requestXml should equal(requestQueryDef) actual.queryInstanceId should equal(queryInstanceId) actual.results should equal(Seq(qr1, qr2)) actual.queryName should equal(queryName) } @Test def testToXml { AggregatedRunQueryResponse(queryId, createDate, userId, groupId, requestQueryDef, queryInstanceId, Seq(qr1, qr2)).toXmlString should equal(runQueryResponse.toString) } @Test def testFromI2b2 { val translatedResponse = AggregatedRunQueryResponse.fromI2b2(breakdownTypes.toSet)(response).get translatedResponse.queryId should equal(queryId) translatedResponse.createDate should equal(createDate) translatedResponse.userId should equal(userId) translatedResponse.groupId should equal(groupId) translatedResponse.requestXml should equal(requestQueryDef) translatedResponse.queryInstanceId should equal(queryInstanceId) translatedResponse.results should equal(Seq(qr1, qr2)) translatedResponse.queryName should equal(queryName) } @Test def testFromI2b2StringRequestXml { val hackToProduceXml = new HasResponse { //Produces a message body where the tag contains escaped XML as a String, as is produced by the CRC override def messageBody: NodeSeq = { DONE { queryId } { queryName } { userId } { groupId } { createDate } { requestQueryDef.toI2b2String } { queryInstanceId } { queryId } { userId } { groupId } 6 COMPLETED COMPLETED { resultId } { queryInstanceId } { resultType1 } 1 LIST LA Patient list { setSize } { startDate } { endDate } { statusType } 3 FINISHED { resultId2 } { queryInstanceId } { resultType2 } 4 CATNUM LA Number of patients { setSize } { startDate } { endDate } { statusType } 3 FINISHED } } doTestFromI2b2(hackToProduceXml.response, requestQueryDef) } private def doTestFromI2b2(i2b2Response: NodeSeq, expectedQueryDef: AnyRef) { val translatedResponse = AggregatedRunQueryResponse.fromI2b2(breakdownTypes.toSet)(i2b2Response).get translatedResponse.queryId should equal(queryId) translatedResponse.createDate should equal(createDate) translatedResponse.userId should equal(userId) translatedResponse.groupId should equal(groupId) translatedResponse.requestXml should equal(expectedQueryDef) translatedResponse.queryInstanceId should equal(queryInstanceId) translatedResponse.results should equal(Seq(qr1, qr2)) translatedResponse.queryName should equal(queryName) } @Test def testResultsPartitioned { val actual = AggregatedRunQueryResponse.fromXml(breakdownTypes.toSet)(runQueryResponse).get { val (nonErrors, errors) = actual.resultsPartitioned nonErrors should equal(Seq(qr1, qr2)) errors.size should equal(0) } val error = QueryResult.errorResult(None, "something broke", TestProblem()) { val withOnlyErrors = actual.withResults(Seq(error, error)) val (nonErrors, errors) = withOnlyErrors.resultsPartitioned nonErrors.size should equal(0) errors should equal(Seq(error, error)) } { val withErrorsAndSuccesses = actual.withResults(actual.results ++ Seq(error, error)) val (nonErrors, errors) = withErrorsAndSuccesses.resultsPartitioned nonErrors should equal(Seq(qr1, qr2)) errors should equal(Seq(error, error)) } { val withNoResults = actual.withResults(Nil) val (nonErrors, errors) = withNoResults.resultsPartitioned nonErrors.size should equal(0) errors.size should equal(0) } } @Test def testToI2b2 { AggregatedRunQueryResponse(queryId, createDate, userId, groupId, requestQueryDef, queryInstanceId, Seq(qr1, qr2)).toI2b2String should equal(response.toString) } } \ No newline at end of file diff --git a/commons/protocol/src/test/scala/net/shrine/protocol/ErrorResponseTest.scala b/commons/protocol/src/test/scala/net/shrine/protocol/ErrorResponseTest.scala index 18bd5a631..42fb78fe5 100644 --- a/commons/protocol/src/test/scala/net/shrine/protocol/ErrorResponseTest.scala +++ b/commons/protocol/src/test/scala/net/shrine/protocol/ErrorResponseTest.scala @@ -1,159 +1,159 @@ package net.shrine.protocol import junit.framework.TestCase -import net.shrine.problem.{TestProblem, TurnOffProblemConnector} +import net.shrine.problem.TestProblem import net.shrine.util.ShouldMatchersForJUnit import org.junit.Test import scala.xml.NodeSeq import net.shrine.util.XmlUtil /** * @author clint * @since Apr 5, 2013 */ -final class ErrorResponseTest extends TestCase with ShouldMatchersForJUnit with TurnOffProblemConnector { +final class ErrorResponseTest extends TestCase with ShouldMatchersForJUnit { val message = "foo" val problem = TestProblem(message) val resp = ErrorResponse(problem) val expectedShrineXml = XmlUtil.stripWhitespace { { message } {problem.toDigest.toXml} } val expectedI2b2Xml = XmlUtil.stripWhitespace { 1.1 2.4 SHRINE 1.3-compatible SHRINE 2011-04-08T16:21:12.251-04:00 { message } {problem.toDigest.toXml} } @Test def testToXml() = doTestToXml(expectedShrineXml, _.toXml) @Test def testToI2b2() = doTestToXml(expectedI2b2Xml, _.toI2b2) @Test def testToXmlRoundTrip() = doTestRoundTrip(_.toXml, ErrorResponse.fromXml) @Test def testToI2b2RoundTrip() = doTestRoundTrip(_.toI2b2, ErrorResponse.fromI2b2) @Test def testFromXml() = doTestFromXml(expectedShrineXml, ErrorResponse.fromXml) @Test def testFromI2b2() = doTestFromXml(expectedI2b2Xml, ErrorResponse.fromI2b2) //NB: See https://open.med.harvard.edu/jira/browse/SHRINE-745 @Test def testFromI2b2AlternateFormat() { val altI2b2Xml = XmlUtil.stripWhitespace { 1.1 2.4 edu.harvard.i2b2.crc 1.5 i2b2 Hive i2b2_QueryTool 0.2 i2b2 Hive 1 i2b2 Log information DONE Query result instance id 3126 not found } val resp = ErrorResponse.fromI2b2(altI2b2Xml) resp should not be null resp.errorMessage should equal("Query result instance id 3126 not found") } private def doTestFromXml(xml: NodeSeq, deserialize: NodeSeq => ErrorResponse) { intercept[Exception] { deserialize(null) } intercept[Exception] { deserialize() } intercept[Exception] { //Correct I2b2 XML structure, wrong status type deserialize({ message }) } val deserialized = deserialize(xml) deserialized.problemDigest.detailsXml should equal(resp.problemDigest.detailsXml) deserialized.problemDigest should equal(resp.problemDigest) deserialized should equal(resp) } private def doTestToXml(expected: NodeSeq, serialize: ErrorResponse => NodeSeq) { val xml = serialize(resp) xml.toString should equal(expected.toString()) } private def doTestRoundTrip(serialize: ErrorResponse => NodeSeq, deserialize: NodeSeq => ErrorResponse) { val unmarshalled = deserialize(serialize(resp)) unmarshalled should equal(resp) } } \ No newline at end of file diff --git a/commons/protocol/src/test/scala/net/shrine/protocol/NonI2b2ableResponseTest.scala b/commons/protocol/src/test/scala/net/shrine/protocol/NonI2b2ableResponseTest.scala index af55a1b99..58c168184 100644 --- a/commons/protocol/src/test/scala/net/shrine/protocol/NonI2b2ableResponseTest.scala +++ b/commons/protocol/src/test/scala/net/shrine/protocol/NonI2b2ableResponseTest.scala @@ -1,32 +1,31 @@ package net.shrine.protocol import junit.framework.TestCase -import net.shrine.problem.TurnOffProblemConnector import net.shrine.util.ShouldMatchersForJUnit import org.junit.Test /** * @author clint * @date Apr 30, 2013 */ -final class NonI2b2ableResponseTest extends TestCase with ShouldMatchersForJUnit with TurnOffProblemConnector { +final class NonI2b2ableResponseTest extends TestCase with ShouldMatchersForJUnit { object TestResponse extends ShrineResponse with NonI2b2ableResponse { def messageBody = i2b2MessageBody override def toXml = ??? } @Test def testI2b2MessageBody { intercept[Error] { TestResponse.messageBody } } @Test def testToI2b2 { val testResponseClassName = TestResponse.getClass.getSimpleName ErrorResponse.fromI2b2(TestResponse.toI2b2).errorMessage.contains(testResponseClassName) should be(true) } } \ No newline at end of file diff --git a/commons/protocol/src/test/scala/net/shrine/protocol/QueryResultTest.scala b/commons/protocol/src/test/scala/net/shrine/protocol/QueryResultTest.scala index 1414e6cdc..b41a6300f 100644 --- a/commons/protocol/src/test/scala/net/shrine/protocol/QueryResultTest.scala +++ b/commons/protocol/src/test/scala/net/shrine/protocol/QueryResultTest.scala @@ -1,499 +1,499 @@ package net.shrine.protocol -import net.shrine.problem.{TestProblem, TurnOffProblemConnector} +import net.shrine.problem.TestProblem import net.shrine.util.ShouldMatchersForJUnit import org.junit.Test import net.shrine.util.XmlUtil import net.shrine.util.XmlDateHelper import net.shrine.util.XmlGcEnrichments import scala.xml.NodeSeq /** * @author Bill Simons * @author clint * @since 8/19/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 */ //noinspection EmptyParenMethodAccessedAsParameterless,NameBooleanParameters final class QueryResultTest extends ShouldMatchersForJUnit with XmlRoundTripper[QueryResult] - with I2b2SerializableValidator with TurnOffProblemConnector + with I2b2SerializableValidator { private val date = XmlDateHelper.now private val resultId = 1L private val instanceId = 2L private val resultType = ResultOutputType.PATIENT_COUNT_XML private val setSize = 12L private val statusType = QueryResult.StatusType.Finished private val description = "description" private val statusMessage = "lakjdalsjd" private val queryResult = QueryResult(resultId, instanceId, Some(resultType), setSize, Option(date), Option(date), Option(description), statusType, Option(statusType.name)) import DefaultBreakdownResultOutputTypes.{ values => breakdownTypes, _ } private val resultWithBreakDowns = queryResult.copy( statusMessage = Some(statusMessage), breakdowns = Map(PATIENT_AGE_COUNT_XML -> I2b2ResultEnvelope(PATIENT_AGE_COUNT_XML, Map("foo" -> 1L, "bar" -> 2L)), PATIENT_RACE_COUNT_XML -> I2b2ResultEnvelope(PATIENT_RACE_COUNT_XML, Map("nuh" -> 3L, "zuh" -> 4L)), PATIENT_VITALSTATUS_COUNT_XML -> I2b2ResultEnvelope(PATIENT_VITALSTATUS_COUNT_XML, Map("blarg" -> 5L, "glarg" -> 6L)), PATIENT_GENDER_COUNT_XML -> I2b2ResultEnvelope(PATIENT_GENDER_COUNT_XML, Map("huh" -> 7L, "yeah" -> 8)))) private val expectedWhenBreakdownsArePresent = XmlUtil.stripWhitespace { { resultId } { instanceId } { resultType.toXml } { setSize } { date } { date } { description } { statusType } { statusMessage } { PATIENT_AGE_COUNT_XML } bar 2 foo 1 { PATIENT_GENDER_COUNT_XML } huh 7 yeah 8 { PATIENT_RACE_COUNT_XML } nuh 3 zuh 4 { PATIENT_VITALSTATUS_COUNT_XML } blarg 5 glarg 6 }.toString private val expectedI2b2Xml = XmlUtil.stripWhitespace { { resultId } { instanceId } { description } 4 { resultType } CATNUMLANumber of patients { setSize } { date } { date } { statusType } 3FINISHED }.toString private val expectedI2b2XmlWithBreakdowns = XmlUtil.stripWhitespace { { resultId } { instanceId } { description } { resultType.toI2b2 } { setSize } { date } { date } { statusType } 3FINISHED { PATIENT_AGE_COUNT_XML } bar 2 foo 1 { PATIENT_GENDER_COUNT_XML } huh 7 yeah 8 { PATIENT_RACE_COUNT_XML } nuh 3 zuh 4 { PATIENT_VITALSTATUS_COUNT_XML } blarg 5 glarg 6 }.toString private val expectedI2b2ErrorXml = XmlUtil.stripWhitespace { 0 0 { description } 0 ERROR { statusMessage } }.toString //NB: See https://open.med.harvard.edu/jira/browse/SHRINE-745 private val expectedI2b2IncompleteXml = XmlUtil.stripWhitespace { 0 0 { description } 0 INCOMPLETE { statusMessage } }.toString import scala.xml.XML.loadString //NB: See https://open.med.harvard.edu/jira/browse/SHRINE-745 @Test def testParseIncomplete() { val qr = QueryResult.fromI2b2(breakdownTypes.toSet)(loadString(expectedI2b2IncompleteXml)) qr.statusType should be(QueryResult.StatusType.Incomplete) } @Test def testElapsed() { queryResult.copy(startDate = None).elapsed should be(None) queryResult.copy(endDate = None).elapsed should be(None) queryResult.copy(startDate = None, endDate = None).elapsed should be(None) { val now = XmlDateHelper.now queryResult.copy(startDate = Some(now), endDate = Some(now)).elapsed should equal(Some(0L)) } { val start = XmlDateHelper.now val delta = 123L import XmlGcEnrichments._ import scala.concurrent.duration._ val end = start + delta.milliseconds queryResult.copy(startDate = Some(start), endDate = Some(end)).elapsed should equal(Some(delta)) } } @Test def testIsError() { queryResult.isError should be(false) queryResult.copy(statusType = QueryResult.StatusType.Processing).isError should be(false) queryResult.copy(statusType = QueryResult.StatusType.Finished).isError should be(false) queryResult.copy(statusType = QueryResult.StatusType.Queued).isError should be(false) queryResult.copy(statusType = QueryResult.StatusType.Incomplete).isError should be(false) queryResult.copy(statusType = QueryResult.StatusType.Error).isError should be(true) } @Test def testToXml() { val queryResultForShrine = queryResult.copy(statusMessage = Some(statusMessage)) val expectedWhenNoBreakdowns = XmlUtil.stripWhitespace { { resultId } { instanceId } { resultType.toXml } { setSize } { date } { date } { description } { statusType } { statusMessage } }.toString queryResultForShrine.copy(statusMessage = Some(statusMessage)).toXmlString should equal(expectedWhenNoBreakdowns) val expectedWhenNoStartDate = XmlUtil.stripWhitespace { { resultId } { instanceId } { resultType.toXml } { setSize } { date } { description } { statusType } { statusMessage } }.toString queryResultForShrine.copy(startDate = None).toXmlString should equal(expectedWhenNoStartDate) val expectedWhenNoEndDate = XmlUtil.stripWhitespace { { resultId } { instanceId } { resultType.toXml } { setSize } { date } { description } { statusType } { statusMessage } }.toString queryResultForShrine.copy(endDate = None).toXmlString should equal(expectedWhenNoEndDate) val expectedWhenNoDescription = XmlUtil.stripWhitespace { { resultId } { instanceId } { resultType.toXml } { setSize } { date } { date } { statusType } { statusMessage } }.toString queryResultForShrine.copy(description = None).toXmlString should equal(expectedWhenNoDescription) val expectedWhenNoStatusMessage = XmlUtil.stripWhitespace { { resultId } { instanceId } { resultType.toXml } { setSize } { date } { date } { description } { statusType } }.toString queryResult.copy(statusMessage = None).toXmlString should equal(expectedWhenNoStatusMessage) resultWithBreakDowns.toXmlString should equal(expectedWhenBreakdownsArePresent) } @Test def testFromXml() { QueryResult.fromXml(breakdownTypes.toSet)(loadString(expectedWhenBreakdownsArePresent)) should equal(resultWithBreakDowns) } @Test def testShrineRoundTrip() = { QueryResult.fromXml(breakdownTypes.toSet)(resultWithBreakDowns.toXml) should equal(resultWithBreakDowns) } private def compareIgnoringBreakdowns(actual: QueryResult, expected: QueryResult) { //Ignore breakdowns field, since this can't be serialized to i2b2 format as part of a actual.breakdowns should equal(Map.empty) actual.description should equal(expected.description) actual.endDate should equal(expected.endDate) actual.instanceId should equal(expected.instanceId) actual.resultId should equal(expected.resultId) actual.resultType should equal(expected.resultType) actual.setSize should equal(expected.setSize) actual.startDate should equal(expected.startDate) actual.statusMessage should equal(expected.statusMessage) actual.statusType should equal(expected.statusType) } @Test def testI2b2RoundTrip() = { //NB: Needed because i2b2 handles status messages differently. In the error case, statusMessage is //descriptive; otherwise, it's the all-caps name of the status type. This is different from how //Shrine creates and parses statusMessage XML, so we need a new QueryResult here. (Previously, we //could use the same one, since we were ignoring statusMessage and description when unmarshalling //from i2b2 format.) val newStatusMessage = Some(resultWithBreakDowns.statusType.name) val resultWithBreakDownsForI2b2 = resultWithBreakDowns.copy(statusMessage = newStatusMessage) val unmarshalled = QueryResult.fromI2b2(breakdownTypes.toSet)(resultWithBreakDownsForI2b2.toI2b2) compareIgnoringBreakdowns(unmarshalled, resultWithBreakDownsForI2b2) } @Test def testFromI2b2() { compareIgnoringBreakdowns(QueryResult.fromI2b2(breakdownTypes.toSet)(loadString(expectedI2b2Xml)), queryResult) } @Test def testFromI2b2WithErrors() { val errorResult = QueryResult.errorResult(Some(description), statusMessage,TestProblem()) val actual = QueryResult.fromI2b2(breakdownTypes.toSet)(loadString(expectedI2b2ErrorXml)) compareIgnoringBreakdowns(actual, errorResult) } @Test def testToI2b2() { queryResult.toI2b2String should equal(expectedI2b2Xml) } @Test def testToI2b2WithBreakdowns() { resultWithBreakDowns.toI2b2String should equal(expectedI2b2XmlWithBreakdowns) } @Test def testToI2b2AllStatusTypes(): Unit = { def doTest(statusType: QueryResult.StatusType) { val expectedI2b2Xml = XmlUtil.stripWhitespace { { resultId } { instanceId } { description } { resultType.toI2b2 } { setSize } { date } { date } { statusType } { statusType.i2b2Id.get }{ statusType } }.toString val result = queryResult.copy(statusType = statusType) result.toI2b2String should equal(expectedI2b2Xml) } import QueryResult.StatusType //NB: Error is tested by testToI2b2WithErrors() val nonErrorStatuses = StatusType.values.toSet - StatusType.Error for (statusType <- nonErrorStatuses) { doTest(statusType) } } @Test def testToI2b2WithErrors(): Unit = { QueryResult.errorResult(Some(description), statusMessage, TestProblem()).toI2b2String } @Test def testWithErrorsAndProblemDigest():Unit = { val testProblem:TestProblem = new TestProblem() val expected: NodeSeq = 0 0 description 0 ERROR lakjdalsjd {testProblem.problemName} {testProblem.stamp.pretty}

{testProblem.summary} {testProblem.description} {testProblem.stamp.time} {testProblem.detailsXml} val actual = QueryResult.errorResult( Some(description), statusMessage, testProblem) val i2b2Xml: NodeSeq = actual.toI2b2 val pretty = new scala.xml.PrettyPrinter(80, 2) pretty.formatNodes(i2b2Xml) should equal(pretty.formatNodes(expected)) val fromI2b2 = QueryResult.fromI2b2(Set.empty)(i2b2Xml) fromI2b2 should equal(actual) val xml = actual.toXml val fromXml = QueryResult.fromXml(Set.empty)(xml) fromXml should equal(actual) } } \ No newline at end of file diff --git a/commons/protocol/src/test/scala/net/shrine/protocol/ReadPreviousQueriesResponseTest.scala b/commons/protocol/src/test/scala/net/shrine/protocol/ReadPreviousQueriesResponseTest.scala index 6168e10fa..fa03f46ce 100644 --- a/commons/protocol/src/test/scala/net/shrine/protocol/ReadPreviousQueriesResponseTest.scala +++ b/commons/protocol/src/test/scala/net/shrine/protocol/ReadPreviousQueriesResponseTest.scala @@ -1,181 +1,179 @@ package net.shrine.protocol import org.junit.Test import net.shrine.util.XmlUtil import net.shrine.util.XmlDateHelper import javax.xml.datatype.XMLGregorianCalendar -import net.shrine.problem.{TestProblem, TurnOffProblemConnector} +import net.shrine.problem.TestProblem /** * @author Bill Simons * @since 4/12/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 class ReadPreviousQueriesResponseTest extends ShrineResponseI2b2SerializableValidator - with TurnOffProblemConnector -{ +final class ReadPreviousQueriesResponseTest extends ShrineResponseI2b2SerializableValidator { val queryMasterId1 = 1111111L val queryMasterId2 = 222222L val networkQueryId1 = 123455667L val networkQueryId2 = 98327592L val queryName1 = "name1" val queryName2 = "name2" val userId1 = Some("user1") val userId2 = Some("user2") val groupId1 = Some("group1") val groupId2 = Some("group2") val createDate1 = XmlDateHelper.now val createDate2 = XmlDateHelper.now val flagged1 = Some(true) val flagged2 = None val flagMessage1 = Some("askldhlaksdjlkasdjklasdjl") val flagMessage2 = None val queryMaster1 = makeQueryMaster(queryMasterId1, networkQueryId1, queryName1, userId1, groupId1, createDate1, flagged1, flagMessage1) val queryMaster2 = makeQueryMaster(queryMasterId2, networkQueryId2, queryName2, userId2, groupId2, createDate2, flagged2, flagMessage2) def makeQueryMaster(queryMasterId: Long, networkQueryId: Long, queryName: String, userId: Option[String], groupId: Option[String], createDate: XMLGregorianCalendar, flagged: Option[Boolean], flagMessage: Option[String]) = { QueryMaster(String.valueOf(queryMasterId), networkQueryId, queryName, userId.get, groupId.get, createDate, flagged, flagMessage) } def messageBody = XmlUtil.stripWhitespace { DONE { queryMasterId1 } { networkQueryId1 } { queryName1 } { userId1.get } { groupId1.get } { createDate1 } { flagged1.get } { flagMessage1.get } { queryMasterId2 } { networkQueryId2 } { queryName2 } { userId2.get } { groupId2.get } { createDate2 } } //keep the held field around to be sure that old messages can be read. def oldMessageBody = XmlUtil.stripWhitespace { DONE { queryMasterId1 } { networkQueryId1 } { queryName1 } { userId1.get } { groupId1.get } { createDate1 } { flagged1.get } { flagMessage1.get } { queryMasterId2 } { networkQueryId2 } { queryName2 } { userId2.get } { groupId2.get } { createDate2 } false } val readPreviousQueriesResponse = XmlUtil.stripWhitespace { { queryMasterId1 } { networkQueryId1 } { queryName1 } { createDate1 } { userId1.get } { groupId1.get } { flagged1.get } { flagMessage1.get } { queryMasterId2 } { networkQueryId2 } { queryName2 } { createDate2 } { userId2.get } { groupId2.get } } @Test def testFromI2b2FailsFast() { intercept[Exception] { ReadPreviousQueriesResponse.fromI2b2() } intercept[Exception] { ReadPreviousQueriesResponse.fromI2b2(ErrorResponse(TestProblem("foo!")).toI2b2) } } @Test def testFromXml() { val actual = ReadPreviousQueriesResponse.fromXml(readPreviousQueriesResponse) val expectedQueryMasters = Set(queryMaster1, queryMaster2) actual.queryMasters.toSet should equal(expectedQueryMasters) } @Test def testToXml() { //we compare the string versions of the xml because Scala's xml equality does not always behave properly ReadPreviousQueriesResponse(Seq(queryMaster1, queryMaster2)).toXmlString should equal(readPreviousQueriesResponse.toString) } @Test def testFromI2b2() { val translatedResponse = ReadPreviousQueriesResponse.fromI2b2(response) translatedResponse.queryMasters.toSet should equal(Set(queryMaster1, queryMaster2)) } @Test def testToI2b2() { //Per-queryMaster userids and groupids //we compare the string versions of the xml because Scala's xml equality does not always behave properly val actual = ReadPreviousQueriesResponse(Seq(queryMaster1, queryMaster2)).toI2b2String val expected = response.toString actual should equal(expected) } } \ No newline at end of file diff --git a/commons/protocol/src/test/scala/net/shrine/protocol/RunQueryResponseTest.scala b/commons/protocol/src/test/scala/net/shrine/protocol/RunQueryResponseTest.scala index 07f287807..0f359d75e 100644 --- a/commons/protocol/src/test/scala/net/shrine/protocol/RunQueryResponseTest.scala +++ b/commons/protocol/src/test/scala/net/shrine/protocol/RunQueryResponseTest.scala @@ -1,252 +1,252 @@ package net.shrine.protocol -import net.shrine.problem.{ProblemDigest, TurnOffProblemConnector} +import net.shrine.problem.ProblemDigest import scala.xml.NodeSeq import org.junit.Test import net.shrine.protocol.query.QueryDefinition import net.shrine.protocol.query.Term import net.shrine.util.XmlDateHelper import net.shrine.util.XmlUtil /** * * * @author Justin Quan * @see http://chip.org * Date: 8/12/11 */ //noinspection EmptyParenMethodOverridenAsParameterless,EmptyParenMethodAccessedAsParameterless,UnitMethodIsParameterless -final class RunQueryResponseTest extends ShrineResponseI2b2SerializableValidator with TurnOffProblemConnector { +final class RunQueryResponseTest extends ShrineResponseI2b2SerializableValidator { private val queryId = 1L private val queryName = "queryName" private val userId = "user" private val groupId = "group" private val createDate = XmlDateHelper.now private val requestQueryDef = QueryDefinition(queryName, Term("""\\i2b2\i2b2\Demographics\Age\0-9 years old\""")) private val queryInstanceId = 2L private val resultId = 3L private val setSize = 10L private val startDate = createDate private val endDate = createDate private val resultId2 = 4L private val resultType1 = ResultOutputType.PATIENT_COUNT_XML private val resultType2 = ResultOutputType.PATIENT_COUNT_XML private val statusType = QueryResult.StatusType.Finished override def messageBody: NodeSeq = { DONE { queryId } { queryName } { userId } { groupId } { createDate } { requestQueryDef.toI2b2 } { queryInstanceId } { queryId } { userId } { groupId } 6 COMPLETED COMPLETED { resultId } { queryInstanceId } 4 { resultType1 } CATNUM LA Number of patients { setSize } { startDate } { endDate } { statusType } 3 FINISHED } private val qr1 = QueryResult( resultId = resultId, instanceId = queryInstanceId, resultType = Option(resultType1), setSize = setSize, startDate = Option(createDate), endDate = Option(createDate), description = None, statusType = statusType, statusMessage = Some(statusType.name), problemDigest = None ) private val runQueryResponse = XmlUtil.stripWhitespace { { queryId } { queryInstanceId } { userId } { groupId } { requestQueryDef.toXml } { createDate } { qr1.toXml } } import DefaultBreakdownResultOutputTypes.{ values => breakdownTypes } @Test def testFromXml: Unit = { val actual = RunQueryResponse.fromXml(breakdownTypes.toSet)(runQueryResponse).get actual.queryId should equal(queryId) actual.createDate should equal(createDate) actual.userId should equal(userId) actual.groupId should equal(groupId) actual.requestXml should equal(requestQueryDef) actual.queryInstanceId should equal(queryInstanceId) actual.results should equal(Seq(qr1)) actual.queryName should equal(queryName) } @Test def testToXml: Unit = { RunQueryResponse(queryId, createDate, userId, groupId, requestQueryDef, queryInstanceId, qr1).toXmlString should equal(runQueryResponse.toString) } @Test def testFromI2b2: Unit = { val translatedResponse = RunQueryResponse.fromI2b2(breakdownTypes.toSet)(response).get translatedResponse.queryId should equal(queryId) translatedResponse.createDate should equal(createDate) translatedResponse.userId should equal(userId) translatedResponse.groupId should equal(groupId) translatedResponse.requestXml should equal(requestQueryDef) translatedResponse.queryInstanceId should equal(queryInstanceId) translatedResponse.results should equal(Seq(qr1)) translatedResponse.queryName should equal(queryName) } @Test def testFromI2b2StringRequestXml: Unit = { def hackToProduceXml(statusType: QueryResult.StatusType): HasResponse = new HasResponse { //Produces a message body where the tag contains escaped XML as a String, as is produced by the CRC override def messageBody: NodeSeq = { DONE { queryId } { queryName } { userId } { groupId } { createDate } { requestQueryDef.toI2b2String } { queryInstanceId } { queryId } { userId } { groupId } 6 COMPLETED COMPLETED { resultId } { queryInstanceId } { resultType1 } 1 LIST LA Timeline { setSize } { startDate } { endDate } { statusType } 3 FINISHED { resultId2 } { queryInstanceId } { resultType2 } 4 CATNUM LA Number of patients { setSize } { startDate } { endDate } { statusType } 3 FINISHED } } for { statusType <- QueryResult.StatusType.values } { doTestFromI2b2(hackToProduceXml(statusType).response, requestQueryDef, statusType) } } private def doTestFromI2b2(i2b2Response: NodeSeq, expectedQueryDef: AnyRef, expectedStatusType: QueryResult.StatusType, expectedProblemDigest:Option[ProblemDigest] = None) { val translatedResponse = RunQueryResponse.fromI2b2(breakdownTypes.toSet)(i2b2Response).get translatedResponse.queryId should equal(queryId) translatedResponse.createDate should equal(createDate) translatedResponse.userId should equal(userId) translatedResponse.groupId should equal(groupId) translatedResponse.requestXml should equal(expectedQueryDef) translatedResponse.queryInstanceId should equal(queryInstanceId) if(!expectedStatusType.isError) translatedResponse.results should equal(Seq(qr1.copy(statusType = expectedStatusType,problemDigest = expectedProblemDigest))) else { translatedResponse.results.size should equal(1) val result: QueryResult = translatedResponse.results.head result.copy(problemDigest = None) should equal(qr1.copy(statusType = expectedStatusType,resultType = None,setSize = 0,startDate = None,endDate = None)) result.problemDigest.get.codec should equal(classOf[ErrorStatusFromCrc].getName) } translatedResponse.queryName should equal(queryName) translatedResponse.singleNodeResult.statusType should be(expectedStatusType) } @Test def testToI2b2 { RunQueryResponse(queryId, createDate, userId, groupId, requestQueryDef, queryInstanceId, qr1).toI2b2String should equal(response.toString) } } \ No newline at end of file diff --git a/commons/protocol/src/test/scala/net/shrine/protocol/ShrineMessageTest.scala b/commons/protocol/src/test/scala/net/shrine/protocol/ShrineMessageTest.scala index 8da07e917..33a7e994b 100644 --- a/commons/protocol/src/test/scala/net/shrine/protocol/ShrineMessageTest.scala +++ b/commons/protocol/src/test/scala/net/shrine/protocol/ShrineMessageTest.scala @@ -1,168 +1,168 @@ package net.shrine.protocol import net.shrine.util.ShouldMatchersForJUnit import org.junit.Test import scala.xml.NodeSeq import net.shrine.protocol.query.QueryDefinition import net.shrine.protocol.query.Term import net.shrine.util.XmlDateHelper import java.math.BigInteger -import net.shrine.problem.{TestProblem, TurnOffProblemConnector} +import net.shrine.problem.{TestProblem} /** * @author clint * @since Feb 24, 2014 */ -final class ShrineMessageTest extends ShouldMatchersForJUnit with TurnOffProblemConnector { +final class ShrineMessageTest extends ShouldMatchersForJUnit { @Test def testRoundTrips { val projectId = "salkdjksaljdkla" import scala.concurrent.duration._ val waitTime: Duration = 98374L.milliseconds val userId = "foo-user" val groupId = "foo-group" val authn = AuthenticationInfo("blarg-domain", userId, Credential("sajkhdkjsadh", true)) val queryId = 485794359L val patientSetCollId = "ksaldjksal" val optionsXml: NodeSeq = x val fetchSize = 12345 val queryName = "saljkd;salda" val topicId = "saldjkasljdasdsadsadasdas" val topicName = "Topic Name" val outputTypes = ResultOutputType.nonBreakdownTypes.toSet val queryDefinition = QueryDefinition(queryName, Term("oiweruoiewkldfhsofi")) val queryDefinition2 = QueryDefinition(queryName, Term("a;slkjflfjlsdkjf")) val localResultId = "aoiduaojsdpaojcmsal" val nodeId = NodeId("foo") val nodeId2 = NodeId("bar") val queryTopicId1 = 123L val queryTopicId2 = 456L val queryTopicName1 = "nuh" val queryTopicName2 = "zuh" val shrineNetworkQueryId = 1287698235L val start = Some(XmlDateHelper.now) val end = Some(XmlDateHelper.now) val singleNodeResult1 = QueryResult.errorResult(Some("blarg"), "glarg",TestProblem()) val singleNodeResult2 = QueryResult( 42L, 99L, Option(ResultOutputType.PATIENT_COUNT_XML), 123L, start, end, Some("description"), QueryResult.StatusType.Finished, Some("status")) val param1 = ParamResponse("foo", "bar", "baz") val queryMaster1 = QueryMaster("kjasdh", 12345L, "name1", userId, groupId, start.get) val queryMaster2 = QueryMaster("skdjlhlasf", 873563L, "name2", userId, groupId, end.get) val queryInstance1 = QueryInstance("asd", "42", userId, groupId, start.get, end.get) val queryInstance2 = QueryInstance("asdasd", "99", userId, groupId, start.get, end.get) val envelope = I2b2ResultEnvelope(DefaultBreakdownResultOutputTypes.PATIENT_AGE_COUNT_XML, Map("x" -> 1, "y" -> 2)) //BroadcastMessage //Non-CA-signed signing cert doMarshallingRoundTrip(BroadcastMessage(123456L, authn, DeleteQueryRequest(projectId, waitTime, authn, queryId), Some(Signature(XmlDateHelper.now, CertId(new BigInteger("1234567890")), None, "asdf".getBytes)))) //CA-signed signing cert doMarshallingRoundTrip(BroadcastMessage(123456L, authn, DeleteQueryRequest(projectId, waitTime, authn, queryId), Some(Signature(XmlDateHelper.now, CertId(new BigInteger("1234567890")), Some(CertData("cert signed by ca".getBytes)), "asdf".getBytes)))) //Non-i2b2able requests doMarshallingRoundTrip(ReadTranslatedQueryDefinitionRequest(authn, waitTime, queryDefinition)) doMarshallingRoundTrip(ReadQueryResultRequest(projectId, waitTime, authn, queryId)) //I2b2able requests doMarshallingRoundTrip(DeleteQueryRequest(projectId, waitTime, authn, queryId)) doMarshallingRoundTrip(ReadApprovedQueryTopicsRequest(projectId, waitTime, authn, userId)) doMarshallingRoundTrip(ReadInstanceResultsRequest(projectId, waitTime, authn, queryId)) doMarshallingRoundTrip(ReadPreviousQueriesRequest(projectId, waitTime, authn, userId, fetchSize)) doMarshallingRoundTrip(ReadQueryDefinitionRequest(projectId, waitTime, authn, queryId)) doMarshallingRoundTrip(ReadQueryInstancesRequest(projectId, waitTime, authn, queryId)) doMarshallingRoundTrip(RenameQueryRequest(projectId, waitTime, authn, queryId, queryName)) doMarshallingRoundTrip(RunQueryRequest(projectId, waitTime, authn, queryId, Option(topicId), Option(topicName), outputTypes, queryDefinition)) doMarshallingRoundTrip(RunQueryRequest(projectId, waitTime, authn, queryId, None, None, outputTypes, queryDefinition)) doMarshallingRoundTrip(ReadResultRequest(projectId, waitTime, authn, localResultId)) //Non-i2b2able responses doMarshallingRoundTrip(SingleNodeReadTranslatedQueryDefinitionResponse(SingleNodeTranslationResult(nodeId, queryDefinition))) doMarshallingRoundTrip(AggregatedReadTranslatedQueryDefinitionResponse(Seq(SingleNodeTranslationResult(nodeId, queryDefinition), SingleNodeTranslationResult(nodeId2, queryDefinition2)))) //I2b2able responses doMarshallingRoundTrip(DeleteQueryResponse(queryId)) doMarshallingRoundTrip(ReadApprovedQueryTopicsResponse(Seq(ApprovedTopic(queryTopicId1, queryTopicName1), ApprovedTopic(queryTopicId2, queryTopicName2)))) doMarshallingRoundTrip(ReadInstanceResultsResponse(shrineNetworkQueryId, singleNodeResult2)) doMarshallingRoundTrip(AggregatedReadInstanceResultsResponse(shrineNetworkQueryId, Seq(singleNodeResult1, singleNodeResult2))) doMarshallingRoundTrip(ReadPdoResponse(Seq(EventResponse("foo", "bar", start, end, Seq(param1))), Seq(PatientResponse("nuh", Seq(param1))), Nil)) doMarshallingRoundTrip(ReadPreviousQueriesResponse(Seq(queryMaster1, queryMaster2))) doMarshallingRoundTrip(ReadQueryDefinitionResponse(42L, "name", userId, start.get, queryDefinition.toXmlString)) doMarshallingRoundTrip(ReadQueryInstancesResponse(42L, userId, groupId, Seq(queryInstance1, queryInstance2))) doMarshallingRoundTrip(RenameQueryResponse(12345L, queryName)) doMarshallingRoundTrip(RunQueryResponse(queryId, start.get, userId, groupId, queryDefinition, 12345L, singleNodeResult1)) doMarshallingRoundTrip(AggregatedRunQueryResponse(queryId, start.get, userId, groupId, queryDefinition, 12345L, Seq(singleNodeResult1, singleNodeResult2))) doMarshallingRoundTrip(ReadResultResponse(42L, singleNodeResult2, envelope)) } private def doMarshallingRoundTrip[T <: ShrineMessage](message: T) { val xml = message.toXml val unmarshalled = ShrineMessage.fromXml(DefaultBreakdownResultOutputTypes.toSet)(xml).get message match { //NB: Special handling of ReadInstanceResultsResponse because its member QueryRequests are munged //before serialization case readInstanceResultsResponse: ReadInstanceResultsResponse => { val unmarshalledResp = unmarshalled.asInstanceOf[ReadInstanceResultsResponse] val expected = readInstanceResultsResponse.copy(singleNodeResult = readInstanceResultsResponse.singleNodeResult.copy(instanceId = readInstanceResultsResponse.shrineNetworkQueryId)) unmarshalledResp should equal(expected) } //NB: Special handling of AggregatedReadInstanceResultsResponse because its member QueryRequests are munged //before serialization case aggReadInstanceResultsResponse: AggregatedReadInstanceResultsResponse => { val unmarshalledResp = unmarshalled.asInstanceOf[AggregatedReadInstanceResultsResponse] val expected = aggReadInstanceResultsResponse.copy(results = aggReadInstanceResultsResponse.results.map(_.copy(instanceId = aggReadInstanceResultsResponse.shrineNetworkQueryId))) unmarshalledResp.results(0).problemDigest should equal(expected.results(0).problemDigest) unmarshalledResp.results(0) should equal(expected.results(0)) unmarshalledResp.results should equal(expected.results) unmarshalledResp should equal(expected) } //NB: Special handling of ReadQueryInstancesResponse because its member QueryInstances are not exactly preserved //on serialization round trips case readQueryInstancesResponse: ReadQueryInstancesResponse => { val unmarshalledResp = unmarshalled.asInstanceOf[ReadQueryInstancesResponse] val expected = unmarshalledResp.withInstances(unmarshalledResp.queryInstances.map(_.copy(queryMasterId = unmarshalledResp.queryMasterId.toString))) } //NB: Special handling of RunQueryResponse because its member QueryRequest is munged //during serialization case runQueryResponse: RunQueryResponse => { val unmarshalledResp = unmarshalled.asInstanceOf[RunQueryResponse] val expected = runQueryResponse.withResult(runQueryResponse.singleNodeResult.copy(instanceId = runQueryResponse.queryInstanceId)) unmarshalledResp should equal(expected) } //NB: Special handling of AggregatedRunQueryResponse because its member QueryRequests are munged //during serialization case aggRunQueryResponse: AggregatedRunQueryResponse => { val unmarshalledResp = unmarshalled.asInstanceOf[AggregatedRunQueryResponse] val expected = aggRunQueryResponse.withResults(aggRunQueryResponse.results.map(_.copy(instanceId = aggRunQueryResponse.queryInstanceId))) unmarshalledResp should equal(expected) } case _ => unmarshalled should equal(message) } } } \ No newline at end of file diff --git a/commons/protocol/src/test/scala/net/shrine/protocol/ShrineResponseTest.scala b/commons/protocol/src/test/scala/net/shrine/protocol/ShrineResponseTest.scala index ea155f431..4f5875424 100644 --- a/commons/protocol/src/test/scala/net/shrine/protocol/ShrineResponseTest.scala +++ b/commons/protocol/src/test/scala/net/shrine/protocol/ShrineResponseTest.scala @@ -1,107 +1,107 @@ package net.shrine.protocol -import net.shrine.problem.{TestProblem, TurnOffProblemConnector} +import net.shrine.problem.TestProblem import scala.xml.NodeSeq import org.junit.Test import net.shrine.util.ShouldMatchersForJUnit import net.shrine.protocol.query.QueryDefinition import net.shrine.protocol.query.Term import net.shrine.util.XmlDateHelper import net.shrine.util.XmlUtil import scala.util.Success /** * @author clint * @since Nov 5, 2012 */ //noinspection UnitMethodIsParameterless,NameBooleanParameters,ScalaUnnecessaryParentheses -final class ShrineResponseTest extends ShouldMatchersForJUnit with TurnOffProblemConnector { +final class ShrineResponseTest extends ShouldMatchersForJUnit { @Test def testFromXml { //ShrineResponse.fromXml(null: String).isFailure should be(true) ShrineResponse.fromXml(DefaultBreakdownResultOutputTypes.toSet)(null: NodeSeq).isFailure should be(true) ShrineResponse.fromXml(DefaultBreakdownResultOutputTypes.toSet)(NodeSeq.Empty).isFailure should be(true) def roundTrip(response: ShrineResponse): Unit = { val unmarshalled = ShrineResponse.fromXml(DefaultBreakdownResultOutputTypes.toSet)(response.toXml) unmarshalled.get.getClass should equal(response.getClass) unmarshalled should not be (null) unmarshalled should equal(Success(response)) } val queryResult1 = QueryResult( resultId = 1L, instanceId = 2342L, resultType = Some(ResultOutputType.PATIENT_COUNT_XML), setSize = 123L, startDate = None, endDate = None, description = None, statusType = QueryResult.StatusType.Finished, statusMessage = None) roundTrip(ReadQueryResultResponse(123L, queryResult1)) roundTrip(AggregatedReadQueryResultResponse(123L, Seq(queryResult1))) roundTrip(DeleteQueryResponse(123L)) roundTrip(ReadInstanceResultsResponse(2342L, queryResult1)) roundTrip(AggregatedReadInstanceResultsResponse(2342L, Seq(queryResult1))) roundTrip(ReadPreviousQueriesResponse(Seq(QueryMaster("queryMasterId", 12345L, "name", "userId", "groupId", XmlDateHelper.now, Some(false))))) roundTrip(ReadQueryDefinitionResponse(8457L, "name", "userId", XmlDateHelper.now, "queryDefXml")) roundTrip(ReadQueryInstancesResponse(12345L, "userId", "groupId", Seq.empty)) roundTrip(RenameQueryResponse(12345L, "name")) roundTrip(RunQueryResponse(38957L, XmlDateHelper.now, "userId", "groupId", QueryDefinition("foo", Term("bar")), 2342L, queryResult1)) roundTrip(AggregatedRunQueryResponse(38957L, XmlDateHelper.now, "userId", "groupId", QueryDefinition("foo", Term("bar")), 2342L, Seq(queryResult1))) roundTrip(UnFlagQueryResponse) roundTrip(FlagQueryResponse) roundTrip(ErrorResponse(TestProblem("errorMessage"))) } @Test def testToXml { val response = new FooResponse response.toXmlString should equal("") } @Test def testToI2b2 { val expected = XmlUtil.stripWhitespace( 1.1 2.4 SHRINE 1.3-compatible SHRINE 2011-04-08T16:21:12.251-04:00 DONE ) val response = new FooResponse response.toI2b2String should equal(expected.toString()) } private final class FooResponse extends ShrineResponse { protected override def i2b2MessageBody = override def toXml = i2b2MessageBody } } \ No newline at end of file diff --git a/commons/util/src/main/scala/net/shrine/problem/DashboardProblemsDatabase.scala b/commons/util/src/main/scala/net/shrine/problem/DashboardProblemDatabase.scala similarity index 91% rename from commons/util/src/main/scala/net/shrine/problem/DashboardProblemsDatabase.scala rename to commons/util/src/main/scala/net/shrine/problem/DashboardProblemDatabase.scala index 8edf215d0..ff5c8752f 100644 --- a/commons/util/src/main/scala/net/shrine/problem/DashboardProblemsDatabase.scala +++ b/commons/util/src/main/scala/net/shrine/problem/DashboardProblemDatabase.scala @@ -1,196 +1,192 @@ package net.shrine.problem import java.util.concurrent.TimeoutException import javax.sql.DataSource import com.typesafe.config.Config import net.shrine.slick.{CouldNotRunDbIoActionException, TestableDataSourceCreator} import slick.dbio.SuccessAction import slick.driver.JdbcProfile import slick.jdbc.meta.MTable import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ import scala.concurrent.{Await, Future} import scala.util.control.NonFatal import scala.xml.XML /** * Problems database object, defines the PROBLEMS table schema and related queries, * as well as all interactions with the database. * @author ty * @since 07/16 */ object Problems { val config:Config = ProblemConfigSource.config.getConfig("shrine.dashboard.database") - val slickProfileClassName = config.getString("slickProfileClassName") - // TODO: Can we not pay this 2 second cost here? - val slickProfile:JdbcProfile = ProblemConfigSource.objectForName(slickProfileClassName) + val slickProfile:JdbcProfile = ProblemConfigSource.getObject("slickProfileClassName", config) import slickProfile.api._ val dataSource: DataSource = TestableDataSourceCreator.dataSource(config) lazy val db = { val db = Database.forDataSource(dataSource) val createTables: String = "createTablesOnStart" if (config.hasPath(createTables) && config.getBoolean(createTables)) { val duration = FiniteDuration(3, SECONDS) Await.ready(db.run(IOActions.createIfNotExists), duration) val testValues: String = "createTestValuesOnStart" if (config.hasPath(testValues) && config.getBoolean(testValues)) { def dumb(id: Int) = ProblemDigest(s"codec($id)", s"stamp($id)", s"sum($id)", s"desc($id)",

{id}
, id) val dummyValues: Seq[ProblemDigest] = Seq(0, 1, 2, 3, 4, 5, 6).map(dumb) Await.ready(db.run(Queries ++= dummyValues), duration) } } db } /** * The Problems Table. This is the table schema. */ class ProblemsT(tag: Tag) extends Table[ProblemDigest](tag, Queries.tableName) { def id = column[Int]("id", O.PrimaryKey, O.AutoInc) def codec = column[String]("codec") def stampText = column[String]("stampText") def summary = column[String]("summary") def description = column[String]("description") - def xml = column[String]("detailsXml", O.SqlType("Clob")) + def xml = column[String]("detailsXml") def epoch= column[Long]("epoch") // projection between table row and problem digest def * = (id, codec, stampText, summary, description, xml, epoch) <> (rowToProblem, problemToRow) def idx = index("idx_epoch", epoch, unique=false) /** * Converts a table row into a ProblemDigest. * @param args the table row, represented as a five-tuple string * @return the corresponding ProblemDigest */ def rowToProblem(args: (Int, String, String, String, String, String, Long)): ProblemDigest = args match { case (id, codec, stampText, summary, description, detailsXml, epoch) => ProblemDigest(codec, stampText, summary, description, XML.loadString(detailsXml), epoch) } /** * Converts a ProblemDigest into an Option of a table row. For now there is no failure * condition, ie a ProblemDigest can always be a table row, but this is a place for * possible future error handling * @param problem the ProblemDigest to convert * @return an Option of a table row. */ def problemToRow(problem: ProblemDigest): Option[(Int, String, String, String, String, String, Long)] = problem match { case ProblemDigest(codec, stampText, summary, description, detailsXml, epoch) => // 7 is ignored on insert and replaced with an auto incremented id Some((7, codec, stampText, summary, description, detailsXml.toString, epoch)) } } /** * Queries related to the Problems table. */ object Queries extends TableQuery(new ProblemsT(_)) { /** * The table name */ val tableName = "problems" /** * Equivalent to Select * from Problems; */ val selectAll = this /** * Selects all the details xml sorted by the problem's time stamp. */ val selectDetails = this.map(_.xml) /** * Sorts the problems in descending order */ val descending = this.sortBy(_.epoch.desc) /** * Selects the last N problems, after the offset */ def lastNProblems(n: Int, offset: Int = 0) = this.descending.drop(offset).take(n) } /** * DBIO Actions. These are pre-defined IO actions that may be useful. * Using it to centralize the location of DBIOs. */ object IOActions { val problems = Queries val tableExists = MTable.getTables(problems.tableName).map(_.nonEmpty) val createIfNotExists = tableExists.flatMap( if (_) SuccessAction(NoOperation) else problems.schema.create) val dropIfExists = tableExists.flatMap( if (_) problems.schema.drop else SuccessAction(NoOperation)) val resetTable = createIfNotExists >> problems.selectAll.delete val selectAll = problems.result def sizeAndProblemDigest(n: Int, offset: Int = 0) = problems.lastNProblems(n, offset).result.zip(problems.size.result) def findIndexOfDate(date: Long) = (problems.size - problems.filter(_.epoch <= date).size).result } /** * Entry point for interacting with the database. Runs IO actions. */ object DatabaseConnector { val IO = IOActions /** * Executes a series of IO actions as a single transactions */ def executeTransaction(actions: DBIOAction[_, NoStream, _]*): Future[Unit] = { db.run(DBIO.seq(actions:_*).transactionally) } /** * Executes a series of IO actions as a single transaction, synchronous */ def executeTransactionBlocking(actions: DBIOAction[_, NoStream, _]*)(implicit timeout: Duration): Unit = { try { Await.ready(this.executeTransaction(actions: _*), timeout) } catch { // TODO: Handle this better case tx:TimeoutException => throw CouldNotRunDbIoActionException(Problems.dataSource, tx) case NonFatal(x) => throw CouldNotRunDbIoActionException(Problems.dataSource, x) } } /** * Straight copy of db.run */ def run[R](dbio: DBIOAction[R, NoStream, _]): Future[R] = { db.run(dbio) } /** * Synchronized copy of db.run */ - def runBlocking[R](dbio: DBIOAction[R, NoStream, _])(implicit timeout: Duration = new FiniteDuration(15, SECONDS)): R = { + def runBlocking[R](dbio: DBIOAction[R, NoStream, _], timeout: Duration = new FiniteDuration(15, SECONDS)): R = { try { Await.result(this.run(dbio), timeout) } catch { case tx:TimeoutException => throw CouldNotRunDbIoActionException(Problems.dataSource, tx) case NonFatal(x) => throw CouldNotRunDbIoActionException(Problems.dataSource, x) } } - /** - * Inserts a problem into the database - * @param problem the ProblemDigest - */ - def insertProblem(problem: ProblemDigest) = { - run(Queries += problem) + def insertProblem(problem: ProblemDigest, timeout: Duration = new FiniteDuration(15, SECONDS)) = { + runBlocking(Queries += problem, timeout) } + + def warmup = runBlocking(Queries.length.result) } } // For SuccessAction, just a no_op. case object NoOperation \ No newline at end of file diff --git a/commons/util/src/main/scala/net/shrine/problem/Problem.scala b/commons/util/src/main/scala/net/shrine/problem/Problem.scala index af59b9c5e..0a6928570 100644 --- a/commons/util/src/main/scala/net/shrine/problem/Problem.scala +++ b/commons/util/src/main/scala/net/shrine/problem/Problem.scala @@ -1,215 +1,216 @@ package net.shrine.problem import java.net.InetAddress -import java.text.SimpleDateFormat import java.util.Date +import java.util.concurrent.Executors import net.shrine.log.Loggable import net.shrine.serialization.{XmlMarshaller, XmlUnmarshaller} -import scala.concurrent.Future +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.{ExecutionContext, Future, Promise} import scala.xml.{Elem, Node, NodeSeq} /** - * Describes what information we have about a problem at the site in code where we discover it. - * - * @author david - * @since 8/6/15 - */ + * Describes what information we have about a problem at the site in code where we discover it. + * + * @author david + * @since 8/6/15 + */ trait Problem { def summary:String def problemName = getClass.getName def throwable:Option[Throwable] = None def stamp:Stamp def description:String def exceptionXml(exception:Option[Throwable]): Option[Elem] = { exception.map{x => - - {x.getClass.getName} - {x.getMessage} - - {x.getStackTrace.map(line => {line})}{exceptionXml(Option(x.getCause)).getOrElse("")} - - - }} + + {x.getClass.getName} + {x.getMessage} + + {x.getStackTrace.map(line => {line})}{exceptionXml(Option(x.getCause)).getOrElse("")} + + + }} def throwableDetail: Option[Elem] = exceptionXml(throwable) def detailsXml: NodeSeq = NodeSeq.fromSeq(
{throwableDetail.getOrElse("")}
) - def toDigest:ProblemDigest = ProblemDigest(problemName,stamp.pretty,summary,description,detailsXml, stamp.time) + def toDigest:ProblemDigest = ProblemDigest(problemName,stamp.pretty,summary,description,detailsXml,stamp.time) - def logDigest:Problem = { - if (!ProblemConfigSource.turnOffConnector) { - val problems = Problems - problems.DatabaseConnector.insertProblem(toDigest) + /** + * Temporary replacement for onCreate, which will be released come Scala 2.13 + * TODO: remove when Scala 2.13 releases + */ + def hackToHandleAfterInitialization(handler:ProblemHandler):Future[Unit] = { + import scala.concurrent.blocking + Future { + var continue = true + while (continue) { + try { + blocking(synchronized(handler.handleProblem(this))) + continue = false + } catch { + case un:UninitializedFieldError => + Thread.sleep(5) + continue = true + } + } + Unit } - this - } - - def createAndLog:Problem = { - if (!ProblemConfigSource.turnOffConnector) - Problems.DatabaseConnector.insertProblem(toDigest) - this } - } + case class ProblemDigest(codec: String, stampText: String, summary: String, description: String, detailsXml: NodeSeq, epoch: Long) extends XmlMarshaller { override def toXml: Node = { {codec} {stampText} {summary} {description} {epoch} {detailsXml} } /** - * Ignores detailXml. equals with scala.xml is impossible. See http://www.scala-lang.org/api/2.10.3/index.html#scala.xml.Equality$ - */ + * Ignores detailXml. equals with scala.xml is impossible. See http://www.scala-lang.org/api/2.10.3/index.html#scala.xml.Equality$ + */ override def equals(other: Any): Boolean = - other match { - - case that: ProblemDigest => - (that canEqual this) && - codec == that.codec && - stampText == that.stampText && - summary == that.summary && - description == that.description && - epoch == that.epoch - case _ => false - } + other match { + + case that: ProblemDigest => + (that canEqual this) && + codec == that.codec && + stampText == that.stampText && + summary == that.summary && + description == that.description && + epoch == that.epoch + case _ => false + } /** - * Ignores detailXml - */ + * Ignores detailXml + */ override def hashCode: Int = { val prime = 67 codec.hashCode + prime * (stampText.hashCode + prime *(summary.hashCode + prime * (description.hashCode + prime * epoch.hashCode()))) } } object ProblemDigest extends XmlUnmarshaller[ProblemDigest] with Loggable { override def fromXml(xml: NodeSeq): ProblemDigest = { val problemNode = xml \ "problem" require(problemNode.nonEmpty,s"No problem tag in $xml") def extractText(tagName:String) = (problemNode \ tagName).text val codec = extractText("codec") val stampText = extractText("stamp") val summary = extractText("summary") val description = extractText("description") val detailsXml: NodeSeq = problemNode \ "details" val epoch = try { extractText("epoch").toLong } catch { case nx:NumberFormatException => error(s"While parsing xml representing a ProblemDigest, the epoch could not be parsed into a long", nx) 0 } ProblemDigest(codec,stampText,summary,description,detailsXml,epoch) } } case class Stamp(host:InetAddress,time:Long,source:ProblemSources.ProblemSource) { def pretty = s"${new Date(time)} on ${host.getHostName} ${source.pretty}" } object Stamp { //TODO: val dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")? //TODO: Currently the stamp text is locale specific, which can change depending on the jre/computer running it... - def apply(source:ProblemSources.ProblemSource, timer: => Long): Stamp = Stamp(InetAddress.getLocalHost, timer,source) + def apply(source:ProblemSources.ProblemSource, timer: => Long): Stamp = Stamp(InetAddress.getLocalHost, timer, source) } -/** - * An abstract problem to enable easy creation of Problems. Note that when overriding fields, - * you should only use def or lazy val, and not val. - * See: http://stackoverflow.com/questions/15346600/field-inside-object-which-extends-app-trait-is-set-to-null-why-is-that-so - * @param source - */ abstract class AbstractProblem(source:ProblemSources.ProblemSource) extends Problem { - println(s"Problem $getClass created") def timer = System.currentTimeMillis - lazy val stamp = Stamp(source, timer) - + override val stamp = Stamp(source, timer) + private val config = ProblemConfigSource.config.getConfig("shrine.problem") + hackToHandleAfterInitialization(ProblemConfigSource.getObject("problemHandler", config)) } trait ProblemHandler { def handleProblem(problem:Problem) } /** - * An example problem handler - */ + * An example problem handler + */ object LoggingProblemHandler extends ProblemHandler with Loggable { override def handleProblem(problem: Problem): Unit = { problem.throwable.fold(error(problem.toString))(throwable => error(problem.toString,throwable) ) } } -//object DatabaseProblemhandler extends ProblemHandler { -// override def handleProblem(problem: Problem): Unit = { -// Problems.DatabaseConnector.insertProblem(problem.toDigest) -// } -// -//} +object DatabaseProblemHandler extends ProblemHandler { + override def handleProblem(problem: Problem): Unit = { + Problems.DatabaseConnector.insertProblem(problem.toDigest) + } +} object ProblemSources{ sealed trait ProblemSource { def pretty = getClass.getSimpleName.dropRight(1) } case object Adapter extends ProblemSource case object Hub extends ProblemSource case object Qep extends ProblemSource case object Dsa extends ProblemSource case object Unknown extends ProblemSource def problemSources = Set(Adapter,Hub,Qep,Dsa,Unknown) } case class ProblemNotYetEncoded(internalSummary:String,t:Option[Throwable] = None) extends AbstractProblem(ProblemSources.Unknown){ override val summary = "An unanticipated problem encountered." override val throwable = { val rx = t.fold(new IllegalStateException(s"$summary"))( new IllegalStateException(s"$summary",_) - ) + ) rx.fillInStackTrace() Some(rx) } val reportedAtStackTrace = new IllegalStateException("Capture reporting stack trace.") override val description = "This problem is not yet classified in Shrine source code. Please report the details to the Shrine dev team." override val detailsXml: NodeSeq = NodeSeq.fromSeq(
{internalSummary} {throwableDetail.getOrElse("")}
) - createAndLog } object ProblemNotYetEncoded { def apply(summary:String,x:Throwable):ProblemNotYetEncoded = ProblemNotYetEncoded(summary,Some(x)) -} +} \ No newline at end of file diff --git a/commons/util/src/main/scala/net/shrine/problem/ProblemConfigSource.scala b/commons/util/src/main/scala/net/shrine/problem/ProblemConfigSource.scala index 1c1b21a78..52f1e86e9 100644 --- a/commons/util/src/main/scala/net/shrine/problem/ProblemConfigSource.scala +++ b/commons/util/src/main/scala/net/shrine/problem/ProblemConfigSource.scala @@ -1,19 +1,18 @@ package net.shrine.problem -import com.typesafe.config.{ConfigValue, ConfigValueFactory} +import com.typesafe.config.{Config, ConfigValue, ConfigValueFactory} import net.shrine.source.ConfigSource /** * Source of typesafe config for the problems database * * @author ty * @since 7/22/16 */ object ProblemConfigSource extends ConfigSource { override val configName: String = "dashboard" - // Makes it so constructing a problem in this context won't log it to the connector - // Does not stop you from constructing the connector and using it manually - var turnOffConnector = false - // var turnOffConnector = config.getProblemHandler + def getObject[T](path: String, config:Config):T = { + objectForName(config.getString(path)) + } } diff --git a/commons/util/src/main/scala/net/shrine/problem/TestProblem.scala b/commons/util/src/main/scala/net/shrine/problem/TestProblem.scala index 5a021356d..55a3419c9 100644 --- a/commons/util/src/main/scala/net/shrine/problem/TestProblem.scala +++ b/commons/util/src/main/scala/net/shrine/problem/TestProblem.scala @@ -1,12 +1,11 @@ package net.shrine.problem /** * @author david * @since 1.22 */ case class TestProblem(override val summary: String = "test summary", override val description:String = "test description", override val throwable: Option[Throwable] = None) extends AbstractProblem(ProblemSources.Unknown) { override def timer = 0 - createAndLog } diff --git a/commons/util/src/main/scala/net/shrine/problem/TurnOffProblemConnector.scala b/commons/util/src/main/scala/net/shrine/problem/TurnOffProblemConnector.scala deleted file mode 100644 index c8290495b..000000000 --- a/commons/util/src/main/scala/net/shrine/problem/TurnOffProblemConnector.scala +++ /dev/null @@ -1,9 +0,0 @@ -package net.shrine.problem - -/** - * Created by ty on 7/29/16. - * Turns off the problem database connector, so tests don't have to run a config file. - */ -trait TurnOffProblemConnector { - ProblemConfigSource.turnOffConnector = true -} diff --git a/commons/util/src/test/resources/dashboard.conf b/commons/util/src/test/resources/dashboard.conf index a6c6913d3..6ab34572d 100644 --- a/commons/util/src/test/resources/dashboard.conf +++ b/commons/util/src/test/resources/dashboard.conf @@ -1,17 +1,20 @@ shrine{ + problem { + problemHandler = "net.shrine.problem.DatabaseProblemHandler$" + } dashboard { database { dataSourceFrom = "testDataSource" slickProfileClassName = "slick.driver.H2Driver$" createTablesOnStart = true // For testing without JNDI testDataSource { //typical test settings for unit tests driverClassName = "org.h2.Driver" url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" } } } } \ No newline at end of file diff --git a/commons/util/src/test/scala/net/shrine/problem/DashboardProblemDatabaseTest.scala b/commons/util/src/test/scala/net/shrine/problem/DashboardProblemDatabaseTest.scala index 6963ae766..bb934bc82 100644 --- a/commons/util/src/test/scala/net/shrine/problem/DashboardProblemDatabaseTest.scala +++ b/commons/util/src/test/scala/net/shrine/problem/DashboardProblemDatabaseTest.scala @@ -1,92 +1,94 @@ package net.shrine.problem +import org.junit.runner.RunWith import org.scalatest.concurrent.ScalaFutures +import org.scalatest.junit.JUnitRunner import org.scalatest.{BeforeAndAfter, FlatSpec, Matchers} import slick.driver.H2Driver.api._ import scala.concurrent.duration._ /** * Test creation, insertion, querying, and deletion of ProblemDigest values into an * in-memory H2 database. Demonstrates proof of concept mapping of ProblemDigest * case class into a database. */ +@RunWith(classOf[JUnitRunner]) class DashboardProblemDatabaseTest extends FlatSpec with BeforeAndAfter with ScalaFutures with Matchers { implicit val timeout = 10.seconds - val connector = Problems.DatabaseConnector val IO = connector.IO val problemDigests = Seq( ProblemDigest("MJPG", "01:01:01", "summary here", "description here" ,
uh not sure
, 2), ProblemDigest("wewu", "01:02:01", "coffee spill", "coffee everywhere" ,
He chose decaf
, 1), ProblemDigest("wuwu", "02:01:01", "squirrel" , "chewed all the cables",
Like ALL of them
, 0), ProblemDigest("code", "10:01:02", "such summary", "such description" ,
Wow
, 3)) before { connector.runBlocking(IO.dropIfExists >> IO.tableExists) shouldBe false connector.runBlocking(IO.createIfNotExists >> IO.tableExists) shouldBe true connector.runBlocking(IO.createIfNotExists) shouldBe NoOperation connector.runBlocking(IO.selectAll) shouldBe empty } after { connector.runBlocking(IO.tableExists) shouldBe true connector.runBlocking(IO.dropIfExists >> IO.tableExists) shouldBe false connector.runBlocking(IO.dropIfExists) shouldBe NoOperation } "The Database" should "Connect without any problems" in { // Insert the suppliers and ProblemDigests connector.executeTransactionBlocking(IO.problems ++= problemDigests) // Test that they are all in the table var * = connector.runBlocking(IO.selectAll) * should contain theSameElementsAs problemDigests * should have length problemDigests.length // Reset the table connector.runBlocking(IO.resetTable >> IO.selectAll) shouldBe empty // Run the test again connector.executeTransactionBlocking(IO.problems += problemDigests.head, IO.problems += problemDigests(1), IO.problems += problemDigests(2), IO.problems += problemDigests(3)) // Test that they are all in the table * = connector.runBlocking(IO.selectAll) * should contain theSameElementsAs problemDigests * should have length problemDigests.length // Test that the simple select and filter queries work val filtered = connector.runBlocking(IO.problems.filter(_.codec === "code").map(_.description).result) filtered should have length 1 filtered.head shouldBe problemDigests(3).description // This also tests that our conversion from xml to strings works val xml = connector.runBlocking(IO.problems.map(_.xml).result) xml should have length problemDigests.length xml should contain theSameElementsAs problemDigests.map(_.detailsXml.toString()) val result = connector.runBlocking(IO.sizeAndProblemDigest(2)) result._1 should have length 2 result._2 shouldBe problemDigests.length result._1.head shouldBe problemDigests(3) result._1(1) shouldBe problemDigests.head val resultOverLength = connector.runBlocking(IO.sizeAndProblemDigest(10)) resultOverLength._1 should have length 4 resultOverLength._1 should contain theSameElementsAs problemDigests connector.runBlocking(IO.problems.size.result) shouldBe problemDigests.size val testProblem = ProblemDatabaseTestProblem(ProblemSources.Unknown) + Thread.sleep(20) connector.runBlocking(IO.problems.size.result) shouldBe problemDigests.size + 1 } } case class ProblemDatabaseTestProblem(source: ProblemSources.ProblemSource) extends AbstractProblem(source: ProblemSources.ProblemSource) { override def summary: String = "This is a test problem! No user should ever see this." override def description: String = "Wow, this is a nice looking problem. I mean really, just look at it." - createAndLog } \ No newline at end of file diff --git a/hms-support/hms-core/src/main/scala/net/shrine/hms/authorization/HmsDataStewardAuthorizationService.scala b/hms-support/hms-core/src/main/scala/net/shrine/hms/authorization/HmsDataStewardAuthorizationService.scala index 7dbdab662..0b444d164 100644 --- a/hms-support/hms-core/src/main/scala/net/shrine/hms/authorization/HmsDataStewardAuthorizationService.scala +++ b/hms-support/hms-core/src/main/scala/net/shrine/hms/authorization/HmsDataStewardAuthorizationService.scala @@ -1,90 +1,89 @@ package net.shrine.hms.authorization import java.net.URL import com.typesafe.config.Config import net.shrine.authentication.{AuthenticationResult, Authenticator} import net.shrine.authorization.{AuthorizationResult, QueryAuthorizationService} import net.shrine.client.EndpointConfig import net.shrine.log.Loggable import net.shrine.protocol.{AuthenticationInfo, CredentialConfig, ErrorResponse, ReadApprovedQueryTopicsRequest, ReadApprovedQueryTopicsResponse, RunQueryRequest} import net.shrine.config.ConfigExtensions import net.shrine.problem.{AbstractProblem, ProblemSources} /** * @author Bill Simons * @since 1/30/12 * @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 HmsDataStewardAuthorizationService( sheriffClient: SheriffClient, authenticator: Authenticator ) extends QueryAuthorizationService with Loggable { import net.shrine.hms.authorization.HmsDataStewardAuthorizationService._ override def readApprovedEntries(request: ReadApprovedQueryTopicsRequest): Either[ErrorResponse, ReadApprovedQueryTopicsResponse] = { val authn = request.authn authenticate(authn) match { case None => Left(ErrorResponse(HMSNotAuthenticatedProblem(authn))) case Some(ecommonsUsername) => val topics = sheriffClient.getApprovedEntries(ecommonsUsername) Right(ReadApprovedQueryTopicsResponse(topics)) } } override def authorizeRunQueryRequest(request: RunQueryRequest): AuthorizationResult = { val authn = request.authn if (request.topicId.isEmpty) { AuthorizationResult.NotAuthorized(s"HMS queries require a topic id; couldn't authenticate user ${toDomainAndUser(authn)}") } else { authenticate(authn) match { case None => AuthorizationResult.NotAuthorized(s"Requested topic is not approved; couldn't authenticate user ${toDomainAndUser(authn)}") case Some(ecommonsUsername) => sheriffClient.isAuthorized(ecommonsUsername, request.topicId.get, request.queryDefinition.toI2b2String) } } } private def authenticate(authn: AuthenticationInfo): Option[String] = { val authenticationResult = authenticator.authenticate(authn) identifyEcommonsUsername(authenticationResult) } } object HmsDataStewardAuthorizationService { def apply(config:Config,authenticator: Authenticator):HmsDataStewardAuthorizationService = { val endpointUrl = config.getString("sheriffEndpoint"+EndpointConfig.Keys.url) val credentials = config.getConfigured("sheriffCredentials", CredentialConfig(_)) val sheriffClient = JerseySheriffClient(endpointUrl, credentials.username, credentials.password) HmsDataStewardAuthorizationService(sheriffClient, authenticator) } private def toDomainAndUser(authn: AuthenticationInfo): String = s"${authn.domain}:${authn.username}" def identifyEcommonsUsername(authenticationResult: AuthenticationResult): Option[String] = authenticationResult match { case AuthenticationResult.Authenticated(_, ecommonsUsername) => Option(ecommonsUsername) case _ => None } } case class HMSNotAuthenticatedProblem(authn: AuthenticationInfo) extends AbstractProblem(ProblemSources.Qep){ override val summary = s"Can not authenticate ${authn.domain}:${authn.username}." override val description = s"Can not authenticate ${authn.domain}:${authn.username}." - createAndLog } \ No newline at end of file diff --git a/hms-support/hms-core/src/test/resources/dashboard.conf b/hms-support/hms-core/src/test/resources/dashboard.conf new file mode 100644 index 000000000..e280c18e3 --- /dev/null +++ b/hms-support/hms-core/src/test/resources/dashboard.conf @@ -0,0 +1,5 @@ +shrine { + problem { + problemHandler = "net.shrine.problem.LoggingProblemHandler$" + } +} \ No newline at end of file diff --git a/hms-support/hms-core/src/test/scala/net/shrine/hms/authorization/HmsDataStewardAuthorizationServiceTest.scala b/hms-support/hms-core/src/test/scala/net/shrine/hms/authorization/HmsDataStewardAuthorizationServiceTest.scala index 0da9ff494..359ab5e95 100644 --- a/hms-support/hms-core/src/test/scala/net/shrine/hms/authorization/HmsDataStewardAuthorizationServiceTest.scala +++ b/hms-support/hms-core/src/test/scala/net/shrine/hms/authorization/HmsDataStewardAuthorizationServiceTest.scala @@ -1,159 +1,158 @@ package net.shrine.hms.authorization import net.shrine.authentication.{AuthenticationResult, Authenticator} import net.shrine.authorization.AuthorizationResult.{Authorized, NotAuthorized} -import net.shrine.problem.TurnOffProblemConnector import net.shrine.protocol.{ApprovedTopic, AuthenticationInfo, Credential, ErrorResponse, ReadApprovedQueryTopicsRequest, ReadApprovedQueryTopicsResponse, RunQueryRequest} import net.shrine.protocol.query.{QueryDefinition, Term} import net.shrine.util.ShouldMatchersForJUnit import org.junit.Test /** * @author Bill Simons * @since 1/30/12 * @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 class HmsDataStewardAuthorizationServiceTest extends ShouldMatchersForJUnit with TurnOffProblemConnector { +final class HmsDataStewardAuthorizationServiceTest extends ShouldMatchersForJUnit { @Test def testIdentifyEcommonsUsername(): Unit = { import AuthenticationResult._ import HmsDataStewardAuthorizationService.identifyEcommonsUsername identifyEcommonsUsername(NotAuthenticated("", "", "")) should be(None) val ecommonsId = "foo" identifyEcommonsUsername(Authenticated("", ecommonsId)) should be(Some(ecommonsId)) } import HmsDataStewardAuthorizationServiceTest._ import scala.concurrent.duration._ private val authn = AuthenticationInfo("d", "u", Credential("p", isToken = false)) @Test def testReadApprovedEntriesNotAuthenticated() { val service = new HmsDataStewardAuthorizationService(null, NeverAuthenticatesAuthenticator) val result = service.readApprovedEntries(ReadApprovedQueryTopicsRequest("projectId", 0.minutes, authn, authn.username)) val Left(errorResponse: ErrorResponse) = result //noinspection ScalaUnnecessaryParentheses errorResponse.errorMessage should not be (null) } @Test def testReadApprovedEntriesAuthenticated() { val topic = ApprovedTopic(123L, "blarg") val ecommonsUsername = "abc123" val mockSheriffClient = MockSheriffClient(topics = Seq(topic)) val service = HmsDataStewardAuthorizationService(mockSheriffClient, AlwaysAuthenticatesAuthenticator(ecommonsUsername)) val result = service.readApprovedEntries(ReadApprovedQueryTopicsRequest("projectId", 0.minutes, authn, authn.username)) val Right(ReadApprovedQueryTopicsResponse(Seq(actualTopic))) = result actualTopic should equal(topic) mockSheriffClient.Params.user should be(null) mockSheriffClient.Params.topicId should be(null) mockSheriffClient.Params.queryText should be(null) mockSheriffClient.Params.ecommonsUsername should be(ecommonsUsername) } @Test def testAuthorizeRunQueryRequestNotAuthenticated() { val service = new HmsDataStewardAuthorizationService(null, NeverAuthenticatesAuthenticator) def doTest(topicId: Option[String],topicName:Option[String]): Unit = { val result = service.authorizeRunQueryRequest(RunQueryRequest("projectId", 0.minutes, authn, topicId, topicName, Set.empty, QueryDefinition("foo", Term("foo")))) result.isAuthorized should be(false) } doTest(None,None) doTest(Some("topicId"),Some("Topic Name")) } @Test def testAuthorizeRunQueryRequestAuthenticated() { def doTest(isAuthorized: Boolean, topicId: Option[String], topicName:Option[String]): Unit = { val ecommonsUsername = "abc123" val queryDef = QueryDefinition("foo", Term("foo")) val mockSheriffClient = MockSheriffClient(authorized = isAuthorized) val service = HmsDataStewardAuthorizationService(mockSheriffClient, AlwaysAuthenticatesAuthenticator(ecommonsUsername)) val result = service.authorizeRunQueryRequest(RunQueryRequest("projectId", 0.minutes, authn, topicId, topicName, Set.empty, queryDef)) val expectedIsAuthorized = isAuthorized && topicId.isDefined result.isAuthorized should be(expectedIsAuthorized) if (topicId.isDefined) { mockSheriffClient.Params.user should equal(ecommonsUsername) mockSheriffClient.Params.topicId should equal(topicId.get) mockSheriffClient.Params.queryText should equal(queryDef.toI2b2String) mockSheriffClient.Params.ecommonsUsername should be(null) } else { mockSheriffClient.Params.user should be(null) mockSheriffClient.Params.topicId should be(null) mockSheriffClient.Params.queryText should be(null) mockSheriffClient.Params.ecommonsUsername should be(null) } } doTest(isAuthorized = true, Some("topic123"), Some("Topic Name")) doTest(isAuthorized = false, Some("topic123"), Some("Topic Name")) doTest(isAuthorized = false, Some("topic123"), None) doTest(isAuthorized = true, None, None) doTest(isAuthorized = false, None, None) } } object HmsDataStewardAuthorizationServiceTest { object NeverAuthenticatesAuthenticator extends Authenticator { override def authenticate(authn: AuthenticationInfo) = AuthenticationResult.NotAuthenticated(authn.domain, authn.username, "foo") } final case class AlwaysAuthenticatesAuthenticator(ecommonsUsername: String) extends Authenticator { override def authenticate(authn: AuthenticationInfo) = AuthenticationResult.Authenticated(authn.domain, ecommonsUsername) } final case class MockSheriffClient(authorized: Boolean = false, topics: Seq[ApprovedTopic] = Nil) extends SheriffClient { object Params { var ecommonsUsername: String = _ var user: String = _ var topicId: String = _ var queryText: String = _ } override def getApprovedEntries(ecommonsUsername: String): Seq[ApprovedTopic] = { Params.ecommonsUsername = ecommonsUsername topics } override def isAuthorized(user: String, topicId: String, queryText: String) = { Params.user = user Params.topicId = topicId Params.queryText = queryText if(authorized) Authorized(Some((topicId,"Mock Topic"))) else NotAuthorized("Mock authorization failure") } } } \ No newline at end of file diff --git a/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/BasicAggregator.scala b/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/BasicAggregator.scala index 37338324f..0a1de8fe6 100644 --- a/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/BasicAggregator.scala +++ b/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/BasicAggregator.scala @@ -1,125 +1,121 @@ package net.shrine.aggregation import java.net.{ConnectException, UnknownHostException} import com.sun.jersey.api.client.ClientHandlerException import net.shrine.broadcaster.CouldNotParseResultsException import net.shrine.log.Loggable import net.shrine.problem.{AbstractProblem, ProblemNotYetEncoded, ProblemSources} import scala.concurrent.duration.Duration import net.shrine.protocol.{BaseShrineResponse, ErrorResponse, FailureResult, FailureResult$, NodeId, Result, SingleNodeResult, Timeout} /** * * @author Clint Gilbert * @since Sep 16, 2011 * @see http://cbmi.med.harvard.edu * * This software is licensed under the LGPL * @see http://www.gnu.org/licenses/lgpl.html * * Represents the basic aggregation strategy shared by several aggregators: * - Parses a sequence of SpinResultEntries into a sequence of some * combination of valid responses, ErrorResponses, and invalid * responses (cases where ShrineResponse.fromXml returns None) * - Filters the valid responses, weeding out responses that aren't of * the expected type * Invokes an abstract method with the valid responses, errors, and * invalid responses. * * Needs to be an abstract class instead of a trait due to the view bound on T (: Manifest) */ abstract class BasicAggregator[T <: BaseShrineResponse: Manifest] extends Aggregator with Loggable { private[aggregation] def isAggregatable(response: BaseShrineResponse): Boolean = { manifest[T].runtimeClass.isAssignableFrom(response.getClass) } import BasicAggregator._ override def aggregate(results: Iterable[SingleNodeResult], errors: Iterable[ErrorResponse]): BaseShrineResponse = { val resultsOrErrors: Iterable[ParsedResult[T]] = { for { result <- results } yield { val parsedResponse: ParsedResult[T] = result match { case Result(origin, _, errorResponse: ErrorResponse) => Error(Option(origin), errorResponse) case Result(origin, elapsed, response: T) if isAggregatable(response) => Valid(origin, elapsed, response) case Timeout(origin) => Error(Option(origin), ErrorResponse(TimedOutWithAdapter(origin))) case FailureResult(origin, cause) => cause match { case cx: ConnectException => Error(Option(origin), ErrorResponse(CouldNotConnectToAdapter(origin, cx))) case uhx: UnknownHostException => Error(Option(origin), ErrorResponse(CouldNotConnectToAdapter(origin, uhx))) case chx: ClientHandlerException => Error(Option(origin), ErrorResponse(CouldNotConnectToAdapter(origin, chx))) case cnprx:CouldNotParseResultsException => if(cnprx.statusCode >= 400) Error(Option(origin), ErrorResponse(HttpErrorResponseProblem(cnprx))) else Error(Option(origin), ErrorResponse(CouldNotParseResultsProblem(cnprx))) case x => Error(Option(origin), ErrorResponse(ProblemNotYetEncoded(s"Failure querying node ${origin.name}",x))) } case _ => Invalid(None, s"Unexpected response in $getClass:\r\n $result") } parsedResponse } } val invalidResponses = resultsOrErrors.collect { case invalid: Invalid => invalid } val validResponses = resultsOrErrors.collect { case valid: Valid[T] => valid } val errorResponses: Iterable[Error] = resultsOrErrors.collect { case error: Error => error } //Log all parsing errors invalidResponses.map(_.errorMessage).foreach(this.error(_)) val previouslyDetectedErrors = errors.map(Error(None, _)) makeResponseFrom(validResponses, errorResponses ++ previouslyDetectedErrors, invalidResponses) } private[aggregation] def makeResponseFrom(validResponses: Iterable[Valid[T]], errorResponses: Iterable[Error], invalidResponses: Iterable[Invalid]): BaseShrineResponse } object BasicAggregator { private[aggregation] sealed abstract class ParsedResult[+T] private[aggregation] final case class Valid[T](origin: NodeId, elapsed: Duration, response: T) extends ParsedResult[T] private[aggregation] final case class Error(origin: Option[NodeId], response: ErrorResponse) extends ParsedResult[Nothing] private[aggregation] final case class Invalid(origin: Option[NodeId], errorMessage: String) extends ParsedResult[Nothing] } case class CouldNotConnectToAdapter(origin:NodeId,cx: Exception) extends AbstractProblem(ProblemSources.Hub) { override val throwable = Some(cx) override val summary: String = "Shrine could not connect to the adapter." override val description: String = s"Shrine could not connect to the adapter at ${origin.name} due to ${throwable.get}." - createAndLog } case class TimedOutWithAdapter(origin:NodeId) extends AbstractProblem(ProblemSources.Hub) { override val throwable = None override val summary: String = "Timed out with adapter." override val description: String = s"Shrine observed a timeout with the adapter at ${origin.name}." - createAndLog } case class CouldNotParseResultsProblem(cnrpx:CouldNotParseResultsException) extends AbstractProblem(ProblemSources.Hub) { override val throwable = Some(cnrpx) override val summary: String = "Could not parse response." override val description = s"While parsing a response from ${cnrpx.url} with http code ${cnrpx.statusCode} caught '${cnrpx.cause}'" override val detailsXml =

Message body is {cnrpx.body} {throwableDetail.getOrElse("")}
- createAndLog } case class HttpErrorResponseProblem(cnrpx:CouldNotParseResultsException) extends AbstractProblem(ProblemSources.Hub) { override val throwable = Some(cnrpx) override val summary: String = "Adapter error." override val description = s"Observed http status code ${cnrpx.statusCode} from ${cnrpx.url} and caught ${cnrpx.cause}." override val detailsXml =
Message body is {cnrpx.body} {throwableDetail.getOrElse("")}
- createAndLog } \ No newline at end of file diff --git a/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/IgnoresErrorsAggregator.scala b/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/IgnoresErrorsAggregator.scala index ab6099403..97789b287 100644 --- a/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/IgnoresErrorsAggregator.scala +++ b/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/IgnoresErrorsAggregator.scala @@ -1,44 +1,43 @@ package net.shrine.aggregation import net.shrine.aggregation.BasicAggregator.{Error, Invalid, Valid} import net.shrine.problem.{AbstractProblem, ProblemSources} import net.shrine.protocol.ErrorResponse import net.shrine.protocol.BaseShrineResponse /** * * @author Clint Gilbert * @since Sep 16, 2011 * * @see http://cbmi.med.harvard.edu * * This software is licensed under the LGPL * @see http://www.gnu.org/licenses/lgpl.html * * Extends BasicAggregator to ignore Errors and Invalid responses * * Needs to be an abstract class instead of a trait due to the view bound on T (: Manifest) */ abstract class IgnoresErrorsAggregator[T <: BaseShrineResponse : Manifest] extends BasicAggregator[T] { private[aggregation] override def makeResponseFrom(validResponses: Iterable[Valid[T]], errorResponses: Iterable[Error], invalidResponses: Iterable[Invalid]): BaseShrineResponse = { //Filter out errors and invalid responses makeResponseFrom(validResponses) } //Default implementation, just returns first valid response, or if there are none, an ErrorResponse private[aggregation] def makeResponseFrom(validResponses: Iterable[Valid[T]]): BaseShrineResponse = { validResponses.map(_.response).toSet.headOption.getOrElse{ val problem = NoValidResponsesToAggregate() ErrorResponse(problem) } } } case class NoValidResponsesToAggregate() extends AbstractProblem(ProblemSources.Hub) { override val summary: String = "No valid responses to aggregate." override val description:String = "The hub received no valid responses to aggregate." - createAndLog } \ No newline at end of file diff --git a/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/PackagesErrorsAggregator.scala b/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/PackagesErrorsAggregator.scala index d87b0381b..12d42f4e9 100644 --- a/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/PackagesErrorsAggregator.scala +++ b/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/PackagesErrorsAggregator.scala @@ -1,55 +1,54 @@ package net.shrine.aggregation import net.shrine.protocol.ShrineResponse import net.shrine.aggregation.BasicAggregator.{Error, Invalid, Valid} import net.shrine.problem.{AbstractProblem, ProblemSources} import net.shrine.protocol.QueryResult /** * * @author Clint Gilbert * @since Sep 16, 2011 * * @see http://cbmi.med.harvard.edu * * This software is licensed under the LGPL * @see http://www.gnu.org/licenses/lgpl.html * * Extends BasicAggregator to package Errors and Invalid responses into QueryResults * * Needs to be an abstract class instead of a trait due to the view bound on T (: Manifest) */ abstract class PackagesErrorsAggregator[T <: ShrineResponse : Manifest]( errorMessage: Option[String] = None, invalidMessage: Option[String] = None) extends BasicAggregator[T] { private[aggregation] def makeErrorResult(error: Error): QueryResult = { val Error(originOption, errorResponse) = error //Use node name as the description, to avoid giving the web UI more data than it can display val desc = originOption.map(_.name) QueryResult.errorResult(desc, errorMessage.getOrElse(errorResponse.errorMessage),errorResponse.problemDigest) } private[aggregation] def makeInvalidResult(invalid: Invalid): QueryResult = { val Invalid(originOption, errorMessage) = invalid //Use node name as the description, to avoid giving the web UI more data than it can display val desc = originOption.map(_.name) QueryResult.errorResult(desc, invalidMessage.getOrElse(errorMessage),InvalidResultProblem(invalid)) } private[aggregation] final override def makeResponseFrom(validResponses: Iterable[Valid[T]], errorResponses: Iterable[Error], invalidResponses: Iterable[Invalid]): ShrineResponse = { makeResponse(validResponses, errorResponses.map(makeErrorResult), invalidResponses.map(makeInvalidResult)) } private[aggregation] def makeResponse(validResponses: Iterable[Valid[T]], errorResponses: Iterable[QueryResult], invalidResponses: Iterable[QueryResult]): ShrineResponse } case class InvalidResultProblem(invalid:Invalid) extends AbstractProblem(ProblemSources.Hub) { override def summary: String = s"Invalid response." override def description: String = s"${invalid.errorMessage} from ${invalid.origin.getOrElse("an unknown node")}" - createAndLog } \ No newline at end of file diff --git a/hub/broadcaster-aggregator/src/test/resources/dashboard.conf b/hub/broadcaster-aggregator/src/test/resources/dashboard.conf new file mode 100644 index 000000000..e280c18e3 --- /dev/null +++ b/hub/broadcaster-aggregator/src/test/resources/dashboard.conf @@ -0,0 +1,5 @@ +shrine { + problem { + problemHandler = "net.shrine.problem.LoggingProblemHandler$" + } +} \ No newline at end of file diff --git a/hub/broadcaster-aggregator/src/test/scala/net/shrine/broadcaster/SigningBroadcastAndAggregationServiceTest.scala b/hub/broadcaster-aggregator/src/test/scala/net/shrine/broadcaster/SigningBroadcastAndAggregationServiceTest.scala index fdb780a5d..bd27fca01 100644 --- a/hub/broadcaster-aggregator/src/test/scala/net/shrine/broadcaster/SigningBroadcastAndAggregationServiceTest.scala +++ b/hub/broadcaster-aggregator/src/test/scala/net/shrine/broadcaster/SigningBroadcastAndAggregationServiceTest.scala @@ -1,97 +1,97 @@ package net.shrine.broadcaster import scala.concurrent.Await import org.junit.Test import net.shrine.util.ShouldMatchersForJUnit import net.shrine.aggregation.Aggregator import net.shrine.crypto.DefaultSignerVerifier import net.shrine.crypto.TestKeystore import net.shrine.protocol.{AuthenticationInfo, BroadcastMessage, Credential, DeleteQueryRequest, ErrorResponse, FailureResult, FailureResult$, NodeId, Result, ShrineResponse, SingleNodeResult, Timeout} import net.shrine.crypto.SigningCertStrategy import net.shrine.broadcaster.dao.MockHubDao -import net.shrine.problem.{TestProblem, TurnOffProblemConnector} +import net.shrine.problem.TestProblem /** * @author clint * @since Nov 19, 2013 */ -final class SigningBroadcastAndAggregationServiceTest extends ShouldMatchersForJUnit with TurnOffProblemConnector { +final class SigningBroadcastAndAggregationServiceTest extends ShouldMatchersForJUnit { import scala.concurrent.duration._ import MockBroadcasters._ private def result(description: Char) = { val problem: TestProblem = TestProblem(summary = "blah blah blah") Result(NodeId(description.toString), 1.second, ErrorResponse(problem)) } private val results = "abcde".map(result) private lazy val nullResultsByOrigin: Map[NodeId, SingleNodeResult] = Map(NodeId("X") -> null, NodeId("Y") -> null) private lazy val resultsWithNullsByOrigin: Map[NodeId, SingleNodeResult] = { results.collect { case r @ Result(origin, _, _) => origin -> r }.toMap ++ nullResultsByOrigin } private lazy val signer = new DefaultSignerVerifier(TestKeystore.certCollection) private val broadcastMessage = { val authn = AuthenticationInfo("domain", "username", Credential("asdasd", false)) import scala.concurrent.duration._ BroadcastMessage(authn, DeleteQueryRequest("projectId", 12345.milliseconds, authn, 12345L)) } @Test def testAggregateHandlesNullResults { val mockBroadcaster = MockAdapterClientBroadcaster(resultsWithNullsByOrigin) val broadcastService = SigningBroadcastAndAggregationService(InJvmBroadcasterClient(mockBroadcaster), signer, SigningCertStrategy.Attach) val aggregator: Aggregator = new Aggregator { override def aggregate(results: Iterable[SingleNodeResult], errors: Iterable[ErrorResponse]): ShrineResponse = { ErrorResponse(TestProblem(results.size.toString)) } } val aggregatedResult = Await.result(broadcastService.sendAndAggregate(broadcastMessage, aggregator, true), 5.minutes) mockBroadcaster.messageParam.signature.isDefined should be(true) val testProblem = TestProblem(s"${results.size}") //testProblem.stamp.time = aggregatedResult. aggregatedResult should equal(ErrorResponse(TestProblem(s"${results.size}"))) } @Test def testAggregateHandlesFailures { def toResult(description: Char) = Result(NodeId(description.toString), 1.second, ErrorResponse(TestProblem("blah blah blah"))) def toFailure(description: Char) = FailureResult(NodeId(description.toString), new Exception with scala.util.control.NoStackTrace) val failuresByOrigin: Map[NodeId, SingleNodeResult] = { "UV".map(toFailure).map { case f @ FailureResult(origin, _) => origin -> f }.toMap } val timeoutsByOrigin: Map[NodeId, SingleNodeResult] = Map(NodeId("Z") -> Timeout(NodeId("Z"))) val resultsWithFailuresByOrigin: Map[NodeId, SingleNodeResult] = resultsWithNullsByOrigin ++ failuresByOrigin ++ timeoutsByOrigin val mockBroadcaster = MockAdapterClientBroadcaster(resultsWithFailuresByOrigin) val broadcastService = SigningBroadcastAndAggregationService(InJvmBroadcasterClient(mockBroadcaster), signer, SigningCertStrategy.DontAttach) val aggregator: Aggregator = new Aggregator { override def aggregate(results: Iterable[SingleNodeResult], errors: Iterable[ErrorResponse]): ShrineResponse = { ErrorResponse(TestProblem(s"${results.size},${errors.size}")) } } val aggregatedResult = Await.result(broadcastService.sendAndAggregate(broadcastMessage, aggregator, true), 5.minutes) mockBroadcaster.messageParam.signature.isDefined should be(true) aggregatedResult should equal(ErrorResponse(TestProblem(s"${results.size + failuresByOrigin.size + timeoutsByOrigin.size},0"))) } } diff --git a/integration/pom.xml b/integration/pom.xml index 222e04a79..b2519dbd5 100644 --- a/integration/pom.xml +++ b/integration/pom.xml @@ -1,119 +1,128 @@ 4.0.0 SHRINE Integration Tests shrine-integration-tests net.shrine shrine-base 1.22.1-SNAPSHOT src/main/scala src/test/scala net.alchim31.maven scala-maven-plugin net.shrine shrine-crypto ${project.version} net.shrine shrine-crypto ${project.version} test-jar test net.shrine shrine-test-commons ${project.version} test-jar test net.shrine shrine-data-commons ${project.version} test-jar test net.shrine shrine-protocol ${project.version} test net.shrine shrine-adapter-service ${project.version} test net.shrine shrine-adapter-service ${project.version} test-jar test net.shrine shrine-adapter-client-api ${project.version} test net.shrine shrine-broadcaster-aggregator ${project.version} test net.shrine shrine-qep ${project.version} test net.shrine shrine-broadcaster-service ${project.version} test com.h2database h2 test org.slf4j slf4j-log4j12 test org.springframework spring-jdbc test com.sun.jersey.jersey-test-framework jersey-test-framework-http ${jersey-version} test com.sun.jersey.contribs jersey-simple-server ${jersey-version} test + + + com.typesafe.slick + slick-hikaricp_2.11 + ${slick-version} + test + + + diff --git a/commons/protocol/src/test/resources/problem.conf b/integration/src/test/resources/dashboard.conf similarity index 70% rename from commons/protocol/src/test/resources/problem.conf rename to integration/src/test/resources/dashboard.conf index 40d9ade8f..b245b7025 100644 --- a/commons/protocol/src/test/resources/problem.conf +++ b/integration/src/test/resources/dashboard.conf @@ -1,16 +1,21 @@ -shrine{ +shrine { + problem { + problemHandler = "net.shrine.problem.DatabaseProblemHandler$" + } dashboard { database { dataSourceFrom = "testDataSource" slickProfileClassName = "slick.driver.H2Driver$" createTablesOnStart = true + createTestValuesOnStart = false // For testing without JNDI testDataSource { + //typical test settings for unit tests driverClassName = "org.h2.Driver" - + numThreads = 30 url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" } } } } \ No newline at end of file diff --git a/integration/src/test/scala/net/shrine/integration/AdapterResourceEndToEndJaxRsFailureTest.scala b/integration/src/test/scala/net/shrine/integration/AdapterResourceEndToEndJaxRsFailureTest.scala index 982123ded..aff8798bf 100644 --- a/integration/src/test/scala/net/shrine/integration/AdapterResourceEndToEndJaxRsFailureTest.scala +++ b/integration/src/test/scala/net/shrine/integration/AdapterResourceEndToEndJaxRsFailureTest.scala @@ -1,49 +1,47 @@ package net.shrine.integration import org.junit.Test import net.shrine.protocol.{BroadcastMessage, DefaultBreakdownResultOutputTypes, DeleteQueryRequest, DeleteQueryResponse, ErrorResponse, Result} import net.shrine.crypto.DefaultSignerVerifier import net.shrine.crypto.TestKeystore import scala.concurrent.Await import com.sun.jersey.api.client.UniformInterfaceException import net.shrine.adapter.service.AdapterResource import net.shrine.crypto.SigningCertStrategy -import net.shrine.problem.TurnOffProblemConnector /** * @author clint * @since Dec 17, 2013 */ final class AdapterResourceEndToEndJaxRsFailureTest extends AbstractAdapterResourceJaxRsTest - with TurnOffProblemConnector { override val makeHandler = AlwaysThrowsAdapterRequestHandler @Test def testHandleRequestWithServerSideException { import scala.concurrent.duration._ val masterId = 12345L val unsigned = BroadcastMessage(networkAuthn, DeleteQueryRequest("some-project", 1.minute, networkAuthn, masterId)) val signer = new DefaultSignerVerifier(TestKeystore.certCollection) val signed = signer.sign(unsigned, SigningCertStrategy.Attach) val result = Await.result(client.query(signed), 1.hour) result.response.isInstanceOf[ErrorResponse] should be(true) } @Test def testHandleRequestWithBadInput { val resource = AdapterResource(MockAdapterRequestHandler) intercept[Exception] { resource.handleRequest("aslkdjlaksjdlasdj") } } } \ No newline at end of file diff --git a/integration/src/test/scala/net/shrine/integration/ProblemCreation.scala b/integration/src/test/scala/net/shrine/integration/ProblemCreation.scala new file mode 100644 index 000000000..cca8028a8 --- /dev/null +++ b/integration/src/test/scala/net/shrine/integration/ProblemCreation.scala @@ -0,0 +1,140 @@ +package net.shrine.integration + +import java.net.{URL, URLConnection, URLStreamHandler, URLStreamHandlerFactory} +import java.sql.SQLException + +import net.shrine.adapter.AbstractQueryRetrievalTestCase.BogusRequest +import net.shrine.adapter._ +import net.shrine.adapter.client.{CouldNotParseXmlFromAdapter, HttpErrorCodeFromAdapter} +import net.shrine.adapter.components.QueryNotInDatabase +import net.shrine.adapter.dao.BotDetectedException +import net.shrine.adapter.service.{CouldNotVerifySignature, UnknownRequestType} +import net.shrine.aggregation._ +import net.shrine.authentication.{NotAuthenticatedException, NotAuthenticatedProblem} +import net.shrine.authorization.{CouldNotInterpretResponseFromPmCell, CouldNotReachPmCell, ErrorStatusFromDataStewardApp, MissingRequiredRoles} +import net.shrine.broadcaster.CouldNotParseResultsException +import net.shrine.client.HttpResponse +import net.shrine.hms.authorization.HMSNotAuthenticatedProblem +import net.shrine.problem._ +import net.shrine.protocol.QueryResult.StatusType +import net.shrine.protocol.query.QueryDefinition +import net.shrine.protocol._ +import net.shrine.qep.queries.QepDatabaseProblem +import org.junit.runner.RunWith +import org.scalatest.junit.JUnitRunner +import org.scalatest.{FlatSpec, Matchers} +import slick.driver.H2Driver.api._ + +import scala.concurrent.duration.FiniteDuration +import scala.xml.{NodeSeq, SAXParseException} + +/** + * Created by ty on 8/29/16. + * Tests that we can successfully log every problem in the codebase. + * Due to the time nature of logging problems, we create the succeed + * early loop at the bottom to give every problem a chance at being + * created on time. + */ +@RunWith(classOf[JUnitRunner]) +class ProblemCreation extends FlatSpec with Matchers { + val throwable = new IllegalArgumentException("Boo") + val credential: Credential = Credential("string", isToken = true) + val authInfo = AuthenticationInfo("domain", "username", credential) + val authExecption = AdapterLockoutException(authInfo, "url") + val bogus: ShrineRequest = new BogusRequest + val seconds = new FiniteDuration(10, java.util.concurrent.TimeUnit.SECONDS) + val queryDefinition = QueryDefinition("string", None) + val runQueryRequest = new RunQueryRequest("id", seconds, authInfo, 10, None, None, Set(), queryDefinition) + val saxxException: SAXParseException = new SAXParseException("hey", null) + val xmlResponse: String = "" + val someXml =
Heyo!
+ val teapot: HttpResponse = HttpResponse(418, "body") + val nodeId: NodeId = NodeId("name") + val couldNotParseException: CouldNotParseResultsException = CouldNotParseResultsException(5, "url", "body", throwable) + val queryResult = QueryResult(5l, 5l, None, 5l, None, None, None, StatusType("name", isDone=false), None) + val readyQueryResponse = ReadQueryResultResponse(5l, queryResult) + val foo: NonI2b2ableResponse = new Foo() + + "Problems" should "all be successfully created and logged" in { + + URL.setURLStreamHandlerFactory(new BogusUrlFactory) + val db = Problems.DatabaseConnector + val queries = Problems.Queries + val problemSize = () => db.runBlocking(queries.size.result) + + problemSize() shouldBe 0 + + + val problems: Seq[AbstractProblem] = Seq( + HttpErrorCodeFromAdapter("url", 5, "string response body"), + CouldNotParseXmlFromAdapter("url", 6, "responseBody", saxxException), + QueryNotFound(10l), + QueryResultNotAvailable(10l), + CouldNotRetrieveQueryFromCrc(10l, throwable), + AdapterLockout(authInfo, authExecption), + CrcCouldNotBeInvoked("crcUrl", bogus, CrcInvocationException("url", bogus, throwable)), + AdapterMappingProblem(AdapterMappingException(runQueryRequest, "message", throwable)), + AdapterDatabaseProblem(new SQLException("reason", "state", 5)), + BotDetected(BotDetectedException("domain", "user", 5l, 5l, 5l)), + CannotParseXmlFromCrc(saxxException, xmlResponse), + ExceptionWhileLoadingCrcResponse(throwable, xmlResponse), + ErrorFromCrcBreakdown(ErrorFromCrcException("message")), + CannotInterpretCrcBreakdownXml(MissingCrCXmlResultException(someXml, throwable)), + QueryNotInDatabase(I2b2AdminReadQueryDefinitionRequest("project", seconds, authInfo, 5l)), + // Difficult to test, as I would have to pull it out of the defining code, + // Change it from an object to a case class, and make sure that I'm not breaking + // Any breakdown logic by doing so. + // BreakdownFailure, + CouldNotVerifySignature(BroadcastMessage(5l, authInfo, bogus)), + UnknownRequestType(RequestType("apple", None)), + NotAuthenticatedProblem(NotAuthenticatedException("string", "string", "message", throwable)), + MissingRequiredRoles("pid", Set(), authInfo), + CouldNotReachPmCell("url", authInfo, throwable), + CouldNotInterpretResponseFromPmCell("url", authInfo, teapot, throwable), + ErrorStatusFromDataStewardApp(spray.http.HttpResponse(), new URL("bogus", "host", 5, "file")), + CouldNotConnectToAdapter(nodeId, throwable), + TimedOutWithAdapter(nodeId), + CouldNotParseResultsProblem(couldNotParseException), + HttpErrorResponseProblem(couldNotParseException), + NoValidResponsesToAggregate(), + // Difficult to test since I can't grab private value: + //InvalidResultProblem(Invalid(None, "error")), + HMSNotAuthenticatedProblem(authInfo), + ErrorStatusFromCrc(None, ""), + QepDatabaseProblem(throwable), + ProblemNotYetEncoded("summary", Some(throwable)), + NoI2b2AnalogExists(foo.getClass), + TestProblem() + + ) + + var count = 0 + // give it up to 1 second to finish + while(problemSize() != problems.length && count < 20) { + Thread.sleep(50) + count+=1 + } + + problemSize() shouldBe problems.length + db.runBlocking(queries.result) should contain theSameElementsAs problems.map(_.toDigest) + + + } + +} + +class Foo extends ShrineResponse with NonI2b2ableResponse { + override def toXml: NodeSeq =
Yay
+} + +class BogusUrlFactory extends URLStreamHandlerFactory { + override def createURLStreamHandler(protocol: String): URLStreamHandler = new BogusUrlHandler +} + +class BogusUrlHandler extends URLStreamHandler { + override def openConnection(u: URL): URLConnection = new BogusUrlConnection(u) +} + +class BogusUrlConnection(u: URL) extends URLConnection(u) { + override def connect(): Unit = {} +} \ No newline at end of file diff --git a/qep/service/src/main/scala/net/shrine/qep/queries/QepQueryDb.scala b/qep/service/src/main/scala/net/shrine/qep/queries/QepQueryDb.scala index 79bf27266..b61c2a5b0 100644 --- a/qep/service/src/main/scala/net/shrine/qep/queries/QepQueryDb.scala +++ b/qep/service/src/main/scala/net/shrine/qep/queries/QepQueryDb.scala @@ -1,534 +1,533 @@ package net.shrine.qep.queries import java.sql.SQLException import java.util.concurrent.TimeoutException import javax.sql.DataSource import com.typesafe.config.Config import net.shrine.audit.{NetworkQueryId, QueryName, Time, UserName} import net.shrine.log.Loggable import net.shrine.problem.{AbstractProblem, ProblemDigest, ProblemSources} import net.shrine.protocol.{DefaultBreakdownResultOutputTypes, DeleteQueryRequest, FlagQueryRequest, I2b2ResultEnvelope, QueryMaster, QueryResult, ReadPreviousQueriesRequest, ReadPreviousQueriesResponse, RenameQueryRequest, ResultOutputType, ResultOutputTypes, RunQueryRequest, UnFlagQueryRequest} import net.shrine.qep.QepConfigSource import net.shrine.slick.{CouldNotRunDbIoActionException, TestableDataSourceCreator} import net.shrine.util.XmlDateHelper import slick.driver.JdbcProfile import scala.collection.immutable.Iterable import scala.concurrent.duration.{Duration, DurationInt} import scala.concurrent.{Await, Future, blocking} import scala.language.postfixOps import scala.concurrent.ExecutionContext.Implicits.global import scala.util.control.NonFatal import scala.xml.XML /** * DB code for the QEP's query instances and query results. * * @author david * @since 1/19/16 */ case class QepQueryDb(schemaDef:QepQuerySchema,dataSource: DataSource,timeout:Duration) extends Loggable { import schemaDef._ import jdbcProfile.api._ val database = Database.forDataSource(dataSource) def createTables() = schemaDef.createTables(database) def dropTables() = schemaDef.dropTables(database) def dbRun[R](action: DBIOAction[R, NoStream, Nothing]):R = { val future: Future[R] = database.run(action) try { blocking { Await.result(future, timeout) } } catch { case tx:TimeoutException => throw CouldNotRunDbIoActionException(dataSource,tx) case NonFatal(x) => throw CouldNotRunDbIoActionException(dataSource,x) } } def insertQepQuery(runQueryRequest: RunQueryRequest):Unit = { debug(s"insertQepQuery $runQueryRequest") insertQepQuery(QepQuery(runQueryRequest)) } def insertQepQuery(qepQuery: QepQuery):Unit = { dbRun(allQepQueryQuery += qepQuery) } def selectAllQepQueries:Seq[QepQuery] = { dbRun(mostRecentVisibleQepQueries.result) } def selectPreviousQueries(request: ReadPreviousQueriesRequest):ReadPreviousQueriesResponse = { val previousQueries: Seq[QepQuery] = selectPreviousQueriesByUserAndDomain(request.authn.username,request.authn.domain,request.fetchSize) val flags:Map[NetworkQueryId,QepQueryFlag] = selectMostRecentQepQueryFlagsFor(previousQueries.map(_.networkId).to[Set]) val queriesAndFlags = previousQueries.map(x => (x,flags.get(x.networkId))) ReadPreviousQueriesResponse(queriesAndFlags.map(x => x._1.toQueryMaster(x._2))) } def selectPreviousQueriesByUserAndDomain(userName: UserName, domain: String, limit:Int):Seq[QepQuery] = { dbRun(mostRecentVisibleQepQueries.filter(_.userName === userName).filter(_.userDomain === domain).sortBy(x => x.changeDate.desc).take(limit).result) } def renamePreviousQuery(request:RenameQueryRequest):Unit = { val networkQueryId = request.networkQueryId dbRun( for { queryResults <- mostRecentVisibleQepQueries.filter(_.networkId === networkQueryId).result _ <- allQepQueryQuery ++= queryResults.map(_.copy(queryName = request.queryName,changeDate = System.currentTimeMillis())) } yield queryResults ) } def markDeleted(request:DeleteQueryRequest):Unit = { val networkQueryId = request.networkQueryId dbRun( for { queryResults <- mostRecentVisibleQepQueries.filter(_.networkId === networkQueryId).result _ <- allQepQueryQuery ++= queryResults.map(_.copy(deleted = true,changeDate = System.currentTimeMillis())) } yield queryResults ) } def insertQepQueryFlag(flagQueryRequest: FlagQueryRequest):Unit = { insertQepQueryFlag(QepQueryFlag(flagQueryRequest)) } def insertQepQueryFlag(unflagQueryRequest: UnFlagQueryRequest):Unit = { insertQepQueryFlag(QepQueryFlag(unflagQueryRequest)) } def insertQepQueryFlag(qepQueryFlag: QepQueryFlag):Unit = { dbRun(allQepQueryFlags += qepQueryFlag) } def selectMostRecentQepQueryFlagsFor(networkIds:Set[NetworkQueryId]):Map[NetworkQueryId,QepQueryFlag] = { val flags:Seq[QepQueryFlag] = dbRun(mostRecentQueryFlags.filter(_.networkId inSet networkIds).result) flags.map(x => x.networkQueryId -> x).toMap } def insertQepResultRow(qepQueryRow:QueryResultRow) = { dbRun(allQueryResultRows += qepQueryRow) } def insertQueryResult(networkQueryId:NetworkQueryId,result:QueryResult) = { val adapterNode = result.description.getOrElse(throw new IllegalStateException("description is empty, does not have an adapter node")) val queryResultRow = QueryResultRow(networkQueryId,result) val breakdowns: Iterable[QepQueryBreakdownResultsRow] = result.breakdowns.flatMap(QepQueryBreakdownResultsRow.breakdownRowsFor(networkQueryId,adapterNode,result.resultId,_)) val problem: Seq[QepProblemDigestRow] = result.problemDigest.map(p => QepProblemDigestRow(networkQueryId,adapterNode,p.codec,p.stampText,p.summary,p.description,p.detailsXml.toString,System.currentTimeMillis())).to[Seq] dbRun( for { _ <- allQueryResultRows += queryResultRow _ <- allBreakdownResultsRows ++= breakdowns _ <- allProblemDigestRows ++= problem } yield () ) } def selectMostRecentQepResultRowsFor(networkId:NetworkQueryId): Seq[QueryResultRow] = { dbRun(mostRecentQueryResultRows.filter(_.networkQueryId === networkId).result) } def selectMostRecentQepResultsFor(networkId:NetworkQueryId): Seq[QueryResult] = { val (queryResults, breakdowns,problems) = dbRun( for { queryResults <- mostRecentQueryResultRows.filter(_.networkQueryId === networkId).result breakdowns <- mostRecentBreakdownResultsRows.filter(_.networkQueryId === networkId).result problems <- mostRecentProblemDigestRows.filter(_.networkQueryId === networkId).result } yield (queryResults, breakdowns, problems) ) val resultIdsToI2b2ResultEnvelopes: Map[Long, Map[ResultOutputType, I2b2ResultEnvelope]] = breakdowns.groupBy(_.resultId).map(rIdToB => rIdToB._1 -> QepQueryBreakdownResultsRow.resultEnvelopesFrom(rIdToB._2)) def seqOfOneProblemRowToProblemDigest(problemSeq:Seq[QepProblemDigestRow]):ProblemDigest = { if(problemSeq.size == 1) problemSeq.head.toProblemDigest else throw new IllegalStateException(s"problemSeq size was not 1. $problemSeq") } val adapterNodesToProblemDigests: Map[String, ProblemDigest] = problems.groupBy(_.adapterNode).map(nodeToProblem => nodeToProblem._1 -> seqOfOneProblemRowToProblemDigest(nodeToProblem._2) ) queryResults.map(r => r.toQueryResult( resultIdsToI2b2ResultEnvelopes.getOrElse(r.resultId,Map.empty), adapterNodesToProblemDigests.get(r.adapterNode) )) } def insertQueryBreakdown(breakdownResultsRow:QepQueryBreakdownResultsRow) = { dbRun(allBreakdownResultsRows += breakdownResultsRow) } def selectAllBreakdownResultsRows: Seq[QepQueryBreakdownResultsRow] = { dbRun(allBreakdownResultsRows.result) } } object QepQueryDb extends Loggable { val dataSource:DataSource = TestableDataSourceCreator.dataSource(QepQuerySchema.config) val timeout = QepQuerySchema.config.getInt("timeout") seconds val db = QepQueryDb(QepQuerySchema.schema,dataSource,timeout) val createTablesOnStart = QepQuerySchema.config.getBoolean("createTablesOnStart") if(createTablesOnStart) QepQueryDb.db.createTables() } /** * Separate class to support schema generation without actually connecting to the database. * * @param jdbcProfile Database profile to use for the schema */ case class QepQuerySchema(jdbcProfile: JdbcProfile,moreBreakdowns: Set[ResultOutputType]) extends Loggable { import jdbcProfile.api._ def ddlForAllTables: jdbcProfile.DDL = { allQepQueryQuery.schema ++ allQepQueryFlags.schema ++ allQueryResultRows.schema ++ allBreakdownResultsRows.schema ++ allProblemDigestRows.schema } //to get the schema, use the REPL //println(QepQuerySchema.schema.ddlForAllTables.createStatements.mkString(";\n")) def createTables(database:Database) = { try { val future = database.run(ddlForAllTables.create) Await.result(future,10 seconds) } catch { //I'd prefer to check and create schema only if absent. No way to do that with Oracle. case x:SQLException => info("Caught exception while creating tables. Recover by assuming the tables already exist.",x) } } def dropTables(database:Database) = { val future = database.run(ddlForAllTables.drop) //Really wait forever for the cleanup Await.result(future,Duration.Inf) } class QepQueries(tag:Tag) extends Table[QepQuery](tag,"previousQueries") { def networkId = column[NetworkQueryId]("networkId") def userName = column[UserName]("userName") def userDomain = column[String]("domain") def queryName = column[QueryName]("queryName") def expression = column[Option[String]]("expression") def dateCreated = column[Time]("dateCreated") def deleted = column[Boolean]("deleted") def queryXml = column[String]("queryXml") def changeDate = column[Long]("changeDate") def * = (networkId,userName,userDomain,queryName,expression,dateCreated,deleted,queryXml,changeDate) <> (QepQuery.tupled,QepQuery.unapply) } val allQepQueryQuery = TableQuery[QepQueries] val mostRecentQepQueryQuery: Query[QepQueries, QepQuery, Seq] = for( queries <- allQepQueryQuery if !allQepQueryQuery.filter(_.networkId === queries.networkId).filter(_.changeDate > queries.changeDate).exists ) yield queries val mostRecentVisibleQepQueries = mostRecentQepQueryQuery.filter(_.deleted === false) class QepQueryFlags(tag:Tag) extends Table[QepQueryFlag](tag,"queryFlags") { def networkId = column[NetworkQueryId]("networkId") def flagged = column[Boolean]("flagged") def flagMessage = column[String]("flagMessage") def changeDate = column[Long]("changeDate") def * = (networkId,flagged,flagMessage,changeDate) <> (QepQueryFlag.tupled,QepQueryFlag.unapply) } val allQepQueryFlags = TableQuery[QepQueryFlags] val mostRecentQueryFlags: Query[QepQueryFlags, QepQueryFlag, Seq] = for( queryFlags <- allQepQueryFlags if !allQepQueryFlags.filter(_.networkId === queryFlags.networkId).filter(_.changeDate > queryFlags.changeDate).exists ) yield queryFlags val qepQueryResultTypes = DefaultBreakdownResultOutputTypes.toSet ++ ResultOutputType.values ++ moreBreakdowns val stringsToQueryResultTypes: Map[String, ResultOutputType] = qepQueryResultTypes.map(x => (x.name,x)).toMap val queryResultTypesToString: Map[ResultOutputType, String] = stringsToQueryResultTypes.map(_.swap) implicit val qepQueryResultTypesColumnType = MappedColumnType.base[ResultOutputType,String] ({ (resultType: ResultOutputType) => queryResultTypesToString(resultType) },{ (string: String) => stringsToQueryResultTypes(string) }) implicit val queryStatusColumnType = MappedColumnType.base[QueryResult.StatusType,String] ({ statusType => statusType.name },{ name => QueryResult.StatusType.valueOf(name).getOrElse(throw new IllegalStateException(s"$name is not one of ${QueryResult.StatusType.values.map(_.name).mkString(", ")}")) }) class QepQueryResults(tag:Tag) extends Table[QueryResultRow](tag,"queryResults") { def resultId = column[Long]("resultId") def networkQueryId = column[NetworkQueryId]("networkQueryId") def instanceId = column[Long]("instanceId") def adapterNode = column[String]("adapterNode") def resultType = column[Option[ResultOutputType]]("resultType") def size = column[Long]("size") def startDate = column[Option[Long]]("startDate") def endDate = column[Option[Long]]("endDate") def status = column[QueryResult.StatusType]("status") def statusMessage = column[Option[String]]("statusMessage") def changeDate = column[Long]("changeDate") def * = (resultId,networkQueryId,instanceId,adapterNode,resultType,size,startDate,endDate,status,statusMessage,changeDate) <> (QueryResultRow.tupled,QueryResultRow.unapply) } val allQueryResultRows = TableQuery[QepQueryResults] //Most recent query result rows for each queryId from each adapter val mostRecentQueryResultRows: Query[QepQueryResults, QueryResultRow, Seq] = for( queryResultRows <- allQueryResultRows if !allQueryResultRows.filter(_.networkQueryId === queryResultRows.networkQueryId).filter(_.adapterNode === queryResultRows.adapterNode).filter(_.changeDate > queryResultRows.changeDate).exists ) yield queryResultRows class QepQueryBreakdownResults(tag:Tag) extends Table[QepQueryBreakdownResultsRow](tag,"queryBreakdownResults") { def networkQueryId = column[NetworkQueryId]("networkQueryId") def adapterNode = column[String]("adapterNode") def resultId = column[Long]("resultId") def resultType = column[ResultOutputType]("resultType") def dataKey = column[String]("dataKey") def value = column[Long]("value") def changeDate = column[Long]("changeDate") def * = (networkQueryId,adapterNode,resultId,resultType,dataKey,value,changeDate) <> (QepQueryBreakdownResultsRow.tupled,QepQueryBreakdownResultsRow.unapply) } val allBreakdownResultsRows = TableQuery[QepQueryBreakdownResults] //Most recent query result rows for each queryId from each adapter val mostRecentBreakdownResultsRows: Query[QepQueryBreakdownResults, QepQueryBreakdownResultsRow, Seq] = for( breakdownResultsRows <- allBreakdownResultsRows if !allBreakdownResultsRows.filter(_.networkQueryId === breakdownResultsRows.networkQueryId).filter(_.adapterNode === breakdownResultsRows.adapterNode).filter(_.resultId === breakdownResultsRows.resultId).filter(_.changeDate > breakdownResultsRows.changeDate).exists ) yield breakdownResultsRows /* case class ProblemDigest(codec: String, stampText: String, summary: String, description: String, detailsXml: NodeSeq) extends XmlMarshaller { */ class QepResultProblemDigests(tag:Tag) extends Table [QepProblemDigestRow](tag,"queryResultProblemDigests") { def networkQueryId = column[NetworkQueryId]("networkQueryId") def adapterNode = column[String]("adapterNode") def codec = column[String]("codec") def stamp = column[String]("stamp") def summary = column[String]("summary") def description = column[String]("description") def details = column[String]("details") def changeDate = column[Long]("changeDate") def * = (networkQueryId,adapterNode,codec,stamp,summary,description,details,changeDate) <> (QepProblemDigestRow.tupled,QepProblemDigestRow.unapply) } val allProblemDigestRows = TableQuery[QepResultProblemDigests] val mostRecentProblemDigestRows: Query[QepResultProblemDigests, QepProblemDigestRow, Seq] = for( problemDigests <- allProblemDigestRows if !allProblemDigestRows.filter(_.networkQueryId === problemDigests.networkQueryId).filter(_.adapterNode === problemDigests.adapterNode).filter(_.changeDate > problemDigests.changeDate).exists ) yield problemDigests } object QepQuerySchema { val allConfig:Config = QepConfigSource.config val config:Config = allConfig.getConfig("shrine.queryEntryPoint.audit.database") val slickProfileClassName = config.getString("slickProfileClassName") val slickProfile:JdbcProfile = QepConfigSource.objectForName(slickProfileClassName) import net.shrine.config.{ConfigExtensions, Keys} val moreBreakdowns: Set[ResultOutputType] = config.getOptionConfigured("breakdownResultOutputTypes",ResultOutputTypes.fromConfig).getOrElse(Set.empty) val schema = QepQuerySchema(slickProfile,moreBreakdowns) } case class QepQuery( networkId:NetworkQueryId, userName: UserName, userDomain: String, queryName: QueryName, expression: Option[String], dateCreated: Time, deleted: Boolean, queryXml: String, changeDate: Time ){ def toQueryMaster(qepQueryFlag:Option[QepQueryFlag]):QueryMaster = { QueryMaster( queryMasterId = networkId.toString, networkQueryId = networkId, name = queryName, userId = userName, groupId = userDomain, createDate = XmlDateHelper.toXmlGregorianCalendar(dateCreated), flagged = qepQueryFlag.map(_.flagged), flagMessage = qepQueryFlag.map(_.flagMessage) ) } } object QepQuery extends ((NetworkQueryId,UserName,String,QueryName,Option[String],Time,Boolean,String,Time) => QepQuery) { def apply(runQueryRequest: RunQueryRequest):QepQuery = { new QepQuery( networkId = runQueryRequest.networkQueryId, userName = runQueryRequest.authn.username, userDomain = runQueryRequest.authn.domain, queryName = runQueryRequest.queryDefinition.name, expression = runQueryRequest.queryDefinition.expr.map(_.toString), dateCreated = System.currentTimeMillis(), deleted = false, queryXml = runQueryRequest.toXmlString, changeDate = System.currentTimeMillis() ) } } case class QepQueryFlag( networkQueryId: NetworkQueryId, flagged:Boolean, flagMessage:String, changeDate:Long ) object QepQueryFlag extends ((NetworkQueryId,Boolean,String,Long) => QepQueryFlag) { def apply(flagQueryRequest: FlagQueryRequest):QepQueryFlag = { QepQueryFlag( networkQueryId = flagQueryRequest.networkQueryId, flagged = true, flagMessage = flagQueryRequest.message.getOrElse(""), changeDate = System.currentTimeMillis() ) } def apply(unflagQueryRequest: UnFlagQueryRequest):QepQueryFlag = { QepQueryFlag( networkQueryId = unflagQueryRequest.networkQueryId, flagged = false, flagMessage = "", changeDate = System.currentTimeMillis() ) } } case class QueryResultRow( resultId:Long, networkQueryId:NetworkQueryId, instanceId:Long, adapterNode:String, resultType:Option[ResultOutputType], size:Long, startDate:Option[Long], endDate:Option[Long], status:QueryResult.StatusType, statusMessage:Option[String], changeDate:Long ) { def toQueryResult(breakdowns:Map[ResultOutputType,I2b2ResultEnvelope],problemDigest:Option[ProblemDigest]) = QueryResult( resultId = resultId, instanceId = instanceId, resultType = resultType, setSize = size, startDate = startDate.map(XmlDateHelper.toXmlGregorianCalendar), endDate = endDate.map(XmlDateHelper.toXmlGregorianCalendar), description = Some(adapterNode), statusType = status, statusMessage = statusMessage, breakdowns = breakdowns, problemDigest = problemDigest ) } object QueryResultRow extends ((Long,NetworkQueryId,Long,String,Option[ResultOutputType],Long,Option[Long],Option[Long],QueryResult.StatusType,Option[String],Long) => QueryResultRow) { def apply(networkQueryId:NetworkQueryId,result:QueryResult):QueryResultRow = { new QueryResultRow( resultId = result.resultId, networkQueryId = networkQueryId, instanceId = result.instanceId, adapterNode = result.description.getOrElse(s"$result has None in its description field, not a name of an adapter node."), resultType = result.resultType, size = result.setSize, startDate = result.startDate.map(_.toGregorianCalendar.getTimeInMillis), endDate = result.endDate.map(_.toGregorianCalendar.getTimeInMillis), status = result.statusType, statusMessage = result.statusMessage, changeDate = System.currentTimeMillis() ) } } case class QepQueryBreakdownResultsRow( networkQueryId: NetworkQueryId, adapterNode:String, resultId:Long, resultType: ResultOutputType, dataKey:String, value:Long, changeDate:Long ) object QepQueryBreakdownResultsRow extends ((NetworkQueryId,String,Long,ResultOutputType,String,Long,Long) => QepQueryBreakdownResultsRow){ def breakdownRowsFor(networkQueryId:NetworkQueryId, adapterNode:String, resultId:Long, breakdown:(ResultOutputType,I2b2ResultEnvelope)): Iterable[QepQueryBreakdownResultsRow] = { breakdown._2.data.map(b => QepQueryBreakdownResultsRow(networkQueryId,adapterNode,resultId,breakdown._1,b._1,b._2,System.currentTimeMillis())) } def resultEnvelopesFrom(breakdowns:Seq[QepQueryBreakdownResultsRow]): Map[ResultOutputType, I2b2ResultEnvelope] = { def resultEnvelopeFrom(resultType:ResultOutputType,breakdowns:Seq[QepQueryBreakdownResultsRow]):I2b2ResultEnvelope = { val data = breakdowns.map(b => b.dataKey -> b.value).toMap I2b2ResultEnvelope(resultType,data) } breakdowns.groupBy(_.resultType).map(r => r._1 -> resultEnvelopeFrom(r._1,r._2)) } } case class QepProblemDigestRow( networkQueryId: NetworkQueryId, adapterNode: String, codec: String, stampText: String, summary: String, description: String, details: String, changeDate:Long ){ def toProblemDigest = { ProblemDigest( codec, stampText, summary, description, if(!details.isEmpty) XML.loadString(details) else
, //TODO: FIGURE OUT HOW TO GET AN ACUTAL EPOCH INTO HERE 0 ) } } case class QepDatabaseProblem(x:Exception) extends AbstractProblem(ProblemSources.Qep){ override val summary = "A problem encountered while using a database." override val throwable = Some(x) override val description = x.getMessage - createAndLog } \ No newline at end of file diff --git a/shrine-webclient/src/main/html/js-i2b2/cells/CRC/CRC_view_QryTool.js b/shrine-webclient/src/main/html/js-i2b2/cells/CRC/CRC_view_QryTool.js index 1dd9db1f6..46c398f43 100644 --- a/shrine-webclient/src/main/html/js-i2b2/cells/CRC/CRC_view_QryTool.js +++ b/shrine-webclient/src/main/html/js-i2b2/cells/CRC/CRC_view_QryTool.js @@ -1,1352 +1,1363 @@ /** * @projectDescription View controller for CRC Query Tool window. * @inherits i2b2.CRC.view * @namespace i2b2.CRC.view.QT * @author Nick Benik, Griffin Weber MD PhD * @version 1.3 * ---------------------------------------------------------------------------------------- * updated 9-15-08: RC4 launch [Nick Benik] */ console.group('Load & Execute component file: CRC > view > Main'); console.time('execute time'); // create and save the screen objects +//create base object and append view methods to it? i2b2.CRC.view['QT'] = new i2b2Base_cellViewController(i2b2.CRC, 'QT'); var queryTimingButton; // define the option functions // ================================================================================================== // -i2b2.CRC.view.QT.showOptions = function(subScreen) { + +/** + * Options pop up at start of query. + */ +i2b2.CRC.view.QT.showOptions = function (subScreen) { + + // if modal options does not exist create it and append event handlers. if (!this.modalOptions) { - var handleSubmit = function() { + + /** + * submit handler. + */ + var handleSubmit = function () { // submit value(s) - if(this.submit()) { - var tmpValue = parseInt($('QryTimeout').value,10); + if (this.submit()) { + var tmpValue = parseInt($('QryTimeout').value, 10); i2b2.CRC.view['QT'].params.queryTimeout = tmpValue; - // var tmpValue = parseInt($('MaxChldDisp').value,10); - // i2b2.CRC.view['QT'].params.maxChildren = tmpValue; } } - var handleCancel = function() { + + /** + * cancel handler + */ + var handleCancel = function () { this.cancel(); } + + /** + * wrap element as Yahoo widget. + */ this.modalOptions = new YAHOO.widget.SimpleDialog("optionsQT", - { width : "400px", - fixedcenter : true, - constraintoviewport : true, + { + width: "400px", + fixedcenter: true, + constraintoviewport: true, modal: true, zindex: 700, - buttons : [ { text:"OK", handler:handleSubmit, isDefault:true }, - { text:"Cancel", handler:handleCancel } ] - } ); + buttons: [{ text: "OK", handler: handleSubmit, isDefault: true }, + { text: "Cancel", handler: handleCancel }] + }); + $('optionsQT').show(); - this.modalOptions.validate = function() { + + /** + * method for validation on submit. + */ + this.modalOptions.validate = function () { // now process the form data var msgError = ''; - // var tmpValue = parseInt($('MaxChldDisp').value,10); - // if (!isNaN(tmpValue) && tmpValue <= 0) { - // msgError += "The max number of Children to display must be a whole number larger then zero.\n"; - // } - var tmpValue = parseInt($('QryTimeout').value,10); + + var tmpValue = parseInt($('QryTimeout').value, 10); if (!isNaN(tmpValue) && tmpValue <= 0) { msgError += "The the query timeout period must be a whole number larger then zero.\n"; } if (msgError) { alert(msgError); return false; } return true; }; this.modalOptions.render(document.body); } + + this.modalOptions.show(); // load settings -// $('MaxChldDisp').value = this.params.maxChildren; $('QryTimeout').value = this.params.queryTimeout; -} +}; -// ================================================================================================== // -i2b2.CRC.view.QT.ContextMenuPreprocess = function(p_oEvent) { + +/** + * ?? + */ +i2b2.CRC.view.QT.ContextMenuPreprocess = function (p_oEvent) { var clickId = false; var clickPanel = false; var isDone = false; var currentNode = this.contextEventTarget; var doNotShow = false; while (!isDone) { // save the first DOM node found with an ID - if (currentNode.id && !clickId) { + if (currentNode.id && !clickId) { clickId = currentNode.id; } // save and exit when we find the linkback to the panel controller if (currentNode.linkbackPanelController) { // we are at the tree root... var clickPanel = currentNode.linkbackPanelController; isDone = true; } if (currentNode.parentNode) { currentNode = currentNode.parentNode; } else { // we have recursed up the tree to the window/document DOM... isDone = true; } } if (!clickId || !clickPanel) { // something is missing, exit this.cancel(); return; } // see if the ID maps back to a treenode with SDX data var tvNode = clickPanel.yuiTree.getNodeByProperty('nodeid', clickId); if (tvNode) { if (!Object.isUndefined(tvNode.data.i2b2_SDX)) { // Make sure the clicked node is at the root level - if (tvNode.parent == clickPanel.yuiTree.getRoot()) { + if (tvNoBde.parent == clickPanel.yuiTree.getRoot()) { if (p_oEvent == "beforeShow") { i2b2.CRC.view.QT.contextRecord = tvNode.data.i2b2_SDX; i2b2.CRC.view.QT.contextPanelCtrlr = clickPanel; // custom build the context menu according to the concept that was clicked var mil = []; var op = i2b2.CRC.view.QT; // all nodes can be deleted - mil.push( { text: "Delete", onclick: { fn: op.ContextMenuRouter, obj: 'delete' }} ); + mil.push({ text: "Delete", onclick: { fn: op.ContextMenuRouter, obj: 'delete' } }); if (i2b2.CRC.view.QT.contextRecord.origData.isModifier) { //Get the blob for this now. - // if (i2b2.CRC.view.QT.contextRecord.origData.xmlOrig != null) { - var cdetails = i2b2.ONT.ajax.GetModifierInfo("CRC:QueryTool", {modifier_applied_path:i2b2.CRC.view.QT.contextRecord.origData.applied_path, modifier_key_value:i2b2.CRC.view.QT.contextRecord.origData.key, ont_synonym_records: true, ont_hidden_records: true} ); + var cdetails = i2b2.ONT.ajax.GetModifierInfo("CRC:QueryTool", { modifier_applied_path: i2b2.CRC.view.QT.contextRecord.origData.applied_path, modifier_key_value: i2b2.CRC.view.QT.contextRecord.origData.key, ont_synonym_records: true, ont_hidden_records: true }); // this is what comes out of the old AJAX call var c = i2b2.h.XPath(cdetails.refXML, 'descendant::modifier'); if (c.length > 0) { i2b2.CRC.view.QT.contextRecord.origData.xmlOrig = c[0]; } - // } - var lvMetaDatas1 = i2b2.h.XPath(i2b2.CRC.view.QT.contextRecord.origData.xmlOrig, 'metadataxml/ValueMetadata[string-length(Version)>0]'); if (lvMetaDatas1.length > 0) { - mil.push( { text: "Set Modifier Value", onclick: { fn: op.ContextMenuRouter, obj: 'setmodifier' }} ); + mil.push({ text: "Set Modifier Value", onclick: { fn: op.ContextMenuRouter, obj: 'setmodifier' } }); } var lvMetaDatas2 = i2b2.h.XPath(i2b2.CRC.view.QT.contextRecord.origData.parent.xmlOrig, 'metadataxml/ValueMetadata[string-length(Version)>0]'); if (lvMetaDatas2.length > 0) { - mil.push( { text: "Set Value...", onclick: { fn: op.ContextMenuRouter, obj: 'labvalues' }} ); + mil.push({ text: "Set Value...", onclick: { fn: op.ContextMenuRouter, obj: 'labvalues' } }); } } else { // For lab tests... if (!Object.isUndefined(i2b2.CRC.view.QT.contextRecord.origData.key)) { - var cdetails = i2b2.ONT.ajax.GetTermInfo("CRC:QueryTool", {concept_key_value:i2b2.CRC.view.QT.contextRecord.origData.key, ont_synonym_records: true, ont_hidden_records: true} ); + var cdetails = i2b2.ONT.ajax.GetTermInfo("CRC:QueryTool", { concept_key_value: i2b2.CRC.view.QT.contextRecord.origData.key, ont_synonym_records: true, ont_hidden_records: true }); var c = i2b2.h.XPath(cdetails.refXML, 'descendant::concept'); if (c.length > 0) { i2b2.CRC.view.QT.contextRecord.origData.xmlOrig = c[0]; } } var lvMetaDatas = i2b2.h.XPath(i2b2.CRC.view.QT.contextRecord.origData.xmlOrig, 'metadataxml/ValueMetadata[string-length(Version)>0]'); if (lvMetaDatas.length > 0) { - mil.push( { text: "Set Value...", onclick: { fn: op.ContextMenuRouter, obj: 'labvalues' }} ); + mil.push({ text: "Set Value...", onclick: { fn: op.ContextMenuRouter, obj: 'labvalues' } }); } } i2b2.CRC.view.QT.ContextMenu.clearContent(); i2b2.CRC.view.QT.ContextMenu.addItems(mil); i2b2.CRC.view.QT.ContextMenu.render(); } } else { // not root level node doNotShow = true; } } else { // no SDX data doNotShow = true; } } else { // not a treenode doNotShow = true; } if (doNotShow) { if (p_oEvent == "beforeShow") { i2b2.CRC.view.QT.ContextMenu.clearContent(); } if (p_oEvent == "triggerContextMenu") { this.cancel(); } } } // ================================================================================================== // -i2b2.CRC.view.QT.ContextMenuRouter = function(a, b, actionName) { +i2b2.CRC.view.QT.ContextMenuRouter = function (a, b, actionName) { // this is used to route the event to the correct handler var op = i2b2.CRC.view.QT; // object path var cdat = { // context node data data: op.contextRecord, ctrlr: op.contextPanelCtrlr }; // route accordingly - switch(actionName) { + switch (actionName) { case "delete": // delete item from the panel cdat.ctrlr._deleteConcept(cdat.data.renderData.htmlID, cdat.data); break; case "labvalues": cdat.ctrlr.showLabValues(cdat.data.sdxInfo.sdxKeyValue, cdat.data); break; case "setmodifier": cdat.ctrlr.showModValues(cdat.data.sdxInfo.sdxKeyValue, cdat.data); break; default: - alert('context event was not found for event "'+actionName+'"'); + alert('context event was not found for event "' + actionName + '"'); } } //================================================================================================== // -i2b2.CRC.view.QT.enableSameTiming = function() { +i2b2.CRC.view.QT.enableSameTiming = function () { if (YAHOO.util.Dom.inDocument(queryTimingButton.getMenu().element)) { var t = queryTimingButton.getMenu().getItems(); if (t.length == 2) { - // queryTimingButton.getMenu().clearContent(); - // queryTimingButton.getMenu().addItems([ - // { text: "Treat Independently", value: "ANY"}]); - // queryTimingButton.getMenu().addItems([ - // { text: "Selected groups occur in the same financial encounter", value: "SAMEVISIT" }]); queryTimingButton.getMenu().addItems([ { text: "Items Instance will be the samer", value: "SAMEINSTANCENUM" }]); queryTimingButton.getMenu().render(); } } else { - queryTimingButton.itemData =[{ text: "Treat Independently", value: "ANY"}, - { text: "Selected groups occur in the same financial encounter", value: "SAMEVISIT"}, - {text: "Items Instance will be the same", value: "SAMEINSTANCENUM" }]; + queryTimingButton.itemData = [{ text: "Treat Independently", value: "ANY" }, + { text: "Selected groups occur in the same financial encounter", value: "SAMEVISIT" }, + { text: "Items Instance will be the same", value: "SAMEINSTANCENUM" }]; } } // ================================================================================================== // -i2b2.CRC.view.QT.setQueryTiming = function(sText) { +i2b2.CRC.view.QT.setQueryTiming = function (sText) { - //TODO cleanup if (YAHOO.util.Dom.inDocument(queryTimingButton.getMenu().element)) { queryTimingButton.getMenu().clearContent(); queryTimingButton.getMenu().addItems([ - { text: "Treat Independently", value: "ANY"}]); + { text: "Treat Independently", value: "ANY" }]); queryTimingButton.getMenu().addItems([ { text: "Selected groups occur in the same financial encounter", value: "SAMEVISIT" }]); queryTimingButton.getMenu().addItems([ { text: "Define sequence of Events", value: "TEMPORAL" }]); if (sText == "SAMEINSTANCENUM") { queryTimingButton.getMenu().addItems([ { text: "Items Instance will be the same", value: "SAMEINSTANCENUM" }]); } queryTimingButton.getMenu().render(); - } else { + } else { - if (sText =="TEMPORAL") { - queryTimingButton.set("label", "Define sequence of Events"); + if (sText == "TEMPORAL") { + queryTimingButton.set("label", "Define sequence of Events"); i2b2.CRC.ctrlr.QT.queryTiming = "TEMPORAL"; $('defineTemporalBar').show(); - } else if (sText =="SAMEVISIT") { - queryTimingButton.set("label", "Selected groups occur in the same financial encounter"); + } else if (sText == "SAMEVISIT") { + queryTimingButton.set("label", "Selected groups occur in the same financial encounter"); } } queryTimingButton.getMenu().render(); var menu = queryTimingButton.getMenu(); - if (sText == "SAMEINSTANCENUM" ) - { + if (sText == "SAMEINSTANCENUM") { var item = menu.getItem(3); - } else if (sText == "SAMEVISIT" ) - { + } else if (sText == "SAMEVISIT") { var item = menu.getItem(1); - } else if (sText == "TEMPORAL" ) - { + } else if (sText == "TEMPORAL") { var item = menu.getItem(2); - } else - { + } else { var item = menu.getItem(0); } queryTimingButton.set("selectedMenuItem", item); } //================================================================================================== // -i2b2.CRC.view.QT.setPanelTiming = function(panelNum, sText) { - if (panelNum > 3) {return} - if (sText == "SAMEVISIT" ) - { - $("queryPanelTimingB" + (panelNum) + "-button").innerHTML = "Occurs in Same Encounter"; +i2b2.CRC.view.QT.setPanelTiming = function (panelNum, sText) { + if (panelNum > 3) { return } + if (sText == "SAMEVISIT") { + $("queryPanelTimingB" + (panelNum) + "-button").innerHTML = "Occurs in Same Encounter"; i2b2.CRC.ctrlr.QT.panelControllers[panelNum - 1].doTiming(sText); i2b2.CRC.ctrlr.QT.panelControllers[panelNum - 1].refTiming.set('disabled', false); } else if (sText == "SAMEINSTANCENUM") { - $("queryPanelTimingB" + (panelNum) + "-button").innerHTML = "Items Instance will be the same"; + $("queryPanelTimingB" + (panelNum) + "-button").innerHTML = "Items Instance will be the same"; i2b2.CRC.ctrlr.QT.panelControllers[panelNum - 1].doTiming(sText); i2b2.CRC.ctrlr.QT.panelControllers[panelNum - 1].refTiming.set('disabled', false); } else { - $("queryPanelTimingB" + (panelNum) + "-button").innerHTML = "Treat Independently"; + $("queryPanelTimingB" + (panelNum) + "-button").innerHTML = "Treat Independently"; i2b2.CRC.ctrlr.QT.panelControllers[panelNum - 1].doTiming(sText); } } // ================================================================================================== // -i2b2.CRC.view.QT.ZoomView = function() { +i2b2.CRC.view.QT.ZoomView = function () { i2b2.hive.MasterView.toggleZoomWindow("QT"); } // ================================================================================================== // -i2b2.CRC.view.QT.Resize = function(e) { - //var ds = document.viewport.getDimensions(); - //var w = ds.width; - //var h = ds.height - var w = window.innerWidth || (window.document.documentElement.clientWidth || window.document.body.clientWidth); - var h = window.innerHeight || (window.document.documentElement.clientHeight || window.document.body.clientHeight); - - - if (w < 840) {w = 840;} - if (h < 517) {h = 517;} +i2b2.CRC.view.QT.Resize = function (e) { + //var ds = document.viewport.getDimensions(); + //var w = ds.width; + //var h = ds.height + var w = window.innerWidth || (window.document.documentElement.clientWidth || window.document.body.clientWidth); + var h = window.innerHeight || (window.document.documentElement.clientHeight || window.document.body.clientHeight); + + + if (w < 840) { w = 840; } + if (h < 517) { h = 517; } // resize our visual components - //var queryToolWidth = ds.width * 0.6; - //$('crcQueryToolBox').style.left = w-queryToolWidth; - //debugOnScreen("crcQueryToolBox.width = " + queryToolWidth ); + $('crcQueryToolBox').style.left = w - 550; - $('crcQueryToolBox').style.left = w-550; if (i2b2.WORK && i2b2.WORK.isLoaded) { var z = h - 400; //392 + 44 - 17 - 25; - if (i2b2.CRC.view.QT.isZoomed) { z += 196 - 44; } + if (i2b2.CRC.view.QT.isZoomed) { z += 196 - 44; }//you mean 152? } else { - var z = h - 392 - 17 - 25; + var z = h - 392 - 17 - 25;// why not just: h - 350? if (i2b2.CRC.view.QT.isZoomed) { z += 196; } } // display the topic selector bar if we are in SHRINE-mode if (i2b2.h.isSHRINE()) { $('queryTopicPanel').show(); z = z - 28; } $('QPD1').style.height = z; $('QPD2').style.height = z; $('QPD3').style.height = z; $('temporalbuilders').style.height = z + 50; } -//YAHOO.util.Event.addListener(window, "resize", i2b2.CRC.view.QT.Resize, i2b2.CRC.view.QT); // tdw9 - //================================================================================================== // -i2b2.CRC.view.QT.splitterDragged = function() -{ - //var viewPortDim = document.viewport.getDimensions(); - var w = window.innerWidth || (window.document.documentElement.clientWidth || window.document.body.clientWidth); - - var splitter = $( i2b2.hive.mySplitter.name ); +i2b2.CRC.view.QT.splitterDragged = function () { + //var viewPortDim = document.viewport.getDimensions(); + var w = window.innerWidth || (window.document.documentElement.clientWidth || window.document.body.clientWidth); + + var splitter = $(i2b2.hive.mySplitter.name); var CRCQT = $("crcQueryToolBox"); var CRCQTBodyBox = $("crcQueryToolBox.bodyBox"); - var CRCQueryName = $("queryName"); - var CRCQueryNameBar = $("queryNameBar"); - var temporalConstraintBar = $("temporalConstraintBar"); - var defineTemporalBar = $("defineTemporalBar"); + var CRCQueryName = $("queryName"); + var CRCQueryNameBar = $("queryNameBar"); + var temporalConstraintBar = $("temporalConstraintBar"); + var defineTemporalBar = $("defineTemporalBar"); var temporalConstraintLabel = $("temporalConstraintLabel"); - var temporalConstraintDiv = $("temporalConstraintDiv"); - var queryTiming = $("queryTiming"); - var queryTimingButton = $("queryTiming-button"); + var temporalConstraintDiv = $("temporalConstraintDiv"); + var queryTiming = $("queryTiming"); + var queryTimingButton = $("queryTiming-button"); - var defineTemporal = $("defineTemporal"); - var defineTemporalButton = $("defineTemporal-button"); + var defineTemporal = $("defineTemporal"); + var defineTemporalButton = $("defineTemporal-button"); - var CRCQueryPanels = $("crcQryToolPanels"); - var CRCinnerQueryPanel = $("crc.innerQueryPanel"); - var CRCtemoralBuilder = $("crc.temoralBuilder"); - var basicWidth = parseInt(w) - parseInt(splitter.style.left) - parseInt(splitter.offsetWidth); + var CRCQueryPanels = $("crcQryToolPanels"); + var CRCinnerQueryPanel = $("crc.innerQueryPanel"); + var CRCtemoralBuilder = $("crc.temoralBuilder"); + var basicWidth = parseInt(w) - parseInt(splitter.style.left) - parseInt(splitter.offsetWidth); /* Title, buttons, and panels */ - CRCQT.style.left = parseInt(splitter.offsetWidth) + parseInt(splitter.style.left) + 3 + "px"; - CRCQT.style.width = Math.max(basicWidth - 24, 0) + "px"; - CRCQTBodyBox.style.width = Math.max(basicWidth - 41, 0) + "px"; - - CRCQueryNameBar.style.width = Math.max(basicWidth - 38, 0) + "px"; - temporalConstraintBar.style.width = Math.max(basicWidth - 38, 0) + "px"; - defineTemporalBar.style.width = Math.max(basicWidth - 38, 0) + "px"; - temporalConstraintDiv.style.width = Math.max( parseInt(temporalConstraintBar.style.width) - parseInt(temporalConstraintLabel.style.width)-2, 0) + "px"; - queryTimingButton.style.width = Math.max( parseInt(temporalConstraintBar.style.width) - 250,0) + "px"; - defineTemporalButton.style.width = Math.max( parseInt(temporalConstraintBar.style.width) - 250,0) + "px"; + CRCQT.style.left = parseInt(splitter.offsetWidth) + parseInt(splitter.style.left) + 3 + "px"; + CRCQT.style.width = Math.max(basicWidth - 24, 0) + "px"; + CRCQTBodyBox.style.width = Math.max(basicWidth - 41, 0) + "px"; + + CRCQueryNameBar.style.width = Math.max(basicWidth - 38, 0) + "px"; + temporalConstraintBar.style.width = Math.max(basicWidth - 38, 0) + "px"; + defineTemporalBar.style.width = Math.max(basicWidth - 38, 0) + "px"; + temporalConstraintDiv.style.width = Math.max(parseInt(temporalConstraintBar.style.width) - parseInt(temporalConstraintLabel.style.width) - 2, 0) + "px"; + queryTimingButton.style.width = Math.max(parseInt(temporalConstraintBar.style.width) - 250, 0) + "px"; + defineTemporalButton.style.width = Math.max(parseInt(temporalConstraintBar.style.width) - 250, 0) + "px"; //parseInt(temporalConstraintLabel.style.width)-23, 0) + "px"; - CRCQueryName.style.width = Math.max(basicWidth - 128, 0) + "px"; // use max to avoid negative width + CRCQueryName.style.width = Math.max(basicWidth - 128, 0) + "px"; // use max to avoid negative width - CRCQueryPanels.style.width = Math.max(basicWidth - 30, 0) + "px"; - CRCinnerQueryPanel.style.width = Math.max(basicWidth - 36, 0) + "px"; - CRCtemoralBuilder.style.width = Math.max(basicWidth - 36, 0) + "px"; + CRCQueryPanels.style.width = Math.max(basicWidth - 30, 0) + "px"; + CRCinnerQueryPanel.style.width = Math.max(basicWidth - 36, 0) + "px"; + CRCtemoralBuilder.style.width = Math.max(basicWidth - 36, 0) + "px"; - var panelWidth = (basicWidth - 36)/3 - 4; + var panelWidth = (basicWidth - 36) / 3 - 4; var panels = CRCinnerQueryPanel.childNodes; var panelArray = new Array(3); var panelCount = 0; - for ( var i = 0; i < panels.length; i++ ) - { - if ( panels[i].className === "qryPanel") - { + for (var i = 0; i < panels.length; i++) { + if (panels[i].className === "qryPanel") { panels[i].style.width = Math.max(panelWidth, 0) + "px"; var nodes = panels[i].childNodes; - for ( var j = 0; j < nodes.length; j++ ) - { + for (var j = 0; j < nodes.length; j++) { if (nodes[j].className === "qryPanelTitle") nodes[j].style.width = Math.max(panelWidth - 2, 0) + "px"; - else if ( nodes[j].className === "qryPanelButtonBar" ) - { + else if (nodes[j].className === "qryPanelButtonBar") { nodes[j].style.width = Math.max(panelWidth, 0) + "px"; var buttons = nodes[j].childNodes; - for ( var k = 0; k < buttons.length; k++) - { - if ( buttons[k].className === "qryButtonOccurs") + for (var k = 0; k < buttons.length; k++) { + if (buttons[k].className === "qryButtonOccurs") buttons[k].style.width = Math.max(panelWidth - 88, 0) + "px"; } } - else if ( nodes[j].className === "qryPanelTiming" ) - { + else if (nodes[j].className === "qryPanelTiming") { nodes[j].style.width = Math.max(panelWidth, 0) + "px"; var queryPanelTimingChildren = nodes[j].childNodes; - for ( var k = 0; k < queryPanelTimingChildren.length; k++) - { - if ( queryPanelTimingChildren[k].style == null ) + for (var k = 0; k < queryPanelTimingChildren.length; k++) { + if (queryPanelTimingChildren[k].style == null) continue; queryPanelTimingChildren[k].style.width = Math.max(panelWidth - 4, 0) + "px"; } //handle the special "queryPanelTimingB1" var queryPanelTimingB1 = $("queryPanelTimingB1"); queryPanelTimingB1.style.width = Math.max(panelWidth - 4, 0) + "px"; } - else if ( nodes[j].className === "queryPanel" || nodes[j].className === "queryPanel queryPanelDisabled" ) // QueryPanel or disabled QueryPanel - nodes[j].style.width = Math.max(panelWidth - 8, 0) + "px"; + else if (nodes[j].className === "queryPanel" || nodes[j].className === "queryPanel queryPanelDisabled") // QueryPanel or disabled QueryPanel + nodes[j].style.width = Math.max(panelWidth - 8, 0) + "px"; } panelArray[panelCount] = panels[i]; panelCount++; } else continue; } /* Deal with Footer and its components */ var footer = $("qryToolFooter"); // footer var printBox = $('printQueryBox'); // print query var groupCount = $("groupCount"); // # of groups var scrollBox = $("scrollBox"); // scroll control footer.style.width = Math.max(basicWidth - 40, 0) + "px"; // adjust footer width - groupCount.style.width = Math.max(parseInt(footer.style.width) - (printBox.offsetLeft + printBox.offsetWidth) - scrollBox.offsetWidth - 5, 0) + "px"; // adjust groupCount width + groupCount.style.width = Math.max(parseInt(footer.style.width) - (printBox.offsetLeft + printBox.offsetWidth) - scrollBox.offsetWidth - 5, 0) + "px"; // adjust groupCount width /* Deal with Baloons */ - var baloonBox = $("queryBalloonBox"); + var baloonBox = $("queryBalloonBox"); var baloons = baloonBox.getElementsByTagName("div"); - for ( var i = 0; i < baloons.length; i++ ) - { - if ( i%2 === 0) // even baloons + for (var i = 0; i < baloons.length; i++) { + if (i % 2 === 0) // even baloons { - var index = i/2; - if ( index < baloons.length) - baloons[i].style.left = panelArray[index].offsetLeft + parseInt(panelArray[index].style.width)/2 - 35 + "px"; + var index = i / 2; + if (index < baloons.length) + baloons[i].style.left = panelArray[index].offsetLeft + parseInt(panelArray[index].style.width) / 2 - 35 + "px"; } - else - { - var index = Math.floor(i/2); - baloons[i].style.left = panelArray[index].offsetLeft + parseInt(panelArray[index].style.width) - 22.5 + "px"; + else { + var index = Math.floor(i / 2); + baloons[i].style.left = panelArray[index].offsetLeft + parseInt(panelArray[index].style.width) - 22.5 + "px"; } } } //================================================================================================== // -i2b2.CRC.view.QT.ResizeHeight = function() { - //var ds = document.viewport.getDimensions(); - //var h = ds.height; - //var h = window.document.documentElement.clientHeight; - var h = window.innerHeight || (window.document.documentElement.clientHeight || window.document.body.clientHeight); - - if (h < 517) {h = 517;} +i2b2.CRC.view.QT.ResizeHeight = function () { + var h = window.innerHeight || (window.document.documentElement.clientHeight || window.document.body.clientHeight); + + if (h < 517) { h = 517; } // resize our visual components if (i2b2.WORK && i2b2.WORK.isLoaded) { var z = h - 400; if (i2b2.CRC.view.QT.isZoomed) { z += 196 - 44; } } else { var z = h - 434; if (i2b2.CRC.view.QT.isZoomed) { z += 196; } } // display the topic selector bar if we are in SHRINE-mode if (i2b2.h.isSHRINE() && $('queryTopicPanel')) { $('queryTopicPanel').show(); z = z - 28; } if ($('defineTemporalBar').style.display === '') z = z - 20; $('QPD1').style.height = z; $('QPD2').style.height = z; $('QPD3').style.height = z; $('temporalbuilders').style.height = z + 50; } -i2b2.CRC.view.QT.deleteLastTemporalGroup = function() { - - if(i2b2.CRC.model.queryCurrent.panels.length > 3){ - var currentPanels = i2b2.CRC.model.queryCurrent.panels.length - 1; - i2b2.CRC.model.queryCurrent.panels.pop(); - defineTemporalButton.getMenu().removeItem(defineTemporalButton.getMenu().getItems().length-1); - - for( var i = 0; i < i2b2.CRC.ctrlr.QT.tenporalBuilders + 1; i++){ - var select = document.getElementById("instancevent1["+i+"]"); - select.remove(select.length - 1); - - select = document.getElementById("instancevent2["+i+"]"); - select.remove(select.length - 1); - } - - alert('Event ' + currentPanels + ' has been removed.'); - //i2b2.CRC.ctrlr.QT.temporalGroup = i2b2.CRC.model.queryCurrent.panels.length; - - defineTemporalButton.getMenu().getItem(0).element.click() - - } else { - alert('You must leave a minimum of two events.'); - - } +i2b2.CRC.view.QT.addNewTemporalGroup = function() { + + $('addDefineGroup-button').disable(); + + i2b2.CRC.ctrlr.QT.temporalGroup = i2b2.CRC.model.queryCurrent.panels.length; + //i2b2.CRC.ctrlr.QT.temporalGroup = i2b2.CRC.ctrlr.QT.temporalGroup + 1; + + if (YAHOO.util.Dom.inDocument(defineTemporalButton.getMenu().element)) { + defineTemporalButton.getMenu().addItems([ + { text: "Event " + (i2b2.CRC.ctrlr.QT.temporalGroup), value: i2b2.CRC.ctrlr.QT.temporalGroup}]); + defineTemporalButton.getMenu().render(); + } else { + var aMenuItemData=[]; + aMenuItemData[0] = {text: "Event " + (i2b2.CRC.ctrlr.QT.temporalGroup), value: i2b2.CRC.ctrlr.QT.temporalGroup} ; + defineTemporalButton.getMenu().itemData = aMenuItemData; + } + + i2b2.CRC.model.queryCurrent.panels[i2b2.CRC.ctrlr.QT.temporalGroup] = {}; + this.yuiTree = new YAHOO.widget.TreeView("QPD1"); + i2b2.CRC.ctrlr.QT.panelAdd(this.yuiTree); + i2b2.CRC.ctrlr.QT._redrawAllPanels(); + + //Add to define a query + for( var i = 0; i < i2b2.CRC.ctrlr.QT.tenporalBuilders + 1; i++){ + var select = document.getElementById("instancevent1["+i+"]"); + select.options[select.options.length] = new Option( 'Event '+i2b2.CRC.ctrlr.QT.temporalGroup, i2b2.CRC.ctrlr.QT.temporalGroup); + + select = document.getElementById("instancevent2["+i+"]"); + select.options[select.options.length] = new Option( 'Event '+i2b2.CRC.ctrlr.QT.temporalGroup, i2b2.CRC.ctrlr.QT.temporalGroup); + } + + alert('New Event ' + i2b2.CRC.ctrlr.QT.temporalGroup + ' has been added.'); + + $('addDefineGroup-button').enable(); + + + +}; + +i2b2.CRC.view.QT.deleteLastTemporalGroup = function () { + + if (i2b2.CRC.model.queryCurrent.panels.length > 3) { + var currentPanels = i2b2.CRC.model.queryCurrent.panels.length - 1; + i2b2.CRC.model.queryCurrent.panels.pop(); + defineTemporalButton.getMenu().removeItem(defineTemporalButton.getMenu().getItems().length - 1); + + for (var i = 0; i < i2b2.CRC.ctrlr.QT.tenporalBuilders + 1; i++) { + var select = document.getElementById("instancevent1[" + i + "]"); + select.remove(select.length - 1); + + select = document.getElementById("instancevent2[" + i + "]"); + select.remove(select.length - 1); + } + + alert('Event ' + currentPanels + ' has been removed.'); + + defineTemporalButton.getMenu().getItem(0).element.click() + + } else { + alert('You must leave a minimum of two events.'); + + } }; // This is done once the entire cell has been loaded console.info("SUBSCRIBED TO i2b2.events.afterCellInit"); i2b2.events.afterCellInit.subscribe( - (function(en,co) { - if (co[0].cellCode=='CRC') { -// ================================================================================================== // + (function (en, co) { + if (co[0].cellCode == 'CRC') { + // ================================================================================================== // console.debug('[EVENT CAPTURED i2b2.events.afterCellInit]'); //Update the result types from ajax call var scopedCallback = new i2b2_scopedCallback(); - scopedCallback.callback = function(results) { + scopedCallback.callback = function (results) { //var cl_onCompleteCB = onCompleteCallback; // THIS function is used to process the AJAX results of the getChild call // results data object contains the following attributes: // refXML: xmlDomObject <--- for data processing // msgRequest: xml (string) // msgResponse: xml (string) // error: boolean // errorStatus: string [only with error=true] // errorMsg: string [only with error=true] var retMsg = { error: results.error, msgRequest: results.msgRequest, msgResponse: results.msgResponse, msgUrl: results.msgUrl, results: null }; var retChildren = []; // extract records from XML msg var newHTML = ""; var ps = results.refXML.getElementsByTagName('query_result_type'); - for(var i1=0; i1 " + o.description + ""; + ///@todo: condition commented out for configurable result types if (o.visual_attribute_type == "LA") { + newHTML += "
" + o.description + "
"; ///} } - //commented out for @pcori_webclient. $('dialogQryRunResultType').innerHTML = newHTML; } i2b2.CRC.ajax.getQRY_getResultType("CRC:SDX:PatientRecordSet", null, scopedCallback); // register the query panels as valid DragDrop targets for Ontology Concepts (CONCPT) and query master (QM) objects - var op_trgt = {dropTarget:true}; + var op_trgt = { dropTarget: true }; i2b2.sdx.Master.AttachType('QPD1', 'CONCPT', op_trgt); i2b2.sdx.Master.AttachType('QPD2', 'CONCPT', op_trgt); i2b2.sdx.Master.AttachType('QPD3', 'CONCPT', op_trgt); i2b2.sdx.Master.AttachType('QPD1', 'ENS', op_trgt); i2b2.sdx.Master.AttachType('QPD2', 'ENS', op_trgt); i2b2.sdx.Master.AttachType('QPD3', 'ENS', op_trgt); i2b2.sdx.Master.AttachType('QPD1', 'PRS', op_trgt); i2b2.sdx.Master.AttachType('QPD2', 'PRS', op_trgt); i2b2.sdx.Master.AttachType('QPD3', 'PRS', op_trgt); i2b2.sdx.Master.AttachType('QPD1', 'QM', op_trgt); i2b2.sdx.Master.AttachType('QPD2', 'QM', op_trgt); i2b2.sdx.Master.AttachType('QPD3', 'QM', op_trgt); i2b2.sdx.Master.AttachType('queryName', 'QM', op_trgt); //======================= ======================= - var funcHovOverQM = function(e, id, ddProxy) { + var funcHovOverQM = function (e, id, ddProxy) { var el = $(id); // apply DragDrop targeting CCS var targets = YAHOO.util.DDM.getRelated(ddProxy, true); - for (var i=0; i ======================= //======================= ======================= - i2b2.sdx.Master.setHandlerCustom('QPD1', 'CONCPT', 'DropHandler', (function(sdxData) { + i2b2.sdx.Master.setHandlerCustom('QPD1', 'CONCPT', 'DropHandler', (function (sdxData) { sdxData = sdxData[0]; // only interested in first record var t = i2b2.CRC.ctrlr.QT.panelControllers[0]; - if (t.isActive=="Y") { t.doDrop(sdxData); } + if (t.isActive == "Y") { t.doDrop(sdxData); } })); - i2b2.sdx.Master.setHandlerCustom('QPD2', 'CONCPT', 'DropHandler', (function(sdxData) { + i2b2.sdx.Master.setHandlerCustom('QPD2', 'CONCPT', 'DropHandler', (function (sdxData) { sdxData = sdxData[0]; // only interested in first record var t = i2b2.CRC.ctrlr.QT.panelControllers[1]; - if (t.isActive=="Y") { t.doDrop(sdxData); } + if (t.isActive == "Y") { t.doDrop(sdxData); } })); - i2b2.sdx.Master.setHandlerCustom('QPD3', 'CONCPT', 'DropHandler', (function(sdxData) { + i2b2.sdx.Master.setHandlerCustom('QPD3', 'CONCPT', 'DropHandler', (function (sdxData) { sdxData = sdxData[0]; // only interested in first record var t = i2b2.CRC.ctrlr.QT.panelControllers[2]; - if (t.isActive=="Y") { t.doDrop(sdxData); } + if (t.isActive == "Y") { t.doDrop(sdxData); } })); - i2b2.sdx.Master.setHandlerCustom('QPD1', 'ENS', 'DropHandler', (function(sdxData) { + i2b2.sdx.Master.setHandlerCustom('QPD1', 'ENS', 'DropHandler', (function (sdxData) { sdxData = sdxData[0]; // only interested in first record var t = i2b2.CRC.ctrlr.QT.panelControllers[0]; - if (t.isActive=="Y") { t.doDrop(sdxData); } + if (t.isActive == "Y") { t.doDrop(sdxData); } })); - i2b2.sdx.Master.setHandlerCustom('QPD2', 'ENS', 'DropHandler', (function(sdxData) { + i2b2.sdx.Master.setHandlerCustom('QPD2', 'ENS', 'DropHandler', (function (sdxData) { sdxData = sdxData[0]; // only interested in first record var t = i2b2.CRC.ctrlr.QT.panelControllers[1]; - if (t.isActive=="Y") { t.doDrop(sdxData); } + if (t.isActive == "Y") { t.doDrop(sdxData); } })); - i2b2.sdx.Master.setHandlerCustom('QPD3', 'ENS', 'DropHandler', (function(sdxData) { + i2b2.sdx.Master.setHandlerCustom('QPD3', 'ENS', 'DropHandler', (function (sdxData) { sdxData = sdxData[0]; // only interested in first record var t = i2b2.CRC.ctrlr.QT.panelControllers[2]; - if (t.isActive=="Y") { t.doDrop(sdxData); } + if (t.isActive == "Y") { t.doDrop(sdxData); } })); - i2b2.sdx.Master.setHandlerCustom('QPD1', 'PRS', 'DropHandler', (function(sdxData) { + i2b2.sdx.Master.setHandlerCustom('QPD1', 'PRS', 'DropHandler', (function (sdxData) { sdxData = sdxData[0]; // only interested in first record var t = i2b2.CRC.ctrlr.QT.panelControllers[0]; - if (t.isActive=="Y") { t.doDrop(sdxData); } + if (t.isActive == "Y") { t.doDrop(sdxData); } })); - i2b2.sdx.Master.setHandlerCustom('QPD2', 'PRS', 'DropHandler', (function(sdxData) { + i2b2.sdx.Master.setHandlerCustom('QPD2', 'PRS', 'DropHandler', (function (sdxData) { sdxData = sdxData[0]; // only interested in first record var t = i2b2.CRC.ctrlr.QT.panelControllers[1]; - if (t.isActive=="Y") { t.doDrop(sdxData); } + if (t.isActive == "Y") { t.doDrop(sdxData); } })); - i2b2.sdx.Master.setHandlerCustom('QPD3', 'PRS', 'DropHandler', (function(sdxData) { + i2b2.sdx.Master.setHandlerCustom('QPD3', 'PRS', 'DropHandler', (function (sdxData) { sdxData = sdxData[0]; // only interested in first record var t = i2b2.CRC.ctrlr.QT.panelControllers[2]; - if (t.isActive=="Y") { t.doDrop(sdxData); } + if (t.isActive == "Y") { t.doDrop(sdxData); } })); - i2b2.sdx.Master.setHandlerCustom('QPD1', 'QM', 'DropHandler', (function(sdxData) { + i2b2.sdx.Master.setHandlerCustom('QPD1', 'QM', 'DropHandler', (function (sdxData) { sdxData = sdxData[0]; // only interested in first record var t = i2b2.CRC.ctrlr.QT.panelControllers[0]; - if (t.isActive=="Y") { t.doDrop(sdxData); } + if (t.isActive == "Y") { t.doDrop(sdxData); } })); - i2b2.sdx.Master.setHandlerCustom('QPD2', 'QM', 'DropHandler', (function(sdxData) { + i2b2.sdx.Master.setHandlerCustom('QPD2', 'QM', 'DropHandler', (function (sdxData) { sdxData = sdxData[0]; // only interested in first record var t = i2b2.CRC.ctrlr.QT.panelControllers[1]; - if (t.isActive=="Y") { t.doDrop(sdxData); } + if (t.isActive == "Y") { t.doDrop(sdxData); } })); - i2b2.sdx.Master.setHandlerCustom('QPD3', 'QM', 'DropHandler', (function(sdxData) { + i2b2.sdx.Master.setHandlerCustom('QPD3', 'QM', 'DropHandler', (function (sdxData) { sdxData = sdxData[0]; // only interested in first record var t = i2b2.CRC.ctrlr.QT.panelControllers[2]; - if (t.isActive=="Y") { t.doDrop(sdxData); } + if (t.isActive == "Y") { t.doDrop(sdxData); } })) - var funcATN = function(yuiTree, yuiParentNode, sdxDataPack, callbackLoader) { - var myobj = { html: sdxDataPack.renderData.html, nodeid: sdxDataPack.renderData.htmlID} + var funcATN = function (yuiTree, yuiParentNode, sdxDataPack, callbackLoader) { + var myobj = { html: sdxDataPack.renderData.html, nodeid: sdxDataPack.renderData.htmlID } // if the treenode we are appending to is the root node then do not show the [+] infront if (yuiTree.getRoot() == yuiParentNode) { var tmpNode = new YAHOO.widget.HTMLNode(myobj, yuiParentNode, false, false); } else { var tmpNode = new YAHOO.widget.HTMLNode(myobj, yuiParentNode, false, true); } if (sdxDataPack.renderData.iconType != 'CONCPT_item' && !Object.isUndefined(callbackLoader)) { // add the callback to load child nodes sdxDataPack.sdxInfo.sdxLoadChildren = callbackLoader; } - tmpNode.data.i2b2_SDX= sdxDataPack; - tmpNode.toggle = function() { - if (!this.tree.locked && ( this.hasChildren(true) ) ) { + tmpNode.data.i2b2_SDX = sdxDataPack; + tmpNode.toggle = function () { + if (!this.tree.locked && (this.hasChildren(true))) { var data = this.data.i2b2_SDX.renderData; var img = this.getContentEl(); - img = Element.select(img,'img')[0]; + img = Element.select(img, 'img')[0]; if (this.expanded) { img.src = data.icon; this.collapse(); } else { img.src = data.iconExp; this.expand(); } } }; if (sdxDataPack.renderData.iconType == 'CONCPT_leaf' || !sdxDataPack.renderData.canExpand) { tmpNode.dynamicLoadComplete = true; } } i2b2.sdx.Master.setHandlerCustom('QPD1', 'CONCPT', 'AppendTreeNode', funcATN); i2b2.sdx.Master.setHandlerCustom('QPD2', 'CONCPT', 'AppendTreeNode', funcATN); i2b2.sdx.Master.setHandlerCustom('QPD3', 'CONCPT', 'AppendTreeNode', funcATN); i2b2.sdx.Master.setHandlerCustom('QPD1', 'ENS', 'AppendTreeNode', funcATN); i2b2.sdx.Master.setHandlerCustom('QPD2', 'ENS', 'AppendTreeNode', funcATN); i2b2.sdx.Master.setHandlerCustom('QPD3', 'ENS', 'AppendTreeNode', funcATN); i2b2.sdx.Master.setHandlerCustom('QPD1', 'PRS', 'AppendTreeNode', funcATN); i2b2.sdx.Master.setHandlerCustom('QPD2', 'PRS', 'AppendTreeNode', funcATN); i2b2.sdx.Master.setHandlerCustom('QPD3', 'PRS', 'AppendTreeNode', funcATN); - var funcQMDH = function(sdxData) { + var funcQMDH = function (sdxData) { sdxData = sdxData[0]; // only interested in first record // pass the QM ID to be loaded var qm_id = sdxData.sdxInfo.sdxKeyValue; i2b2.CRC.ctrlr.QT.doQueryLoad(qm_id) }; i2b2.sdx.Master.setHandlerCustom('QPD1', 'QM', 'DropHandler', funcQMDH); i2b2.sdx.Master.setHandlerCustom('QPD2', 'QM', 'DropHandler', funcQMDH); i2b2.sdx.Master.setHandlerCustom('QPD3', 'QM', 'DropHandler', funcQMDH); i2b2.sdx.Master.setHandlerCustom('queryName', 'QM', 'DropHandler', funcQMDH); //======================= ======================= // ========= Override default LoadChildrenFromTreeview handler (we need this so that we can properly capture the XML request/response messages) ========= - var funcLCFT = function(node, onCompleteCallback) { + var funcLCFT = function (node, onCompleteCallback) { var scopedCallback = new i2b2_scopedCallback(); scopedCallback.scope = node.data.i2b2_SDX; - scopedCallback.callback = function(results) { + scopedCallback.callback = function (results) { var cl_node = node; var cl_onCompleteCB = onCompleteCallback; var cl_options = options; // THIS function is used to process the AJAX results of the getChild call // results data object contains the following attributes: // refXML: xmlDomObject <--- for data processing // msgRequest: xml (string) // msgResponse: xml (string) // error: boolean // errorStatus: string [only with error=true] // errorMsg: string [only with error=true] // i2b2.CRC.view.QT.queryResponse = results.msgResponse; i2b2.CRC.view.QT.queryRequest = results.msgRequest; i2b2.CRC.view.QT.queryUrl = results.msgUrl; // // clear the drop-lock so the node can be requeried if anything bad happens below node.data.i2b2_dropLock = false; // handle any errors if (results.error) { // process the specific error var errorCode = results.refXML.getElementsByTagName('status')[0].firstChild.nodeValue; if (errorCode == "MAX_EXCEEDED") { var eaction = confirm("The number of children in this node exceeds the maximum number you specified in options.\n Displaying all children may take a long time to do."); } else { alert("The following error has occurred:\n" + errorCode); } // re-fire the call with no max limit if the user requested so if (eaction) { var mod_options = Object.clone(cl_options); delete mod_options.ont_max_records; i2b2.ONT.ajax.GetChildConcepts("CRC:QueryTool", mod_options, scopedCallback); return true; } // ROLLBACK the tree changes cl_onCompleteCB(); // reset dynamic load state for the node (total hack of YUI Treeview) node.collapse(); node.dynamicLoadComplete = false; node.expanded = false; node.childrenRendered = false; node._dynLoad = true; // uber-elite code (fix the style settings) var tc = node.getToggleEl().className; tc = tc.substring(0, tc.length - 1) + 'p'; node.getToggleEl().className = tc; // fix the icon image var img = node.getContentEl(); img = Element.select(img, 'img')[0]; img.src = node.data.i2b2_SDX.sdxInfo.icon; return false; } var c = results.refXML.getElementsByTagName('concept'); - for(var i=0; i<1*c.length; i++) { + for (var i = 0; i < 1 * c.length; i++) { var o = new Object; o.xmlOrig = c[i]; - o.name = i2b2.h.getXNodeVal(c[i],'name'); - o.hasChildren = i2b2.h.getXNodeVal(c[i],'visualattributes').substring(0,2); - o.level = i2b2.h.getXNodeVal(c[i],'level'); - o.key = i2b2.h.getXNodeVal(c[i],'key'); - o.tooltip = i2b2.h.getXNodeVal(c[i],'tooltip'); + o.name = i2b2.h.getXNodeVal(c[i], 'name'); + o.hasChildren = i2b2.h.getXNodeVal(c[i], 'visualattributes').substring(0, 2); + o.level = i2b2.h.getXNodeVal(c[i], 'level'); + o.key = i2b2.h.getXNodeVal(c[i], 'key'); + o.tooltip = i2b2.h.getXNodeVal(c[i], 'tooltip'); o.icd9 = ''; - o.table_name = i2b2.h.getXNodeVal(c[i],'tablename'); - o.column_name = i2b2.h.getXNodeVal(c[i],'columnname'); - o.operator = i2b2.h.getXNodeVal(c[i],'operator'); - o.dim_code = i2b2.h.getXNodeVal(c[i],'dimcode'); + o.table_name = i2b2.h.getXNodeVal(c[i], 'tablename'); + o.column_name = i2b2.h.getXNodeVal(c[i], 'columnname'); + o.operator = i2b2.h.getXNodeVal(c[i], 'operator'); + o.dim_code = i2b2.h.getXNodeVal(c[i], 'dimcode'); // append the data node - var sdxDataNode = i2b2.sdx.Master.EncapsulateData('CONCPT',o); + var sdxDataNode = i2b2.sdx.Master.EncapsulateData('CONCPT', o); var renderOptions = { title: o.name, - dblclick: "i2b2.ONT.view.nav.ToggleNode(this,'"+cl_node.tree.id+"')", + dblclick: "i2b2.ONT.view.nav.ToggleNode(this,'" + cl_node.tree.id + "')", icon: { root: "sdx_ONT_CONCPT_root.gif", rootExp: "sdx_ONT_CONCPT_root-exp.gif", branch: "sdx_ONT_CONCPT_branch.gif", branchExp: "sdx_ONT_CONCPT_branch-exp.gif", leaf: "sdx_ONT_CONCPT_leaf.gif" } }; var sdxRenderData = i2b2.sdx.Master.RenderHTML(cl_node.tree.id, sdxDataNode, renderOptions); i2b2.sdx.Master.AppendTreeNode(cl_node.tree, cl_node, sdxRenderData); } // handle the YUI treeview cl_onCompleteCB(); } // fix double loading error via node level dropping-lock if (node.data.i2b2_dropLock) { return true; } node.data.i2b2_dropLock = true; var options = {}; - options.ont_max_records = "max='" +i2b2.CRC.cfg.params.maxChildren + "'"; - options.result_wait_time= i2b2.CRC.cfg.params.queryTimeout; + options.ont_max_records = "max='" + i2b2.CRC.cfg.params.maxChildren + "'"; + options.result_wait_time = i2b2.CRC.cfg.params.queryTimeout; options.ont_synonym_records = i2b2.ONT.cfg.params.synonyms; options.ont_hidden_records = i2b2.ONT.cfg.params.hiddens; // parent key options.concept_key_value = node.data.i2b2_SDX.sdxInfo.sdxKeyValue; options.version = i2b2.ClientVersion; i2b2.ONT.ajax.GetChildConcepts("CRC:QueryTool", options, scopedCallback); } i2b2.sdx.Master.setHandlerCustom('QPD1', 'CONCPT', 'LoadChildrenFromTreeview', funcLCFT); i2b2.sdx.Master.setHandlerCustom('QPD2', 'CONCPT', 'LoadChildrenFromTreeview', funcLCFT); i2b2.sdx.Master.setHandlerCustom('QPD3', 'CONCPT', 'LoadChildrenFromTreeview', funcLCFT); // ========= END Override default LoadChildrenFromTreeview handler (we need this so that we can properly capture the XML request/response messages) END ========= //======================= ======================= // Connect the panel controllers to the DOM nodes in the document var t = i2b2.CRC.ctrlr.QT; - queryTimingButton = new YAHOO.widget.Button("queryTiming", - { lazyLoad: "false", type: "menu", menu: "menubutton1select", name:"querytiming" }); + queryTimingButton = new YAHOO.widget.Button("queryTiming", + { lazyLoad: "false", type: "menu", menu: "menubutton1select", name: "querytiming" }); - defineTemporalButton = new YAHOO.widget.Button("defineTemporal", - { lazyLoad: "false", type: "menu", menu: "menubutton2select", name:"definetemporal" }); + defineTemporalButton = new YAHOO.widget.Button("defineTemporal", + { lazyLoad: "false", type: "menu", menu: "menubutton2select", name: "definetemporal" }); var addDefineGroup = new YAHOO.widget.Button("addDefineGroup"); addDefineGroup.on("click", function (event) { - i2b2.CRC.ctrlr.QT.temporalGroup = i2b2.CRC.model.queryCurrent.panels.length; - //i2b2.CRC.ctrlr.QT.temporalGroup = i2b2.CRC.ctrlr.QT.temporalGroup + 1; - defineTemporalButton.getMenu().addItems([ - { text: "Event " + (i2b2.CRC.ctrlr.QT.temporalGroup), value: i2b2.CRC.ctrlr.QT.temporalGroup}]); - defineTemporalButton.getMenu().render(); - - i2b2.CRC.model.queryCurrent.panels[i2b2.CRC.ctrlr.QT.temporalGroup] = {}; - this.yuiTree = new YAHOO.widget.TreeView("QPD1"); - i2b2.CRC.ctrlr.QT.panelAdd(this.yuiTree); - i2b2.CRC.ctrlr.QT._redrawAllPanels(); - - //Add to define a query - var select = document.getElementById("instancevent1[0]"); - select.options[select.options.length] = new Option( 'Event '+i2b2.CRC.ctrlr.QT.temporalGroup, i2b2.CRC.ctrlr.QT.temporalGroup); - - select = document.getElementById("instancevent2[0]"); - select.options[select.options.length] = new Option( 'Event '+i2b2.CRC.ctrlr.QT.temporalGroup, i2b2.CRC.ctrlr.QT.temporalGroup); - + i2b2.CRC.view.QT.addNewTemporalGroup(); }); - var removeDefineGroup = new YAHOO.widget.Button("removeDefineGroup"); - removeDefineGroup.on("click", function (event) { - i2b2.CRC.view.QT.deleteLastTemporalGroup(); - }); + var removeDefineGroup = new YAHOO.widget.Button("removeDefineGroup"); + removeDefineGroup.on("click", function (event) { + i2b2.CRC.view.QT.deleteLastTemporalGroup(); + }); queryTimingButton.on("mousedown", function (event) { - //i2b2.CRC.ctrlr.QT.panelControllers[0].doTiming(p_oItem.value); - if ((i2b2.CRC.ctrlr.QT.hasModifier) && (queryTimingButton.getMenu().getItems().length == 3)) { + + if ((i2b2.CRC.ctrlr.QT.hasModifier) && (queryTimingButton.getMenu().getItems().length == 3)) { queryTimingButton.getMenu().addItems([ { text: "Items Instance will be the same", value: "SAMEINSTANCENUM" }]); queryTimingButton.getMenu().render(); } }); defineTemporalButton.on("selectedMenuItemChange", function (event) { //i2b2.CRC.ctrlr.QT.panelControllers[0].doTiming(p_oItem.value); var oMenuItem = event.newValue; var sText = oMenuItem.value; - defineTemporalButton.set("label",oMenuItem.cfg.getProperty("text")); + defineTemporalButton.set("label", oMenuItem.cfg.getProperty("text")); - if (sText != "BUILDER") - { + if (sText != "BUILDER") { $('crc.temoralBuilder').hide(); $('crc.innerQueryPanel').show(); i2b2.CRC.ctrlr.QT.temporalGroup = sText; i2b2.CRC.ctrlr.QT._redrawAllPanels(); - if (sText == "0") - { + if (sText == "0") { $('QPD1').style.background = '#FFFFFF'; $('queryPanelTitle1').innerHTML = 'Group 1'; } else { $('QPD1').style.background = '#D9ECF0'; $('queryPanelTitle1').innerHTML = 'Anchoring Observation'; i2b2.CRC.ctrlr.QT.panelControllers[0].doTiming("SAMEINSTANCENUM"); i2b2.CRC.ctrlr.QT.panelControllers[0].refTiming.set("label", "Items Instance will be the same"); } } else { $('crc.innerQueryPanel').hide(); $('crc.temoralBuilder').show(); // queryTimingButton.set("label", "Temporal Contraint Builder"); } }); queryTimingButton.on("selectedMenuItemChange", function (event) { //i2b2.CRC.ctrlr.QT.panelControllers[0].doTiming(p_oItem.value); var oMenuItem = event.newValue; - if (oMenuItem == 0) - { + if (oMenuItem == 0) { var sValue = "ANY"; var sText = "Treat all groups independently"; - } else if (oMenuItem == 1) - { + } else if (oMenuItem == 1) { var sValue = "SAME"; var sText = "Selected groups occur in the same financial encounter"; } else { var sValue = oMenuItem.value; var sText = oMenuItem.cfg.getProperty("text"); } //var sText = oMenuItem.cfg.getProperty("text"); var length = i2b2.CRC.ctrlr.QT.panelControllers.length; queryTimingButton.set("label", sText); if (sValue != "TEMPORAL") { $('QPD1').style.background = '#FFFFFF'; $('defineTemporalBar').hide(); $('crc.temoralBuilder').hide(); $('crc.innerQueryPanel').show(); } if (sValue == "SAMEVISIT") { i2b2.CRC.ctrlr.QT.queryTiming = "SAMEVISIT"; - for (var i=0; i" + oMenuItem.cfg.getProperty("text") + "")); if (event.newvalue != event.prevValue) { var panelNumber = this.toString(); - panelNumber = panelNumber.substring( panelNumber.length-1, panelNumber.length-0); - i2b2.CRC.ctrlr.QT.panelControllers[panelNumber-1].doTiming(oMenuItem.value); + panelNumber = panelNumber.substring(panelNumber.length - 1, panelNumber.length - 0); + i2b2.CRC.ctrlr.QT.panelControllers[panelNumber - 1].doTiming(oMenuItem.value); } - if (oMenuItem.value.substring(0,5) == "OCCUR") { + if (oMenuItem.value.substring(0, 5) == "OCCUR") { this.setStyle('width', 130); $("qryButtonLimitB1").show(); //$('qryPanelTiming Button').style.width = 120; } else { this.setStyle('width', 160); $("qryButtonLimitB1").hide(); //$('qryPanelTiming Button').style.width = 160; //$(this._button.id).clientWidth = 160; } }; //var panelControl = t.panelControllers[i]; t.panelControllers[i].ctrlIndex = i; - t.panelControllers[i].refTitle = $("queryPanelTitle"+(i+1)); - t.panelControllers[i].refButtonExclude = $("queryPanelExcludeB"+(i+1)); - t.panelControllers[i].refButtonDates = $("queryPanelDatesB"+(i+1)); - t.panelControllers[i].refButtonOccurs = $("queryPanelOccursB"+(i+1)); - t.panelControllers[i].refButtonOccursNum = $("QP"+(i+1)+"Occurs"); - t.panelControllers[i].refBalloon = $("queryBalloon"+(i+1)); - t.panelControllers[i].refDispContents = $("QPD"+(i+1)); + t.panelControllers[i].refTitle = $("queryPanelTitle" + (i + 1)); + t.panelControllers[i].refButtonExclude = $("queryPanelExcludeB" + (i + 1)); + t.panelControllers[i].refButtonDates = $("queryPanelDatesB" + (i + 1)); + t.panelControllers[i].refButtonOccurs = $("queryPanelOccursB" + (i + 1)); + t.panelControllers[i].refButtonOccursNum = $("QP" + (i + 1) + "Occurs"); + t.panelControllers[i].refBalloon = $("queryBalloon" + (i + 1)); + t.panelControllers[i].refDispContents = $("QPD" + (i + 1)); //t.panelControllers[i].refTiming = $("queryPanelTimingB"+(i+1)); //t.panelControllers[i].refTiming = $("queryPanelTimingB"+(i+1)); - var qryButtonTiming = new YAHOO.widget.Button("queryPanelTimingB"+(i+1), - { type: "menu", menu: "menubutton1select", name:"querytiming" }); + var qryButtonTiming = new YAHOO.widget.Button("queryPanelTimingB" + (i + 1), + { type: "menu", menu: "menubutton1select", name: "querytiming" }); //qryButtonTiming.set('disabled', true); qryButtonTiming.on("selectedMenuItemChange", onSelectedMenuItemChange); qryButtonTiming.setStyle('width', 160); t.panelControllers[i].refTiming = qryButtonTiming; t.panelControllers[i].refTiming.set('disabled', true); // create a instance of YUI Treeview if (!t.panelControllers[i].yuiTree) { - t.panelControllers[i].yuiTree = new YAHOO.widget.TreeView("QPD"+(i+1)); - t.panelControllers[i].yuiTree.setDynamicLoad(t.panelControllers[i]._loadTreeDataForNode,1); + t.panelControllers[i].yuiTree = new YAHOO.widget.TreeView("QPD" + (i + 1)); + t.panelControllers[i].yuiTree.setDynamicLoad(t.panelControllers[i]._loadTreeDataForNode, 1); // forward reference from DOM Node to tree obj - $("QPD"+(i+1)).tree = t.panelControllers[i].yuiTree; + $("QPD" + (i + 1)).tree = t.panelControllers[i].yuiTree; // linkback on the treeview to allow it to find its PanelController t.panelControllers[i].refDispContents.linkbackPanelController = t.panelControllers[i]; } } // display the panels t.doScrollFirst(); t._redrawPanelCount(); i2b2.CRC.ctrlr.QT.doShowFrom(0); i2b2.CRC.ctrlr.history.Refresh(); //======================= ======================= function qryPanelTimingClick(p_sType, p_aArgs) { var oEvent = p_aArgs[0], // DOM event oMenuItem = p_aArgs[1]; // MenuItem instance that was the // target of the event if (oMenuItem) { YAHOO.log("[MenuItem Properties] text: " + oMenuItem.cfg.getProperty("text") + ", value: " + oMenuItem.value); } - qryButtonTiming.set("label", qryButtonTiming.getMenu().activeItem.srcElement.text ); + qryButtonTiming.set("label", qryButtonTiming.getMenu().activeItem.srcElement.text); // i2b2.CRC.ctrlr.QT.panelControllers[0].doTiming(p_oItem.value); // var sText = p_oItem.cfg.getProperty("text"); // oMenuPanelTiming1.set("label", sText); } // attach the context controller to all panel controllers objects var op = i2b2.CRC.view.QT; // object path i2b2.CRC.view.QT.ContextMenu = new YAHOO.widget.ContextMenu( "divContextMenu-QT", - { lazyload: true, + { + lazyload: true, trigger: [$('QPD1'), $('QPD2'), $('QPD3')], itemdata: [ - { text: "Delete", onclick: { fn: op.ContextMenuRouter, obj: 'delete' } }, - { text: "Lab Values", onclick: { fn: op.ContextMenuRouter, obj: 'labvalues' } } - ] } + { text: "Delete", onclick: { fn: op.ContextMenuRouter, obj: 'delete' } }, + { text: "Lab Values", onclick: { fn: op.ContextMenuRouter, obj: 'labvalues' } } + ] + } ); i2b2.CRC.view.QT.ContextMenu.subscribe("triggerContextMenu", i2b2.CRC.view.QT.ContextMenuPreprocess); i2b2.CRC.view.QT.ContextMenu.subscribe("beforeShow", i2b2.CRC.view.QT.ContextMenuPreprocess); i2b2.CRC.view.QT.splitterDragged(); // initialize query tool's elements i2b2.CRC.view.QT.ResizeHeight(); -// ================================================================================================== // + // ================================================================================================== // } }) ); // QueryTool Helper Balloons // ================================================================================================== // i2b2.CRC.view.QT.hballoon = { canShowQueryBalloons: true, delayQueryBalloons: false, - hideBalloons: function() { + hideBalloons: function () { var thisObj = i2b2.CRC.view.QT.hballoon; thisObj.canShowQueryBalloons = false; clearTimeout(thisObj.delayQueryBalloons); $('queryBalloonBox').hide(); YAHOO.util.Event.removeListener(document, "mousemove", thisObj.showBalloons); YAHOO.util.Event.addListener(document, "mousemove", thisObj.showBalloons); }, - showBalloons: function(e) { + showBalloons: function (e) { var thisObj = i2b2.CRC.view.QT.hballoon; var x = YAHOO.util.Event.getPageX(e); var y = YAHOO.util.Event.getPageY(e); var elX = parseInt($('crcQueryToolBox').style.left); - if (isNaN(elX)) {elX = 241;} + if (isNaN(elX)) { elX = 241; } var elY = $('crcQueryToolBox').getHeight(); - if (isNaN(elY)) {elY = 280;} + if (isNaN(elY)) { elY = 280; } elY = elY + 76 - 135; - if ( (x < elX-5) || (x > elX+524+5) || (y < elY-15) || (y > elY+110) ) { + if ((x < elX - 5) || (x > elX + 524 + 5) || (y < elY - 15) || (y > elY + 110)) { if (!thisObj.canShowQueryBalloons) { thisObj.canShowQueryBalloons = true; - thisObj.delayQueryBalloons = setTimeout("i2b2.CRC.view.QT.hballoon._showQueryBalloons()",200); + thisObj.delayQueryBalloons = setTimeout("i2b2.CRC.view.QT.hballoon._showQueryBalloons()", 200); } } else { thisObj.canShowQueryBalloons = false; clearTimeout(thisObj.delayQueryBalloons); } }, - _showQueryBalloons: function() { + _showQueryBalloons: function () { var thisObj = i2b2.CRC.view.QT.hballoon; if (thisObj.canShowQueryBalloons) { $('queryBalloonBox').show(); YAHOO.util.Event.removeListener(document, "mousemove", thisObj.showBalloons); } } }; //================================================================================================== // -i2b2.events.initView.subscribe((function(eventTypeName, newMode) { -// ------------------------------------------------------- +i2b2.events.initView.subscribe((function (eventTypeName, newMode) { + // ------------------------------------------------------- this.visible = true; $('crcQueryToolBox').show(); this.Resize(); // initialize the dropdown menu for query timing - var temporalConstraintBar = $("temporalConstraintBar"); + var temporalConstraintBar = $("temporalConstraintBar"); var temporalConstraintLabel = $("temporalConstraintLabel"); - var queryTimingButton = $("queryTiming-button"); - temporalConstraintDiv.style.width = Math.max( parseInt(temporalConstraintBar.style.width) - parseInt(temporalConstraintLabel.style.width)-2, 0) + "px"; - queryTimingButton.style.width = Math.max( parseInt(temporalConstraintBar.style.width) - parseInt(temporalConstraintLabel.style.width)-6, 0) + "px"; + var queryTimingButton = $("queryTiming-button"); + temporalConstraintDiv.style.width = Math.max(parseInt(temporalConstraintBar.style.width) - parseInt(temporalConstraintLabel.style.width) - 2, 0) + "px"; + queryTimingButton.style.width = Math.max(parseInt(temporalConstraintBar.style.width) - parseInt(temporalConstraintLabel.style.width) - 6, 0) + "px"; // ------------------------------------------------------- -}),'',i2b2.CRC.view.QT); +}), '', i2b2.CRC.view.QT); // ================================================================================================== // -i2b2.events.changedViewMode.subscribe((function(eventTypeName, newMode) { -// ------------------------------------------------------- +i2b2.events.changedViewMode.subscribe((function (eventTypeName, newMode) { + // ------------------------------------------------------- newMode = newMode[0]; this.viewMode = newMode; - switch(newMode) { + switch (newMode) { case "Patients": this.visible = true; $('crcQueryToolBox').show(); i2b2.CRC.view.QT.splitterDragged(); //this.Resize(); break; default: this.visible = false; $('crcQueryToolBox').hide(); break; } -// ------------------------------------------------------- -}),'', i2b2.CRC.view.QT); + // ------------------------------------------------------- +}), '', i2b2.CRC.view.QT); // ================================================================================================== // -i2b2.events.changedZoomWindows.subscribe((function(eventTypeName, zoomMsg) { +i2b2.events.changedZoomWindows.subscribe((function (eventTypeName, zoomMsg) { newMode = zoomMsg[0]; if (!newMode.action) { return; } if (newMode.action == "ADD") { switch (newMode.window) { case "QT": this.isZoomed = true; this.visible = true; break; } } else { switch (newMode.window) { case "QT": this.isZoomed = false; this.visible = true; } } this.ResizeHeight(); -}),'',i2b2.CRC.view.QT); +}), '', i2b2.CRC.view.QT); console.timeEnd('execute time'); console.groupEnd(); \ No newline at end of file