Skip to content

Commit 655272a

Browse files
authored
Merge pull request #28 from delphi-hub/csv-option
Adding the --csv option to search and retrieve.
2 parents f74c0fd + 72add90 commit 655272a

File tree

8 files changed

+107
-14
lines changed

8 files changed

+107
-14
lines changed

build.sbt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ libraryDependencies += "io.spray" %% "spray-json" % "1.3.3"
2727
libraryDependencies += "de.vandermeer" % "asciitable" % "0.3.2"
2828
libraryDependencies += "com.lihaoyi" %% "fansi" % "0.2.5"
2929
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value
30+
libraryDependencies += "au.com.bytecode" % "opencsv" % "2.4"
3031

3132
debianPackageDependencies := Seq("java8-runtime-headless")
3233

src/main/scala/de/upb/cs/swt/delphi/cli/Config.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ package de.upb.cs.swt.delphi.cli
2626
case class Config(server: String = sys.env.getOrElse("DELPHI_SERVER", "https://delphi.cs.uni-paderborn.de/api/"),
2727
verbose: Boolean = false,
2828
raw: Boolean = false,
29+
csv: String = "",
2930
silent: Boolean = false,
3031
list : Boolean = false,
3132
mode: String = "",
@@ -36,5 +37,6 @@ case class Config(server: String = sys.env.getOrElse("DELPHI_SERVER", "https://d
3637
opts: List[String] = List()) {
3738

3839
lazy val consoleOutput = new ConsoleOutput(this)
40+
lazy val csvOutput = new CsvOutput(this)
3941

4042
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright (C) 2018 The Delphi Team.
2+
// See the LICENCE file distributed with this work for additional
3+
// information regarding copyright ownership.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package de.upb.cs.swt.delphi.cli
18+
19+
import java.io.{BufferedWriter, FileWriter}
20+
21+
import de.upb.cs.swt.delphi.cli.artifacts.Result
22+
import au.com.bytecode.opencsv.CSVWriter
23+
24+
import scala.collection.JavaConverters._
25+
26+
/**
27+
* Export search and retrieve results to .csv file.
28+
*
29+
* @author Lisa Nguyen Quang Do
30+
* @author Ben Hermann
31+
*
32+
*/
33+
34+
class CsvOutput(config: Config) {
35+
36+
def exportResult(value: Any): Unit = {
37+
printToCsv(
38+
value match {
39+
case results :
40+
Seq[Result] if results.headOption.getOrElse(Seq.empty[Array[String]]).isInstanceOf[Result] => resultsToCsv(results)
41+
case _ => Seq.empty[Array[String]]
42+
}
43+
)
44+
}
45+
46+
def printToCsv(table : Seq[Array[String]]): Unit = {
47+
val outputFile = new BufferedWriter(new FileWriter(config.csv, /* append = */false))
48+
val csvWriter = new CSVWriter(outputFile)
49+
csvWriter.writeAll(seqAsJavaList(table))
50+
outputFile.close()
51+
}
52+
53+
def resultsToCsv(results : Seq[Result]) : Seq[Array[String]] = {
54+
val headOption = results.headOption.getOrElse()
55+
if (!headOption.isInstanceOf[Result]) {
56+
Seq.empty[Array[String]]
57+
} else {
58+
val fieldNames = headOption.asInstanceOf[Result].fieldNames()
59+
val tableHeader : Array[String] =
60+
fieldNames.+:("discovered at").+:("version").+:("groupId").+:("artifactId").+:("source").+:("Id").toArray
61+
results.map {
62+
e => {
63+
Array(e.id, e.metadata.source, e.metadata.artifactId, e.metadata.groupId, e.metadata.version,
64+
e.metadata.discovered).++(fieldNames.map(f => e.metricResults(f).toString))
65+
}
66+
}.+:(tableHeader)
67+
}
68+
}
69+
}

src/main/scala/de/upb/cs/swt/delphi/cli/DelphiCLI.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ object DelphiCLI extends App {
3232

3333
implicit val system = ActorSystem()
3434

35-
3635
val cliParser = {
3736
new scopt.OptionParser[Config]("delphi-cli") {
3837
head("Delphi Command Line Tool", s"(${BuildInfo.version})")
@@ -55,15 +54,17 @@ object DelphiCLI extends App {
5554
.children(
5655
arg[String]("id").action((x, c) => c.copy(id = x)).text("The ID of the project to retrieve"),
5756
opt[Unit]('f', "file").action((_, c) => c.copy(opts = List("file"))).text("Use to load the ID from file, " +
58-
"with the filepath given in place of the ID")
57+
"with the filepath given in place of the ID"),
58+
opt[String]("csv").action((x, c) => c.copy(csv = x)).text("Path to the output .csv file (overwrites existing file)")
5959
)
6060

6161
cmd("search").action((s, c) => c.copy(mode = "search"))
6262
.text("Search artifact using a query.")
6363
.children(
6464
arg[String]("query").action((x,c) => c.copy(query = x)).text("The query to be used."),
6565
opt[Int]("limit").action((x, c) => c.copy(limit = Some(x))).text("The maximal number of results returned."),
66-
opt[Unit](name="list").action((_, c) => c.copy(list = true)).text("Output results as list (raw option overrides this)")
66+
opt[Unit](name="list").action((_, c) => c.copy(list = true)).text("Output results as list (raw option overrides this)"),
67+
opt[String]("csv").action((x, c) => c.copy(csv = x)).text("Path to the output .csv file (overwrites existing file)")
6768
)
6869
}
6970
}

src/main/scala/de/upb/cs/swt/delphi/cli/artifacts/SearchResult.scala

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,23 @@ package de.upb.cs.swt.delphi.cli.artifacts
1818

1919
import spray.json.DefaultJsonProtocol
2020

21-
case class RetrieveResult(val id: String,
22-
val metadata: ArtifactMetadata,
23-
val metricResults: Map[String, Int]) {
21+
trait Result{
22+
val id: String
23+
val metadata: ArtifactMetadata
24+
val metricResults: Map[String, Int]
25+
2426
def toMavenIdentifier() : String = s"${metadata.groupId}:${metadata.artifactId}:${metadata.version}"
2527

2628
def fieldNames() : List[String] = metricResults.keys.toList.sorted
2729
}
2830

29-
case class SearchResult(val id: String,
30-
val metadata: ArtifactMetadata,
31-
val metricResults: Map[String, Int]) {
32-
def toMavenIdentifier() : String = s"${metadata.groupId}:${metadata.artifactId}:${metadata.version}"
31+
case class SearchResult(id: String,
32+
metadata: ArtifactMetadata,
33+
metricResults: Map[String, Int]) extends Result
3334

34-
def fieldNames() : List[String] = metricResults.keys.toList.sorted
35-
}
35+
case class RetrieveResult(id: String,
36+
metadata: ArtifactMetadata,
37+
metricResults: Map[String, Int]) extends Result
3638

3739
case class ArtifactMetadata(val artifactId: String,
3840
val source: String,

src/main/scala/de/upb/cs/swt/delphi/cli/commands/Command.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,6 @@ trait Command {
8080
protected def error(implicit config: Config): String => Unit = config.consoleOutput.outputError _
8181
protected def success(implicit config: Config): String => Unit = config.consoleOutput.outputSuccess _
8282

83+
protected def exportResult(implicit config: Config): Any => Unit = config.csvOutput.exportResult _
84+
8385
}

src/main/scala/de/upb/cs/swt/delphi/cli/commands/RetrieveCommand.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import akka.stream.ActorMaterializer
2323
import de.upb.cs.swt.delphi.cli.Config
2424
import de.upb.cs.swt.delphi.cli.artifacts.RetrieveResult
2525
import de.upb.cs.swt.delphi.cli.artifacts.SearchResultJson._
26+
import de.upb.cs.swt.delphi.cli.commands.SearchCommand.information
2627
import spray.json.DefaultJsonProtocol
2728

2829
import scala.concurrent.Await
@@ -62,7 +63,9 @@ object RetrieveCommand extends Command with SprayJsonSupport with DefaultJsonPro
6263
result.map(s => {
6364
if (config.raw) {
6465
reportResult(config)(s)
65-
} else {
66+
}
67+
68+
if (!config.raw || !config.csv.equals("")) {
6669
val unmarshalledFuture = Unmarshal(s).to[List[RetrieveResult]]
6770

6871
unmarshalledFuture.transform {
@@ -71,6 +74,11 @@ object RetrieveCommand extends Command with SprayJsonSupport with DefaultJsonPro
7174
success(config)(s"Found ${unmarshalled.size} item(s).")
7275
reportResult(config)(unmarshalled)
7376

77+
if(!config.csv.equals("")) {
78+
exportResult(config)(unmarshalled)
79+
information(config)("Results written to file '" + config.csv + "'")
80+
}
81+
7482
Success(unmarshalled)
7583
}
7684
case Failure(e) => {

src/main/scala/de/upb/cs/swt/delphi/cli/commands/SearchCommand.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import akka.util.ByteString
2929
import de.upb.cs.swt.delphi.cli.Config
3030
import de.upb.cs.swt.delphi.cli.artifacts.SearchResult
3131
import de.upb.cs.swt.delphi.cli.artifacts.SearchResultJson._
32+
import de.upb.cs.swt.delphi.cli.commands.RetrieveCommand.information
3233
import spray.json.DefaultJsonProtocol
3334

3435
import scala.concurrent.duration._
@@ -77,7 +78,9 @@ object SearchCommand extends Command with SprayJsonSupport with DefaultJsonProto
7778

7879
if (config.raw || result.equals("")) {
7980
reportResult(config)(result)
80-
} else {
81+
}
82+
83+
if(!(config.raw || result.equals("")) || !config.csv.equals("")) {
8184
val unmarshalledFuture = Unmarshal(result).to[List[SearchResult]]
8285

8386
val processFuture = unmarshalledFuture.transform {
@@ -108,6 +111,11 @@ object SearchCommand extends Command with SprayJsonSupport with DefaultJsonProto
108111
reportResult(config)(results)
109112

110113
information(config)(f"Query took $queryRuntime%.2fs.")
114+
115+
if(!config.csv.equals("")) {
116+
exportResult(config)(results)
117+
information(config)("Results written to file '" + config.csv + "'")
118+
}
111119
}
112120

113121
case class Query(query: String,

0 commit comments

Comments
 (0)