Play Scala - Found Future[String] but expected String error - scala

I am new to Play Scala. Below code snippet I am trying to use to expose an API. Its failing with the below error.
type mismatch;
found : scala.concurrent.Future[String]
required: String
API source:
def getStrategy(date: String) = Action.async {
val currentDate:String = toString(DateTime.now.minusDays(1))
getDecision(date, currentDate).map(lastError => Ok("No Strategy found:%s".format(lastError)))
}
def getDecision(reqestedDate:String, currentDate:String): Future[String] = {
getForecastPrice(reqestedDate).map(forecastPrice =>
getCurrentPrice(currentDate).map(currentPrice =>
getCall(currentPrice, forecastPrice)
)
)
}
def getForecastPrice(requestedDate:String): Future[Option[Double]] = {
predictionRepo.getPrediction(requestedDate).map( maybePrediction =>
maybePrediction.map ( fPrice => fPrice.price )
)
}
def getCurrentPrice(currentDate:String): Future[Option[Double]] = {
priceRepo.getPrice(currentDate).map ( maybePrice =>
maybePrice.map ( cPrice => cPrice.price )
)
}
def getCall(currentPrice:Option[Double], forcastPrice:Option[Double]): String = {
var decision = ""
println("currentPrice:" + currentPrice)
println("forcastPrice:" + forcastPrice)
if(currentPrice.isDefined && forcastPrice.isDefined) {
var currentPriceValue = currentPrice.get.toDouble
var forcastPriceValue = forcastPrice.get.toDouble
if((currentPriceValue*5/100) < (currentPriceValue - forcastPriceValue)) {
decision = "BUY"
} else if((currentPriceValue*5/100) > (currentPriceValue - forcastPriceValue)) {
decision = "SELL"
} else {
decision = "HOLD"
}
}
return decision
}
Error in the above code is shwon at the below location.
getCurrentPrice(currentDate).map(currentPrice =>
Could you please help me to find the reason for this issue?

Can you change the first map in getDecision to flatMap:
def getDecision(reqestedDate:String, currentDate:String): Future[String] = {
getForecastPrice(reqestedDate).flatMap(forecastPrice =>
getCurrentPrice(currentDate).map(currentPrice =>
getCall(currentPrice, forecastPrice)
)
)
}
With the current code the result type would Future[Future[String]]

You can use for comprehension rather than using a map inside another map. The sample code would be something like this.
for(
getForecastPriceResult <- getForecastPrice(requestedDate);
getCurrentPriceResult <- getCurrentPrice(currentDate)
) yield(getCall(getForecastPriceResult,getCurrentPriceResult))

Related

How to skip "ArrayIndexOutOfBoundsException: 0" from a Scala function (return type: Iterator String Array)?

I have a Scala function as shown below. Input neighborhood is array of strings. However, sometimes it (i.e. neighborhood) can be empty. In that case I get "ArrayIndexOutOfBoundsException", which is understandable. I want to avoid this exception. I mean, my code has to skip this error and move on to the next job (not shown here).
I tried this:
if(neighborhood.isEmpty){
true
} else {
val key = neighborhood(0)
neighborhood
.filterNot { _.equals(key) }
.combinations(k - 1)
}
But IntelliJ shows 'type mismatch between iterator and boolean.'
How to deal with this? I am newbie in Scala. Thanks!
Here is original function:
private def scanData(neighborhood: Array[String], k: Int): Iterator[Array[String]] = {
val key = neighborhood(0)
neighborhood
.filterNot { _.equals(key) }
.combinations(k - 1)
}
```scala
You can make use of headOption for a clean approach.
neighborhood.headOption.map { key =>
neighborhood.tail
.filterNot(_ == key)
.combinations(k-1)
}.getOrElse(Iterator.empty)
Use Option. You may find information here. Here you are en example :
object Run {
def main(args:Array[String]):Unit = {
val neighborhood:Array[String] = Array("1", "2","3")
val k = 1
val isa = geta(emptyArray) match {
case Some(isa) => scanData(neighborhood,k)
case None => Array.empty
}
}
def scanData(neighborhood: Array[String], k: Int): Iterator[Array[String]] = {
val key = neighborhood(0)
neighborhood
.filterNot { _.equals(key) }
.combinations(k - 1)
}
def geta(neighborhood:Array[String]):Option[Array[String]] = {
if(neighborhood.isEmpty){
return None;
} else {
return Some(neighborhood)
}
}
}

How to use `flatMap` and `map` to fill a `list` on Play framework + Scala?

I am struggling to use flatMap and map with Play framework + Scala. This method has a bunch of other issues, but I am trying to go through them one for each time. The first thing that I cannot figure out how to implement is how to fill a Seq inside nested flatMap and map and return a Json output. Here is my method:
def getRacks(at: String) = Action.async { implicit request: Request[AnyContent] =>
var rackSeq: Seq[Rack] = Seq.empty
var gpuSeq: Seq[Gpu] = Seq.empty
rackRepository.get(Util.toTime(at)).flatMap { resultRack: Seq[RackRow] =>
resultRack.map { r: RackRow =>
gpuRepository.getByRack(r.id).map { result: Seq[GpuRow] =>
result.map { gpuRow: GpuRow =>
gpuSeq = gpuSeq :+ Gpu(gpuRow.id, gpuRow.rackId, gpuRow.produced, Util.toDate(gpuRow.installedAt))
println(gpuRow)
}
}
val rack = Rack(r.id, r.produced, Util.toDate(r.currentHour), gpuSeq)
rackSeq = rackSeq :+ rack
}
println("rackSeq: " + rackSeq)
Future.successful(Ok(Json.toJson(rackSeq)).as(JSON))
}.recover {
case pe: ParseException => BadRequest(Json.toJson("Error on parse String to time."))
case e: Exception => BadRequest(Json.toJson("Error to get racks."))
case _ => BadRequest(Json.toJson("Unknow error to get racks."))
}
}
I was expecting that rackSeq will be filled with GpuRow. but my output is like this:
rackSeq: List(Rack(rack-1,0.2,2018-01-23T14:15:00.79Z,List()))
GpuRow(rack-1-gpu-0,rack-1,0.2,1515867048515)
How to evaluate both lists to the output?
Instead of mutating variables, stay within the context of a Future and perform transformations until you reach the desired result. Assuming the following types...
rackRepository.get(Util.toTime(at)) // Future[Seq[RackRow]]
gpuRepository.getByRack(r.id) // Future[Seq[GpuRow]]
...you could do this instead:
def gpuRowToGpu(gpuRow: GpuRow): Gpu = {
Gpu(gpuRow.id, gpuRow.rackId, gpuRow.produced, Util.toDate(gpuRow.installedAt))
}
def getRacks(at: String) = Action.async { implicit request: Request[AnyContent] =>
rackRepository.get(Util.toTime(at)).flatMap { resultRack: Seq[RackRow] =>
val seqFutRack: Seq[Future[Rack]] = resultRack.map { r: RackRow =>
gpuRepository.getByRack(r.id).map { result: Seq[GpuRow] =>
val gpus = result.map(gpuRowToGpu) // Seq[Gpu]
Rack(r.id, r.produced, Util.toDate(r.currentHour), gpus)
} // Future[Rack]
}
val futSeqRack: Future[Seq[Rack]] = Future.sequence(seqFutRack)
futSeqRack.map(racks => Ok(Json.toJson(racks)).as(JSON))
}.recover {
...
}
}

scala returns doesn't conform to required S_

I got the error
found : scala.concurrent.Future[Option[models.ProcessTemplatesModel]]
required: Option[models.ProcessTemplatesModel]
My function is below
def createCopyOfProcessTemplate(processTemplateId: Int): Future[Option[ProcessTemplatesModel]] = {
val action = processTemplates.filter(_.id === processTemplateId).result.map(_.headOption)
val result: Future[Option[ProcessTemplatesModel]] = db.run(action)
result.map { case (result) =>
result match {
case Some(r) => {
var copy = (processTemplates returning processTemplates.map(_.id)) += ProcessTemplatesModel(None, "[Copy of] " + r.title, r.version, r.createdat, r.updatedat, r.deadline, r.status, r.comment, Some(false), r.checkedat, Some(false), r.approvedat, false, r.approveprocess, r.trainingsprocess)
val composedAction = copy.flatMap { id =>
processTemplates.filter(_.id === id).result.headOption
}
db.run(composedAction)
}
}
}
}
what is my problem in this case?
edit:
my controller function looks like this:
def createCopyOfProcessTemplate(processTemplateId: Int) = Action.async {
processTemplateDTO.createCopyOfProcessTemplate(processTemplateId).map { process =>
Ok(Json.toJson(process))
}
}
Is there my failure?
According to the your code - there are the following issues:
You use two db.run which return futures, but inner future will
not complete. For resolving it you should compose futures with
flatMap or for-comprehension.
You use only one partial-function case Some(_) => for pattern matching
and don't handle another value None.
You can use only one db.run and actions composition.
Your code can be like as:
def createCopyOfProcessTemplate(processTemplateId: Int): Future[Option[ProcessTemplatesModel]] = {
val action = processTemplates.filter(...).result.map(_.headOption)
val composedAction = action.flatMap {
case Some(r) =>
val copyAction = (processTemplates returning processTemplates...)
copyAction.flatMap { id =>
processTemplates.filter(_.id === id).result.headOption
}
case _ =>
DBIO.successful(None) // issue #2 has been resolved here
}
db.run(composedAction) // issue #3 has been resolved here
}
We get rid of issue #1 (because we use actions composition).

Save model with data from form using Play with Scala

I am trying to create and save a new Supply model with data from a form.
Here's the relevant code from SuppliesController:
def submitSupplyOffer = SecuredAction.async { implicit request =>
SupplyForm.form.bindFromRequest.fold(
form => Future.successful(BadRequest(views.html.supplies.index(request.identity, form))),
data => {
val supply = Supply(
id = UUID.randomUUID(),
userID = request.identity.userID,
resource = data.resource,
amount = data.amount
)
for {
supply <- supplyService.save(supply.copy())
result <- Redirect(routes.Application.index())
} yield {
result
}
}
)
}
And here is the error I get:
Thanks in advance!
Redirect.apply returns a Result, not a Future[Result], so you need to pull it out of the for-comprehension:
def submitSupplyOffer = SecuredAction.async { implicit request =>
SupplyForm.form.bindFromRequest.fold(
form => Future.successful(BadRequest(views.html.supplies.index(request.identity, form))),
data => {
val supply = Supply(
id = UUID.randomUUID(),
userID = request.identity.userID,
resource = data.resource,
amount = data.amount
)
for {
supply <- supplyService.save(supply.copy())
} yield Redirect(routes.Application.index())
}
)
}

In Slick (Scala), how to refactor codes like this?

I see codes like this:
trait LoginInfoRepoImpl extends LoginInfoRepo {
def loginInfoRepository = new LoginInfoRepository {
private val loginInfoTable = TableQuery[LoginInfoTable]
// return a LoginID
def save(userLoginInfo: userLoginInfo): Future[userLoginID] = Future {
val newRecord = DB.withSession { implicit session =>
loginInfoTable.filter(
l => l.userID === userLoginInfo.userID &&
(l.deviceID === userLoginInfo.deviceID || (l.deviceID.isEmpty && userLoginInfo.userID.isEmpty))).list.headOption.fold {
val newSubID = loginInfoTable.filter(l => l.userID === userLoginInfo.userID).sortBy(_.subID.desc).take(1).map(_.subID).list.headOption.getOrElse(0) + 1
(loginInfoTable returning loginInfoTable) += LoginInfoRecord(userLoginInfo.userID, newSubID, userLoginInfo.deviceID, userLoginInfo.userAgent, getCurrentTime)
} { l =>
// to do : update time
val q = for (l <- loginInfoTable if l.userID === userLoginInfo.userID && ((l.deviceID === userLoginInfo.deviceID)
|| (l.deviceID.isEmpty && userLoginInfo.userID.isEmpty)))
yield l.lastLoginTime
q.updateReturning(loginInfoTable.map(identity), getCurrentTime).head
}
}
userLoginID(newRecord.userID, newRecord.subID.toString)
}
}
}
This looks a little monstrous for me. I found many things are crowded into one line. Also, I found l.deviceID is of type Column[Option[String]], while userLoginInfo.deviceID is of typeOption[String], they don't equal if both of them is None. Thus a l.device.isEmpty looks necessary..
Does anyone have suggestions about how to refactor these codes? Thanks!
First of all: Better indentation and breaking up long lines.
Slick-specific: pull queries out of the withSession block and reuse them for shared logic. Use firstOption instead of .list.headOption.
Side-note: Since you are generating IDs based on old ones, you may want to use a transaction or use a more efficient means provided by your DB.
trait LoginInfoRepoImpl extends LoginInfoRepo {
def loginInfoRepository = new LoginInfoRepository {
private val loginInfoTable = TableQuery[LoginInfoTable]
// return a LoginID
def save(userLoginInfo: userLoginInfo): Future[userLoginID] = Future {
val userLoginInfoQuery = loginInfoTable.filter(l => l.userID === userLoginInfo.userID)
val deviceLoginInfoQuery = userLoginInfoQuery.filter(
l =>
l.deviceID === userLoginInfo.deviceID ||
(
l.deviceID.isEmpty && userLoginInfo.userID.isEmpty
)
)
val subIdQuery = userLoginInfoQuery.sortBy(_.subID.desc).map(_.subID)
val newRecord = DB.withTransaction{ implicit session =>
deviceLoginInfoQuery
.firstOption
.fold {
val newSubID = subIdQuery.firstOption.getOrElse(0) + 1
val newLoginInfo = LoginInfoRecord(userLoginInfo.userID, newSubID, userLoginInfo.deviceID, userLoginInfo.userAgent, getCurrentTime)
(loginInfoTable returning loginInfoTable) += newLoginInfo
}( _ =>
// to do : update time
deviceLoginInfoQuery
.map(_.lastLoginTime)
.updateReturning(loginInfoTable.map(identity), getCurrentTime)
.head
)
}
userLoginID(newRecord.userID, newRecord.subID.toString)
}
}
}