Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split project into core and cli modules. Add static method for easy access from Java. #121

Merged
merged 3 commits into from
Apr 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 71 additions & 36 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -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 := <scm>
<url>git@github.com:balhoff/relation-graph.git</url>
<connection>scm:git:git@github.com:balhoff/relation-graph.git</connection>
</scm>
<developers>
<developer>
<id>balhoff</id>
<name>Jim Balhoff</name>
<email>balhoff@renci.org</email>
</developer>
</developers>
)

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"
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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,
Expand All @@ -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
*/
Expand Down
70 changes: 70 additions & 0 deletions cli/src/main/scala/org/renci/relationgraph/Main.scala
Original file line number Diff line number Diff line change
@@ -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))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)

/**
Expand Down Expand Up @@ -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)
}
}
}

Expand Down
Loading