Skip to content

Commit

Permalink
finos#1052 refactored to separated generic file reader vs basket spec…
Browse files Browse the repository at this point in the history
…ific logic
  • Loading branch information
naleeha authored and chrisjstevo committed Jan 4, 2024
1 parent 39c2e71 commit cddecb2
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.finos.vuu.core.module.basket.csv

import com.typesafe.scalalogging.StrictLogging

import scala.util.control.NonFatal

class BasketLoader(resourcePath: Option[String] = None) extends StrictLogging {
def loadBasketIds(): Array[String] = {
FileLoader.getFileNames(getPath(resourcePath), ".csv")
.map(fileName => "." + fileName.replace(".csv", "").toUpperCase)
}

def loadConstituents(basketId: String): Array[Map[String, Any]] = {
try {
val csvFiles = FileLoader.getFiles(getPath(resourcePath), getBasketFileName(basketId))

if (csvFiles.isEmpty) {
logger.error(s"Failed to find constituents file for $basketId")
Array.empty
}
else {
val csvFile = csvFiles(0)
logger.info("Loading basket static:" + basketId + "(" + csvFile + ")")
val csvContent = FileLoader.readCsvContent(csvFile)

logger.info(s"Found ${csvContent.dataRows.length} constituents for basket $basketId")

csvContent.dataRows.map(row => toConstituent(csvContent, row))
}
}
catch {
case NonFatal(t) => logger.error(s"Failed to parse constituents for $basketId", t)
Array.empty
}
}

private def toConstituent(csvContent: CsvContent, row: Array[String]) = {
Map[String, Any](
"Symbol" -> csvContent.getValue("Symbol", row),
"Last Trade" -> csvContent.getValue("Last Trade", row),
"Name" -> csvContent.getValue("Name", row),
"Weighting" -> csvContent.getValueAsDouble("Weighting", row, 0.0D),
"Volume" -> csvContent.getValue("Volume", row),
"Change" -> csvContent.getValue("Change", row)
)
}

private def getBasketFileName(basketId: String) = {
basketId.replace(".", "").toLowerCase + ".csv"
}

private def getPath(resourcePath: Option[String] = None): String = {
if (resourcePath.isDefined) resourcePath.get
else getClass.getResource("/static").getPath
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.finos.vuu.core.module.basket.csv

class CsvContent(data: Array[Array[String]]) {

private val header = data(0)
val dataRows: Array[Array[String]] = data.tail

def getValue(headerName: String, row: Array[String]) = {
val index = header.indexOf(headerName)
if (row.length > index && index > -1) row(index) else null
}

def getValueAsDouble(headerName: String, row: Array[String], defaultValue: Double) = {
val x = getValue(headerName, row)
if (x == null) defaultValue else x.toDouble
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.finos.vuu.core.module.basket.csv

import java.io.File
import scala.io.Source

object FileLoader {
def getFileNames(folderPath: String, extensionFilter: String): Array[String] = {
getFiles(folderPath)
.filter(_.getName.endsWith(extensionFilter))
.map(file => file.getName)
}

def getFiles(folderPath: String, fileName: String): Array[File] = {
getFiles(folderPath)
.filter(_.getName.equals(fileName))
}

def readCsvContent(file: File): CsvContent = {
val bufferedSource = Source.fromFile(file)
val csv = for (line <- bufferedSource.getLines) yield line.split(",").map(_.trim)
val array = csv.toArray
bufferedSource.close
new CsvContent(array)
}

private def getFiles(folderPath: String): Array[File] = {
val dir = new File(folderPath)
if (dir.listFiles == null) Array.empty
else dir.listFiles.filter(_.isFile)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.finos.vuu.csv

import org.finos.vuu.core.module.basket.csv.BasketLoader
import org.scalatest.featurespec.AnyFeatureSpec
class BasketLoaderTests extends AnyFeatureSpec {

Feature("Basket ids loading Test Case") {

Scenario("Can successfully get basket ids from files") {
val testResourcePath = this.getClass.getResource("/constituents").getPath
val basketLoader = new BasketLoader(Some(testResourcePath))
val basketIds = basketLoader.loadBasketIds()

assert(basketIds.length == 2)
assert(basketIds.contains(".FTSE100"))
assert(basketIds.contains(".FTSEWITHERROR"))
}

Scenario("when no file found return empty") {
val testResourcePath = this.getClass.getResource("/constituents").getPath + "/doesNotExist"
val basketLoader = new BasketLoader(Some(testResourcePath))
val basketIds = basketLoader.loadBasketIds()

assert(basketIds.length == 0)
}
}

Feature("Basket constituent loading Test Case") {

val testResourcePath = this.getClass.getResource("/constituents").getPath
val basketLoader = new BasketLoader(Some(testResourcePath))

Scenario("Can successfully load and parse basket constituents") {

val constituents = basketLoader.loadConstituents(".FTSE100")

assert(constituents.length == 3)
val firstRow = constituents.head
assert(firstRow("Symbol") == "AAL.L")
assert(firstRow("Last Trade") == "436.35")
assert(firstRow("Name") == "Anglo American PLC")
assert(firstRow("Weighting") == 0.0278736825813547)
assert(firstRow("Volume") == "5799089")
assert(firstRow("Change") == "5.35")
}

Scenario("When parsing basket constituents fails return empty") {

val constituents = basketLoader.loadConstituents(".FTSEWithError")

assert(constituents.length == 0)
}


Scenario("When no matching basket constituents file return empty") {

val constituents = basketLoader.loadConstituents(".NoSuchFile")

assert(constituents.length == 0)
}

}
}

0 comments on commit cddecb2

Please sign in to comment.