Skip to content

Commit fd4e7e9

Browse files
authored
Split project into core and cli modules. Add static method for easy access from Java. (#121)
* Split project into core and cli modules. Add static method for easy access from Java. * Fix type error.
1 parent 6725672 commit fd4e7e9

File tree

10 files changed

+278
-155
lines changed

10 files changed

+278
-155
lines changed

build.sbt

Lines changed: 71 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,77 @@
1-
enablePlugins(JavaAppPackaging)
2-
enablePlugins(BuildInfoPlugin)
3-
enablePlugins(GitVersioning)
1+
lazy val zioVersion = "2.0.0-RC5"
2+
lazy val gitCommitString = SettingKey[String]("gitCommit")
43

5-
organization := "org.renci"
4+
lazy val commonSettings = Seq(
5+
organization := "org.geneontology",
6+
version := "2.2-SNAPSHOT",
7+
licenses := Seq("MIT license" -> url("https://opensource.org/licenses/MIT")),
8+
scalaVersion := "2.13.8",
9+
scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8"),
10+
javaOptions += "-Xmx8G"
11+
)
612

7-
name := "relation-graph"
13+
lazy val publishSettings = Seq(
14+
Test / publishArtifact := false,
15+
publishMavenStyle := true,
16+
publishTo := {
17+
val nexus = "https://oss.sonatype.org/"
18+
if (isSnapshot.value) Some("snapshots" at nexus + "content/repositories/snapshots")
19+
else Some("releases" at nexus + "service/local/staging/deploy/maven2")
20+
},
21+
pomExtra := <scm>
22+
<url>git@github.com:balhoff/relation-graph.git</url>
23+
<connection>scm:git:git@github.com:balhoff/relation-graph.git</connection>
24+
</scm>
25+
<developers>
26+
<developer>
27+
<id>balhoff</id>
28+
<name>Jim Balhoff</name>
29+
<email>balhoff@renci.org</email>
30+
</developer>
31+
</developers>
32+
)
833

9-
version := "2.1.0"
34+
lazy val parentProject = project
35+
.in(file("."))
36+
.settings(commonSettings)
37+
.settings(name := "relation-graph-project", publish / skip := true)
38+
.aggregate(core, cli)
1039

11-
licenses := Seq("MIT license" -> url("https://opensource.org/licenses/MIT"))
12-
13-
scalaVersion := "2.13.8"
14-
15-
scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8")
16-
17-
javaOptions += "-Xmx8G"
18-
19-
testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework")
20-
21-
val gitCommitString = SettingKey[String]("gitCommit")
22-
23-
gitCommitString := git.gitHeadCommit.value.getOrElse("Not Set")
24-
25-
buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion, gitCommitString)
26-
27-
buildInfoPackage := "org.renci.relationgraph"
28-
29-
val zioVersion = "2.0.0-RC3"
40+
lazy val core = project
41+
.in(file("core"))
42+
.settings(commonSettings)
43+
.settings(
44+
name := "relation-graph",
45+
description := "relation-graph core",
46+
testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework"),
47+
libraryDependencies ++= Seq(
48+
"dev.zio" %% "zio" % zioVersion,
49+
"dev.zio" %% "zio-streams" % zioVersion,
50+
"org.geneontology" %% "whelk-owlapi" % "1.1.1",
51+
"org.apache.jena" % "apache-jena-libs" % "4.4.0" exclude("org.slf4j", "slf4j-log4j12"),
52+
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.4",
53+
"dev.zio" %% "zio-test" % zioVersion % Test,
54+
"dev.zio" %% "zio-test-sbt" % zioVersion % Test
55+
)
56+
)
57+
.settings(publishSettings)
3058

31-
libraryDependencies ++= {
32-
Seq(
33-
"dev.zio" %% "zio" % zioVersion,
34-
"dev.zio" %% "zio-streams" % zioVersion,
35-
"org.geneontology" %% "whelk-owlapi" % "1.1.1",
36-
"com.outr" %% "scribe-slf4j" % "3.8.2",
37-
"com.github.alexarchambault" %% "case-app" % "2.0.6",
38-
"org.apache.jena" % "apache-jena-libs" % "4.4.0" exclude ("org.slf4j", "slf4j-log4j12"),
39-
"dev.zio" %% "zio-test" % zioVersion % Test,
40-
"dev.zio" %% "zio-test-sbt" % zioVersion % Test
59+
lazy val cli = project
60+
.in(file("cli"))
61+
.enablePlugins(JavaAppPackaging)
62+
.enablePlugins(BuildInfoPlugin)
63+
.enablePlugins(GitVersioning)
64+
.settings(commonSettings)
65+
.dependsOn(core)
66+
.settings(
67+
name := "relation-graph-cli",
68+
executableScriptName := "relation-graph",
69+
publish / skip := true,
70+
libraryDependencies ++= Seq(
71+
"com.outr" %% "scribe-slf4j" % "3.8.2",
72+
"com.github.alexarchambault" %% "case-app" % "2.0.6"
73+
),
74+
gitCommitString := git.gitHeadCommit.value.getOrElse("Not Set"),
75+
buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion, gitCommitString),
76+
buildInfoPackage := "org.renci.relationgraph"
4177
)
42-
}

src/main/scala/org/renci/relationgraph/Config.scala renamed to cli/src/main/scala/org/renci/relationgraph/Config.scala

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import caseapp._
44
import caseapp.core.Error.MalformedValue
55
import caseapp.core.argparser.{ArgParser, SimpleArgParser}
66
import org.renci.relationgraph.Config.{BoolValue, FalseValue, TrueValue}
7+
import org.renci.relationgraph.RelationGraph.Config.{OWLMode, OutputMode, RDFMode}
78

89
@AppName("relation-graph")
910
@ProgName("relation-graph")
@@ -16,7 +17,7 @@ final case class Config(
1617
outputFile: String,
1718
@HelpMessage("Configure style of triples to be output. RDF mode is the default; each existential relation is collapsed to a single direct triple.")
1819
@ValueDescription("RDF|OWL")
19-
mode: Config.OutputMode = Config.RDFMode,
20+
mode: OutputMode = RDFMode,
2021
@HelpMessage("Property to restrict output relations to. Provide option multiple times for multiple properties.")
2122
@ValueDescription("IRI")
2223
property: List[String] = Nil,
@@ -43,28 +44,32 @@ final case class Config(
4344
disableOwlNothing: BoolValue = FalseValue,
4445
@HelpMessage("Set log level to INFO")
4546
@ValueDescription("bool")
46-
verbose: Boolean = false)
47+
verbose: Boolean = false) {
48+
49+
def toRelationGraphConfig: RelationGraph.Config =
50+
RelationGraph.Config(
51+
mode = this.mode,
52+
outputSubclasses = this.outputSubclasses.bool,
53+
reflexiveSubclasses = this.reflexiveSubclasses.bool,
54+
equivalenceAsSubclass = this.equivalenceAsSubclass.bool,
55+
outputClasses = this.outputClasses.bool,
56+
outputIndividuals = this.outputIndividuals.bool,
57+
disableOwlNothing = this.disableOwlNothing.bool,
58+
)
4759

48-
object Config {
49-
50-
sealed trait OutputMode
51-
52-
case object RDFMode extends OutputMode
53-
54-
case object OWLMode extends OutputMode
60+
}
5561

56-
object OutputMode {
62+
object Config {
5763

58-
implicit val argParser: ArgParser[OutputMode] = SimpleArgParser.from[OutputMode]("output mode") { arg =>
59-
arg.toLowerCase match {
60-
case "rdf" => Right(RDFMode)
61-
case "owl" => Right(OWLMode)
62-
case _ => Left(MalformedValue("output mode", arg))
63-
}
64+
implicit val rdfModeParser: ArgParser[OutputMode] = SimpleArgParser.from[OutputMode]("output mode") { arg =>
65+
arg.toLowerCase match {
66+
case "rdf" => Right(RDFMode)
67+
case "owl" => Right(OWLMode)
68+
case _ => Left(MalformedValue("output mode", arg))
6469
}
65-
6670
}
6771

72+
6873
/**
6974
* This works around some confusing behavior in case-app boolean parsing
7075
*/
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.renci.relationgraph
2+
3+
import caseapp._
4+
import org.apache.jena.riot.RDFFormat
5+
import org.apache.jena.riot.system.{StreamRDF, StreamRDFWriter}
6+
import org.geneontology.whelk._
7+
import org.renci.relationgraph.RelationGraph.TriplesGroup
8+
import org.semanticweb.owlapi.apibinding.OWLManager
9+
import org.semanticweb.owlapi.model._
10+
import scribe.Level
11+
import scribe.filter.{packageName, select}
12+
import zio._
13+
import Config._
14+
15+
import java.io.{File, FileOutputStream}
16+
import scala.io.Source
17+
18+
object Main extends ZCaseApp[Config] {
19+
20+
override def run(config: Config, arg: RemainingArgs): ZIO[Environment, Nothing, ExitCode] = {
21+
val configureLogging = ZIO.succeed {
22+
scribe.Logger.root
23+
.clearHandlers()
24+
.clearModifiers()
25+
.withModifier(select(packageName("org.renci.relationgraph")).boosted(Level.Info, Level.Warn))
26+
.withHandler(minimumLevel = Some(if (config.verbose) Level.Info else Level.Warn))
27+
.replace()
28+
}
29+
val program = ZIO.scoped {
30+
createStreamRDF(config.outputFile).flatMap { rdfWriter =>
31+
for {
32+
fileProperties <- config.propertiesFile.map(readPropertiesFile).getOrElse(ZIO.succeed(Set.empty[AtomicConcept]))
33+
specifiedProperties = fileProperties ++ config.property.map(prop => AtomicConcept(prop)).to(Set)
34+
ontology <- loadOntology(config.ontologyFile)
35+
_ <- RelationGraph.computeRelations(ontology, specifiedProperties, config.toRelationGraphConfig)
36+
.foreach {
37+
case TriplesGroup(triples) => ZIO.attempt(triples.foreach(rdfWriter.triple))
38+
}
39+
_ <- ZIO.succeed(scribe.info("Done computing relations"))
40+
} yield ()
41+
}
42+
}
43+
configureLogging *>
44+
program.tapError { e =>
45+
if (config.verbose) ZIO.succeed(e.printStackTrace())
46+
else ZIO.succeed(scribe.error(e.getMessage))
47+
}.exitCode
48+
}
49+
50+
def createStreamRDF(path: String): ZIO[Scope, Throwable, StreamRDF] = {
51+
ZIO.acquireRelease(ZIO.attempt(new FileOutputStream(new File(path))))(stream => ZIO.succeed(stream.close())).flatMap { outputStream =>
52+
ZIO.acquireRelease(ZIO.attempt {
53+
val stream = StreamRDFWriter.getWriterStream(outputStream, RDFFormat.TURTLE_FLAT, null)
54+
stream.start()
55+
stream
56+
})(stream => ZIO.succeed(stream.finish()))
57+
}
58+
}
59+
60+
def loadOntology(path: String): Task[OWLOntology] = for {
61+
manager <- ZIO.attempt(OWLManager.createOWLOntologyManager())
62+
ontology <- ZIO.attemptBlocking(manager.loadOntologyFromOntologyDocument(new File(path)))
63+
} yield ontology
64+
65+
def readPropertiesFile(file: String): ZIO[Any, Throwable, Set[AtomicConcept]] =
66+
ZIO.attemptBlocking(Source.fromFile(file, "utf-8")).acquireReleaseWithAuto { source =>
67+
ZIO.attemptBlocking(source.getLines().map(_.trim).filter(_.nonEmpty).map(line => AtomicConcept(line)).to(Set))
68+
}
69+
70+
}

src/main/scala/org/renci/relationgraph/ZCaseApp.scala renamed to cli/src/main/scala/org/renci/relationgraph/ZCaseApp.scala

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import java.io.IOException
1313
/**
1414
* Adapted from caseapp.cats.IOCaseApp
1515
*/
16-
abstract class ZCaseApp[T](implicit val parser0: Parser[T], val messages: Help[T]) extends App {
16+
abstract class ZCaseApp[T](implicit val parser0: Parser[T], val messages: Help[T]) extends ZIOAppDefault {
1717

1818
private[this] def parser: Parser[T] = {
1919
val p = parser0.nameFormatter(nameFormatter)
@@ -23,15 +23,15 @@ abstract class ZCaseApp[T](implicit val parser0: Parser[T], val messages: Help[T
2323
p
2424
}
2525

26-
def run(options: T, remainingArgs: RemainingArgs): ZIO[ZEnv, Nothing, ExitCode]
26+
def run(options: T, remainingArgs: RemainingArgs): ZIO[Environment, Nothing, ExitCode]
2727

28-
private[this] def error(message: Error): ZIO[Console, IOException, ExitCode] =
28+
private[this] def error(message: Error): ZIO[Any, IOException, ExitCode] =
2929
printLine(message.message).as(ExitCode.failure)
3030

31-
private[this] def helpAsked: ZIO[Console, IOException, ExitCode] =
31+
private[this] def helpAsked: ZIO[Any, IOException, ExitCode] =
3232
printLine(messages.withHelp.help).as(ExitCode.success)
3333

34-
private[this] def usageAsked: ZIO[Console, IOException, ExitCode] =
34+
private[this] def usageAsked: ZIO[Any, IOException, ExitCode] =
3535
printLine(messages.withHelp.usage).as(ExitCode.success)
3636

3737
/**
@@ -64,14 +64,17 @@ abstract class ZCaseApp[T](implicit val parser0: Parser[T], val messages: Help[T
6464

6565
private[this] def nameFormatter: Formatter[Name] = Formatter.DefaultNameFormatter
6666

67-
override def run(args: List[String]): ZIO[ZEnv, Nothing, ExitCode] = {
68-
if (args == List("--version")) ZIO.succeed(println(org.renci.relationgraph.BuildInfo.toString)).exitCode
69-
else parser.withHelp.detailedParse(expandArgs(args), stopAtFirstUnrecognized) match {
70-
case Left(err) => error(err).orDie
71-
case Right((WithHelp(_, true, _), _)) => helpAsked.orDie
72-
case Right((WithHelp(true, _, _), _)) => usageAsked.orDie
73-
case Right((WithHelp(_, _, Left(err)), _)) => error(err).orDie
74-
case Right((WithHelp(_, _, Right(t)), remainingArgs)) => run(t, remainingArgs)
67+
override def run: ZIO[Environment with ZIOAppArgs with Scope, Any, Any] = {
68+
ZIO.service[ZIOAppArgs].flatMap { appArgs =>
69+
val args = appArgs.getArgs.toList
70+
if (args == List("--version")) ZIO.succeed(println(org.renci.relationgraph.BuildInfo.toString)).exitCode
71+
else parser.withHelp.detailedParse(expandArgs(args), stopAtFirstUnrecognized) match {
72+
case Left(err) => error(err).orDie
73+
case Right((WithHelp(_, true, _), _)) => helpAsked.orDie
74+
case Right((WithHelp(true, _, _), _)) => usageAsked.orDie
75+
case Right((WithHelp(_, _, Left(err)), _)) => error(err).orDie
76+
case Right((WithHelp(_, _, Right(t)), remainingArgs)) => run(t, remainingArgs)
77+
}
7578
}
7679
}
7780

0 commit comments

Comments
 (0)