diff --git a/build.sbt b/build.sbt
index 761d3f2..422a6c4 100644
--- a/build.sbt
+++ b/build.sbt
@@ -1,42 +1,77 @@
-enablePlugins(JavaAppPackaging)
-enablePlugins(BuildInfoPlugin)
-enablePlugins(GitVersioning)
+lazy val zioVersion = "2.0.0-RC5"
+lazy val gitCommitString = SettingKey[String]("gitCommit")
-organization := "org.renci"
+lazy val commonSettings = Seq(
+ organization := "org.geneontology",
+ version := "2.2-SNAPSHOT",
+ licenses := Seq("MIT license" -> url("https://opensource.org/licenses/MIT")),
+ scalaVersion := "2.13.8",
+ scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8"),
+ javaOptions += "-Xmx8G"
+)
-name := "relation-graph"
+lazy val publishSettings = Seq(
+ Test / publishArtifact := false,
+ publishMavenStyle := true,
+ publishTo := {
+ val nexus = "https://oss.sonatype.org/"
+ if (isSnapshot.value) Some("snapshots" at nexus + "content/repositories/snapshots")
+ else Some("releases" at nexus + "service/local/staging/deploy/maven2")
+ },
+ pomExtra :=
+ git@github.com:balhoff/relation-graph.git
+ scm:git:git@github.com:balhoff/relation-graph.git
+
+
+
+ balhoff
+ Jim Balhoff
+ balhoff@renci.org
+
+
+)
-version := "2.1.0"
+lazy val parentProject = project
+ .in(file("."))
+ .settings(commonSettings)
+ .settings(name := "relation-graph-project", publish / skip := true)
+ .aggregate(core, cli)
-licenses := Seq("MIT license" -> url("https://opensource.org/licenses/MIT"))
-
-scalaVersion := "2.13.8"
-
-scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8")
-
-javaOptions += "-Xmx8G"
-
-testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework")
-
-val gitCommitString = SettingKey[String]("gitCommit")
-
-gitCommitString := git.gitHeadCommit.value.getOrElse("Not Set")
-
-buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion, gitCommitString)
-
-buildInfoPackage := "org.renci.relationgraph"
-
-val zioVersion = "2.0.0-RC3"
+lazy val core = project
+ .in(file("core"))
+ .settings(commonSettings)
+ .settings(
+ name := "relation-graph",
+ description := "relation-graph core",
+ testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework"),
+ libraryDependencies ++= Seq(
+ "dev.zio" %% "zio" % zioVersion,
+ "dev.zio" %% "zio-streams" % zioVersion,
+ "org.geneontology" %% "whelk-owlapi" % "1.1.1",
+ "org.apache.jena" % "apache-jena-libs" % "4.4.0" exclude("org.slf4j", "slf4j-log4j12"),
+ "com.typesafe.scala-logging" %% "scala-logging" % "3.9.4",
+ "dev.zio" %% "zio-test" % zioVersion % Test,
+ "dev.zio" %% "zio-test-sbt" % zioVersion % Test
+ )
+ )
+ .settings(publishSettings)
-libraryDependencies ++= {
- Seq(
- "dev.zio" %% "zio" % zioVersion,
- "dev.zio" %% "zio-streams" % zioVersion,
- "org.geneontology" %% "whelk-owlapi" % "1.1.1",
- "com.outr" %% "scribe-slf4j" % "3.8.2",
- "com.github.alexarchambault" %% "case-app" % "2.0.6",
- "org.apache.jena" % "apache-jena-libs" % "4.4.0" exclude ("org.slf4j", "slf4j-log4j12"),
- "dev.zio" %% "zio-test" % zioVersion % Test,
- "dev.zio" %% "zio-test-sbt" % zioVersion % Test
+lazy val cli = project
+ .in(file("cli"))
+ .enablePlugins(JavaAppPackaging)
+ .enablePlugins(BuildInfoPlugin)
+ .enablePlugins(GitVersioning)
+ .settings(commonSettings)
+ .dependsOn(core)
+ .settings(
+ name := "relation-graph-cli",
+ executableScriptName := "relation-graph",
+ publish / skip := true,
+ libraryDependencies ++= Seq(
+ "com.outr" %% "scribe-slf4j" % "3.8.2",
+ "com.github.alexarchambault" %% "case-app" % "2.0.6"
+ ),
+ gitCommitString := git.gitHeadCommit.value.getOrElse("Not Set"),
+ buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion, gitCommitString),
+ buildInfoPackage := "org.renci.relationgraph"
)
-}
diff --git a/src/main/scala/org/renci/relationgraph/Config.scala b/cli/src/main/scala/org/renci/relationgraph/Config.scala
similarity index 80%
rename from src/main/scala/org/renci/relationgraph/Config.scala
rename to cli/src/main/scala/org/renci/relationgraph/Config.scala
index 9efc80f..10af006 100644
--- a/src/main/scala/org/renci/relationgraph/Config.scala
+++ b/cli/src/main/scala/org/renci/relationgraph/Config.scala
@@ -4,6 +4,7 @@ import caseapp._
import caseapp.core.Error.MalformedValue
import caseapp.core.argparser.{ArgParser, SimpleArgParser}
import org.renci.relationgraph.Config.{BoolValue, FalseValue, TrueValue}
+import org.renci.relationgraph.RelationGraph.Config.{OWLMode, OutputMode, RDFMode}
@AppName("relation-graph")
@ProgName("relation-graph")
@@ -16,7 +17,7 @@ final case class Config(
outputFile: String,
@HelpMessage("Configure style of triples to be output. RDF mode is the default; each existential relation is collapsed to a single direct triple.")
@ValueDescription("RDF|OWL")
- mode: Config.OutputMode = Config.RDFMode,
+ mode: OutputMode = RDFMode,
@HelpMessage("Property to restrict output relations to. Provide option multiple times for multiple properties.")
@ValueDescription("IRI")
property: List[String] = Nil,
@@ -43,28 +44,32 @@ final case class Config(
disableOwlNothing: BoolValue = FalseValue,
@HelpMessage("Set log level to INFO")
@ValueDescription("bool")
- verbose: Boolean = false)
+ verbose: Boolean = false) {
+
+ def toRelationGraphConfig: RelationGraph.Config =
+ RelationGraph.Config(
+ mode = this.mode,
+ outputSubclasses = this.outputSubclasses.bool,
+ reflexiveSubclasses = this.reflexiveSubclasses.bool,
+ equivalenceAsSubclass = this.equivalenceAsSubclass.bool,
+ outputClasses = this.outputClasses.bool,
+ outputIndividuals = this.outputIndividuals.bool,
+ disableOwlNothing = this.disableOwlNothing.bool,
+ )
-object Config {
-
- sealed trait OutputMode
-
- case object RDFMode extends OutputMode
-
- case object OWLMode extends OutputMode
+}
- object OutputMode {
+object Config {
- implicit val argParser: ArgParser[OutputMode] = SimpleArgParser.from[OutputMode]("output mode") { arg =>
- arg.toLowerCase match {
- case "rdf" => Right(RDFMode)
- case "owl" => Right(OWLMode)
- case _ => Left(MalformedValue("output mode", arg))
- }
+ implicit val rdfModeParser: ArgParser[OutputMode] = SimpleArgParser.from[OutputMode]("output mode") { arg =>
+ arg.toLowerCase match {
+ case "rdf" => Right(RDFMode)
+ case "owl" => Right(OWLMode)
+ case _ => Left(MalformedValue("output mode", arg))
}
-
}
+
/**
* This works around some confusing behavior in case-app boolean parsing
*/
diff --git a/cli/src/main/scala/org/renci/relationgraph/Main.scala b/cli/src/main/scala/org/renci/relationgraph/Main.scala
new file mode 100644
index 0000000..9d4f3a9
--- /dev/null
+++ b/cli/src/main/scala/org/renci/relationgraph/Main.scala
@@ -0,0 +1,70 @@
+package org.renci.relationgraph
+
+import caseapp._
+import org.apache.jena.riot.RDFFormat
+import org.apache.jena.riot.system.{StreamRDF, StreamRDFWriter}
+import org.geneontology.whelk._
+import org.renci.relationgraph.RelationGraph.TriplesGroup
+import org.semanticweb.owlapi.apibinding.OWLManager
+import org.semanticweb.owlapi.model._
+import scribe.Level
+import scribe.filter.{packageName, select}
+import zio._
+import Config._
+
+import java.io.{File, FileOutputStream}
+import scala.io.Source
+
+object Main extends ZCaseApp[Config] {
+
+ override def run(config: Config, arg: RemainingArgs): ZIO[Environment, Nothing, ExitCode] = {
+ val configureLogging = ZIO.succeed {
+ scribe.Logger.root
+ .clearHandlers()
+ .clearModifiers()
+ .withModifier(select(packageName("org.renci.relationgraph")).boosted(Level.Info, Level.Warn))
+ .withHandler(minimumLevel = Some(if (config.verbose) Level.Info else Level.Warn))
+ .replace()
+ }
+ val program = ZIO.scoped {
+ createStreamRDF(config.outputFile).flatMap { rdfWriter =>
+ for {
+ fileProperties <- config.propertiesFile.map(readPropertiesFile).getOrElse(ZIO.succeed(Set.empty[AtomicConcept]))
+ specifiedProperties = fileProperties ++ config.property.map(prop => AtomicConcept(prop)).to(Set)
+ ontology <- loadOntology(config.ontologyFile)
+ _ <- RelationGraph.computeRelations(ontology, specifiedProperties, config.toRelationGraphConfig)
+ .foreach {
+ case TriplesGroup(triples) => ZIO.attempt(triples.foreach(rdfWriter.triple))
+ }
+ _ <- ZIO.succeed(scribe.info("Done computing relations"))
+ } yield ()
+ }
+ }
+ configureLogging *>
+ program.tapError { e =>
+ if (config.verbose) ZIO.succeed(e.printStackTrace())
+ else ZIO.succeed(scribe.error(e.getMessage))
+ }.exitCode
+ }
+
+ def createStreamRDF(path: String): ZIO[Scope, Throwable, StreamRDF] = {
+ ZIO.acquireRelease(ZIO.attempt(new FileOutputStream(new File(path))))(stream => ZIO.succeed(stream.close())).flatMap { outputStream =>
+ ZIO.acquireRelease(ZIO.attempt {
+ val stream = StreamRDFWriter.getWriterStream(outputStream, RDFFormat.TURTLE_FLAT, null)
+ stream.start()
+ stream
+ })(stream => ZIO.succeed(stream.finish()))
+ }
+ }
+
+ def loadOntology(path: String): Task[OWLOntology] = for {
+ manager <- ZIO.attempt(OWLManager.createOWLOntologyManager())
+ ontology <- ZIO.attemptBlocking(manager.loadOntologyFromOntologyDocument(new File(path)))
+ } yield ontology
+
+ def readPropertiesFile(file: String): ZIO[Any, Throwable, Set[AtomicConcept]] =
+ ZIO.attemptBlocking(Source.fromFile(file, "utf-8")).acquireReleaseWithAuto { source =>
+ ZIO.attemptBlocking(source.getLines().map(_.trim).filter(_.nonEmpty).map(line => AtomicConcept(line)).to(Set))
+ }
+
+}
diff --git a/src/main/scala/org/renci/relationgraph/ZCaseApp.scala b/cli/src/main/scala/org/renci/relationgraph/ZCaseApp.scala
similarity index 62%
rename from src/main/scala/org/renci/relationgraph/ZCaseApp.scala
rename to cli/src/main/scala/org/renci/relationgraph/ZCaseApp.scala
index 322c146..34125dd 100644
--- a/src/main/scala/org/renci/relationgraph/ZCaseApp.scala
+++ b/cli/src/main/scala/org/renci/relationgraph/ZCaseApp.scala
@@ -13,7 +13,7 @@ import java.io.IOException
/**
* Adapted from caseapp.cats.IOCaseApp
*/
-abstract class ZCaseApp[T](implicit val parser0: Parser[T], val messages: Help[T]) extends App {
+abstract class ZCaseApp[T](implicit val parser0: Parser[T], val messages: Help[T]) extends ZIOAppDefault {
private[this] def parser: Parser[T] = {
val p = parser0.nameFormatter(nameFormatter)
@@ -23,15 +23,15 @@ abstract class ZCaseApp[T](implicit val parser0: Parser[T], val messages: Help[T
p
}
- def run(options: T, remainingArgs: RemainingArgs): ZIO[ZEnv, Nothing, ExitCode]
+ def run(options: T, remainingArgs: RemainingArgs): ZIO[Environment, Nothing, ExitCode]
- private[this] def error(message: Error): ZIO[Console, IOException, ExitCode] =
+ private[this] def error(message: Error): ZIO[Any, IOException, ExitCode] =
printLine(message.message).as(ExitCode.failure)
- private[this] def helpAsked: ZIO[Console, IOException, ExitCode] =
+ private[this] def helpAsked: ZIO[Any, IOException, ExitCode] =
printLine(messages.withHelp.help).as(ExitCode.success)
- private[this] def usageAsked: ZIO[Console, IOException, ExitCode] =
+ private[this] def usageAsked: ZIO[Any, IOException, ExitCode] =
printLine(messages.withHelp.usage).as(ExitCode.success)
/**
@@ -64,14 +64,17 @@ abstract class ZCaseApp[T](implicit val parser0: Parser[T], val messages: Help[T
private[this] def nameFormatter: Formatter[Name] = Formatter.DefaultNameFormatter
- override def run(args: List[String]): ZIO[ZEnv, Nothing, ExitCode] = {
- if (args == List("--version")) ZIO.succeed(println(org.renci.relationgraph.BuildInfo.toString)).exitCode
- else parser.withHelp.detailedParse(expandArgs(args), stopAtFirstUnrecognized) match {
- case Left(err) => error(err).orDie
- case Right((WithHelp(_, true, _), _)) => helpAsked.orDie
- case Right((WithHelp(true, _, _), _)) => usageAsked.orDie
- case Right((WithHelp(_, _, Left(err)), _)) => error(err).orDie
- case Right((WithHelp(_, _, Right(t)), remainingArgs)) => run(t, remainingArgs)
+ override def run: ZIO[Environment with ZIOAppArgs with Scope, Any, Any] = {
+ ZIO.service[ZIOAppArgs].flatMap { appArgs =>
+ val args = appArgs.getArgs.toList
+ if (args == List("--version")) ZIO.succeed(println(org.renci.relationgraph.BuildInfo.toString)).exitCode
+ else parser.withHelp.detailedParse(expandArgs(args), stopAtFirstUnrecognized) match {
+ case Left(err) => error(err).orDie
+ case Right((WithHelp(_, true, _), _)) => helpAsked.orDie
+ case Right((WithHelp(true, _, _), _)) => usageAsked.orDie
+ case Right((WithHelp(_, _, Left(err)), _)) => error(err).orDie
+ case Right((WithHelp(_, _, Right(t)), remainingArgs)) => run(t, remainingArgs)
+ }
}
}
diff --git a/src/main/scala/org/renci/relationgraph/Main.scala b/core/src/main/scala/org/renci/relationgraph/RelationGraph.scala
similarity index 73%
rename from src/main/scala/org/renci/relationgraph/Main.scala
rename to core/src/main/scala/org/renci/relationgraph/RelationGraph.scala
index f33d227..a6740a2 100644
--- a/src/main/scala/org/renci/relationgraph/Main.scala
+++ b/core/src/main/scala/org/renci/relationgraph/RelationGraph.scala
@@ -1,34 +1,25 @@
package org.renci.relationgraph
-import caseapp._
+import com.typesafe.scalalogging.StrictLogging
import org.apache.jena.graph.{Node, NodeFactory, Triple}
-import org.apache.jena.riot.RDFFormat
-import org.apache.jena.riot.system.{StreamRDF, StreamRDFWriter}
import org.apache.jena.sys.JenaSystem
import org.apache.jena.vocabulary.{OWL2, RDF, RDFS}
import org.geneontology.whelk.BuiltIn.{Bottom, Top}
import org.geneontology.whelk._
-import org.renci.relationgraph.Config.{OWLMode, RDFMode}
+import org.renci.relationgraph.RelationGraph.Config.{OWLMode, OutputMode, RDFMode}
import org.semanticweb.owlapi.apibinding.OWLFunctionalSyntaxFactory.{OWLNothing, OWLThing}
-import org.semanticweb.owlapi.apibinding.OWLManager
-import org.semanticweb.owlapi.model._
import org.semanticweb.owlapi.model.parameters.Imports
-import scribe.Level
-import scribe.filter.{packageName, select}
-import zio.ZIO.attemptBlockingIO
+import org.semanticweb.owlapi.model._
import zio._
import zio.stream._
-import java.io.{File, FileOutputStream}
import java.lang.{Runtime => JRuntime}
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.util.Base64
-import java.util.concurrent.TimeUnit
-import scala.io.Source
import scala.jdk.CollectionConverters._
-object Main extends ZCaseApp[Config] {
+object RelationGraph extends StrictLogging {
JenaSystem.init()
@@ -40,86 +31,51 @@ object Main extends ZCaseApp[Config] {
private val OWLSomeValuesFrom = OWL2.someValuesFrom.asNode
private val OWLOntology = OWL2.Ontology.asNode
- override def run(config: Config, arg: RemainingArgs): ZIO[ZEnv, Nothing, ExitCode] = {
- val configureLogging = ZIO.succeed {
- scribe.Logger.root
- .clearHandlers()
- .clearModifiers()
- .withModifier(select(packageName("org.renci.relationgraph")).boosted(Level.Info, Level.Warn))
- .withHandler(minimumLevel = Some(if (config.verbose) Level.Info else Level.Warn))
- .replace()
- }
+ final case class Config(
+ mode: OutputMode = RDFMode,
+ outputSubclasses: Boolean = false,
+ reflexiveSubclasses: Boolean = true,
+ equivalenceAsSubclass: Boolean = true,
+ outputClasses: Boolean = true,
+ outputIndividuals: Boolean = false,
+ disableOwlNothing: Boolean = false,
+ )
+
+ object Config {
+
+ sealed trait OutputMode
+
+ case object RDFMode extends OutputMode
+
+ case object OWLMode extends OutputMode
- val program = ZIO.scoped {
- createStreamRDF(config.outputFile).flatMap { rdfWriter =>
- for {
- fileProperties <- config.propertiesFile.map(readPropertiesFile).getOrElse(ZIO.succeed(Set.empty[AtomicConcept]))
- specifiedProperties = fileProperties ++ config.property.map(prop => AtomicConcept(prop)).to(Set)
- ontology <- loadOntology(config.ontologyFile)
- whelkOntology = Bridge.ontologyToAxioms(ontology)
- _ <- ZIO.succeed(scribe.info("Running reasoner"))
- whelk = Reasoner.assert(whelkOntology, disableBottom = config.disableOwlNothing.bool)
- indexedWhelk = IndexedReasonerState(whelk)
- _ <- ZIO.succeed(scribe.info("Done running reasoner"))
- _ <- ZIO.fail(new Exception("Ontology is incoherent; please correct unsatisfiable classes.")).when(isIncoherent(whelk))
- _ <- attemptBlockingIO(rdfWriter.triple(Triple.create(NodeFactory.createBlankNode("redundant"), RDFType, OWLOntology)))
- .when(config.mode == OWLMode)
- start <- Clock.currentTime(TimeUnit.MILLISECONDS)
- triplesStream = computeRelations(ontology, indexedWhelk, specifiedProperties, config.outputSubclasses.bool, config.reflexiveSubclasses.bool, config.equivalenceAsSubclass.bool, config.outputClasses.bool, config.outputIndividuals.bool, config.mode)
- _ <- triplesStream.foreach {
- case TriplesGroup(triples) => ZIO.attempt(triples.foreach(rdfWriter.triple))
- }
- stop <- Clock.currentTime(TimeUnit.MILLISECONDS)
- _ <- ZIO.succeed(scribe.info(s"Computed relations in ${(stop - start) / 1000.0}s"))
- } yield ()
- }
- }
- configureLogging *>
- program.as(ExitCode.success)
- .catchAll { e =>
- if (config.verbose) ZIO.succeed(e.printStackTrace()).as(ExitCode.failure)
- else ZIO.succeed(scribe.error(e.getMessage)).as(ExitCode.failure)
- }
}
- def computeRelations(ontology: OWLOntology, whelk: IndexedReasonerState, specifiedProperties: Set[AtomicConcept], outputSubclasses: Boolean, reflexiveSubclasses: Boolean, equivalenceAsSubclass: Boolean, outputClasses: Boolean, outputIndividuals: Boolean, mode: Config.OutputMode): UStream[TriplesGroup] = {
- val classes = classHierarchy(whelk.state)
+ def computeRelations(ontology: OWLOntology, specifiedProperties: Set[AtomicConcept], outputConfig: Config): UStream[TriplesGroup] = {
+ val whelkOntology = Bridge.ontologyToAxioms(ontology)
+ logger.info("Running reasoner")
+ val whelk = Reasoner.assert(whelkOntology, disableBottom = outputConfig.disableOwlNothing)
+ val indexedWhelk = IndexedReasonerState(whelk)
+ logger.info("Done running reasoner")
+ val classes = classHierarchy(indexedWhelk.state)
val properties = propertyHierarchy(ontology)
val allProperties = properties.subclasses.keySet.map(c => Role(c.id))
- val classesTasks = if (outputSubclasses) {
- allClasses(ontology).map(c => ZIO.succeed(processSubclasses(c, whelk.state, reflexiveSubclasses, equivalenceAsSubclass, outputClasses, outputIndividuals)))
+ val ontologyDeclarationStream = ZStream.succeed(ZIO.succeed(TriplesGroup(Set(Triple.create(NodeFactory.createBlankNode("redundant"), RDFType, OWLOntology)))))
+ .when(outputConfig.mode == OWLMode)
+ val classesTasks = if (outputConfig.outputSubclasses) {
+ allClasses(ontology).map(c => ZIO.succeed(processSubclasses(c, indexedWhelk.state, outputConfig.reflexiveSubclasses, outputConfig.equivalenceAsSubclass, outputConfig.outputClasses, outputConfig.outputIndividuals)))
} else Stream.empty
val streamZ = for {
queue <- Queue.unbounded[Restriction]
activeRestrictions <- Ref.make(0)
seenRefs <- ZIO.foreach(allProperties)(p => Ref.make(Set.empty[AtomicConcept]).map(p -> _)).map(_.toMap)
_ <- traverse(specifiedProperties, properties, classes, queue, activeRestrictions, seenRefs)
- restrictionsStream = Stream.fromQueue(queue).map(r => processRestrictionAndExtendQueue(r, properties, classes, whelk, mode, specifiedProperties.isEmpty, outputClasses, outputIndividuals, queue, activeRestrictions, seenRefs))
- allTasks = classesTasks ++ restrictionsStream
+ restrictionsStream = Stream.fromQueue(queue).map(r => processRestrictionAndExtendQueue(r, properties, classes, indexedWhelk, outputConfig.mode, specifiedProperties.isEmpty, outputConfig.outputClasses, outputConfig.outputIndividuals, queue, activeRestrictions, seenRefs))
+ allTasks = ontologyDeclarationStream ++ classesTasks ++ restrictionsStream
} yield allTasks.mapZIOParUnordered(JRuntime.getRuntime.availableProcessors)(identity)
Stream.unwrap(streamZ)
}
- def readPropertiesFile(file: String): ZIO[Any, Throwable, Set[AtomicConcept]] =
- ZIO.attemptBlocking(Source.fromFile(file, "utf-8")).acquireReleaseWithAuto { source =>
- ZIO.attemptBlocking(source.getLines().map(_.trim).filter(_.nonEmpty).map(line => AtomicConcept(line)).to(Set))
- }
-
- def loadOntology(path: String): Task[OWLOntology] = for {
- manager <- ZIO.attempt(OWLManager.createOWLOntologyManager())
- ontology <- ZIO.attemptBlocking(manager.loadOntologyFromOntologyDocument(new File(path)))
- } yield ontology
-
- def createStreamRDF(path: String): ZIO[Scope, Throwable, StreamRDF] = {
- ZIO.acquireRelease(ZIO.attempt(new FileOutputStream(new File(path))))(stream => ZIO.succeed(stream.close())).flatMap { outputStream =>
- ZIO.acquireRelease(ZIO.attempt {
- val stream = StreamRDFWriter.getWriterStream(outputStream, RDFFormat.TURTLE_FLAT, null)
- stream.start()
- stream
- })(stream => ZIO.succeed(stream.finish()))
- }
- }
-
def allClasses(ont: OWLOntology): ZStream[Any, Nothing, OWLClass] = Stream.fromIterable(ont.getClassesInSignature(Imports.INCLUDED).asScala.to(Set) - OWLThing - OWLNothing)
def traverse(specifiedProperties: Set[AtomicConcept], properties: Hierarchy, classes: Hierarchy, queue: Queue[Restriction], activeRestrictions: Ref[Int], seenRefs: Map[Role, Ref[Set[AtomicConcept]]]): UIO[Unit] = {
@@ -318,4 +274,5 @@ object Main extends ZCaseApp[Config] {
}
+
}
diff --git a/core/src/main/scala/org/renci/relationgraph/RelationGraphUtil.scala b/core/src/main/scala/org/renci/relationgraph/RelationGraphUtil.scala
new file mode 100644
index 0000000..42515ab
--- /dev/null
+++ b/core/src/main/scala/org/renci/relationgraph/RelationGraphUtil.scala
@@ -0,0 +1,47 @@
+package org.renci.relationgraph
+
+import org.apache.jena.vocabulary.{OWL, RDFS}
+import org.geneontology.whelk.AtomicConcept
+import org.phenoscape.scowl._
+import org.renci.relationgraph.RelationGraph.Config
+import org.renci.relationgraph.RelationGraph.Config.RDFMode
+import org.semanticweb.owlapi.model.{IRI, OWLClassAxiom, OWLOntology}
+import zio._
+
+import java.util
+import scala.jdk.CollectionConverters._
+
+/**
+ * Static methods supporting easier, synchronous, access from Java
+ */
+object RelationGraphUtil {
+
+ private val RDFSSubClassOf = RDFS.subClassOf.getURI
+ private val OWLEquivalentClass = OWL.equivalentClass.getURI
+
+ /**
+ * @param ontology
+ * @param specifiedProperties properties for which to compute relations; an empty collection signifies all
+ * @param outputConfig configuration for RelationGraph; `mode` is ignored, since results are converted to OWL axioms
+ * @return
+ */
+ def computeRelationGraph(ontology: OWLOntology, specifiedProperties: util.Collection[IRI], outputConfig: Config): util.Set[OWLClassAxiom] = {
+ val properties = specifiedProperties.asScala.to(Set).map(iri => AtomicConcept(iri.toString))
+ val owlZ = RelationGraph.computeRelations(ontology, properties, outputConfig.copy(mode = RDFMode))
+ .map { triplesGroup =>
+ val triples = triplesGroup.redundant
+ triples.map { triple =>
+ triple.getPredicate.getURI match {
+ case RDFSSubClassOf => SubClassOf(Class(triple.getSubject.getURI), Class(triple.getObject.getURI))
+ case OWLEquivalentClass => EquivalentClasses(Class(triple.getSubject.getURI), Class(triple.getObject.getURI))
+ case property => SubClassOf(Class(triple.getSubject.getURI), ObjectSomeValuesFrom(ObjectProperty(property), Class(triple.getObject.getURI)))
+ }
+ }
+ }
+ .runCollect
+ .map(_.toSet.flatten[OWLClassAxiom])
+ .map(_.asJava)
+ Runtime.default.unsafeRun(owlZ)
+ }
+
+}
diff --git a/src/test/resources/org/renci/relationgraph/apo.owl b/core/src/test/resources/org/renci/relationgraph/apo.owl
similarity index 100%
rename from src/test/resources/org/renci/relationgraph/apo.owl
rename to core/src/test/resources/org/renci/relationgraph/apo.owl
diff --git a/src/test/resources/org/renci/relationgraph/materialize_test.ofn b/core/src/test/resources/org/renci/relationgraph/materialize_test.ofn
similarity index 100%
rename from src/test/resources/org/renci/relationgraph/materialize_test.ofn
rename to core/src/test/resources/org/renci/relationgraph/materialize_test.ofn
diff --git a/src/test/scala/org/renci/relationgraph/TestRelationGraph.scala b/core/src/test/scala/org/renci/relationgraph/TestRelationGraph.scala
similarity index 73%
rename from src/test/scala/org/renci/relationgraph/TestRelationGraph.scala
rename to core/src/test/scala/org/renci/relationgraph/TestRelationGraph.scala
index 59f4dcc..4e860dd 100644
--- a/src/test/scala/org/renci/relationgraph/TestRelationGraph.scala
+++ b/core/src/test/scala/org/renci/relationgraph/TestRelationGraph.scala
@@ -1,30 +1,38 @@
package org.renci.relationgraph
import org.apache.jena.graph.{Node, NodeFactory, Triple}
-import org.geneontology.whelk.{Bridge, Reasoner}
-import org.renci.relationgraph.Main.{IndexedReasonerState, TriplesGroup}
+import org.renci.relationgraph.RelationGraph.Config.RDFMode
+import org.renci.relationgraph.RelationGraph.{Config, TriplesGroup}
import org.semanticweb.owlapi.apibinding.OWLManager
import zio._
import zio.test.TestAspect.timeout
import zio.test._
-object TestRelationGraph extends DefaultRunnableSpec {
+
+object TestRelationGraph extends ZIOSpecDefault {
private val Prefix = "http://example.org/test"
private val P = NodeFactory.createURI(s"$Prefix#p")
private def n: String => Node = NodeFactory.createURI
+ private val testConfig = Config(
+ mode = RDFMode,
+ outputSubclasses = true,
+ reflexiveSubclasses = false,
+ equivalenceAsSubclass = false,
+ outputClasses = true,
+ outputIndividuals = false,
+ disableOwlNothing = false
+ )
+
def spec =
suite("RelationGraphSpec")(
test("testMaterializedRelations") {
for {
manager <- ZIO.attempt(OWLManager.createOWLOntologyManager())
ontology <- ZIO.attempt(manager.loadOntologyFromOntologyDocument(this.getClass.getResourceAsStream("materialize_test.ofn")))
- whelkOntology = Bridge.ontologyToAxioms(ontology)
- whelk = Reasoner.assert(whelkOntology)
- indexedWhelk = IndexedReasonerState(whelk)
- resultsStream = Main.computeRelations(ontology, indexedWhelk, Set.empty, true, false, false, true, false, Config.RDFMode)
+ resultsStream = RelationGraph.computeRelations(ontology, Set.empty, testConfig)
results <- resultsStream.runCollect
triples <- ZIO.from(results.reduceOption((left, right) => TriplesGroup(left.redundant ++ right.redundant)))
TriplesGroup(redundant) = triples
@@ -40,10 +48,7 @@ object TestRelationGraph extends DefaultRunnableSpec {
for {
manager <- ZIO.attempt(OWLManager.createOWLOntologyManager())
ontology <- ZIO.attempt(manager.loadOntologyFromOntologyDocument(this.getClass.getResourceAsStream("apo.owl")))
- whelkOntology = Bridge.ontologyToAxioms(ontology)
- whelk = Reasoner.assert(whelkOntology)
- indexedWhelk = IndexedReasonerState(whelk)
- resultsStream = Main.computeRelations(ontology, indexedWhelk, Set.empty, true, false, false, true, false, Config.RDFMode)
+ resultsStream = RelationGraph.computeRelations(ontology, Set.empty, testConfig)
results <- resultsStream.runCollect
triples <- ZIO.from(results.reduceOption((left, right) => TriplesGroup(left.redundant ++ right.redundant)))
TriplesGroup(redundant) = triples
diff --git a/project/plugins.sbt b/project/plugins.sbt
index e51bf28..84a9f4c 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -1,3 +1,4 @@
addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.9.9")
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0")
addSbtPlugin("com.github.sbt" % "sbt-git" % "2.0.0")
+addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2")