package fr.natan.akkastreamfileprocessingapi.service import akka.actor.ActorSystem import akka.stream.scaladsl.{Flow, Sink, Source} import akka.{Done, NotUsed} import com.typesafe.scalalogging.slf4j.Logger import fr.natan.akkastreamfileprocessingapi.datasource.Datasource.{nameBasics, titleBasics, titlePrincipalsBasics} import fr.natan.akkastreamfileprocessingapi.models.Models.{Person, TvSeries} import fr.natan.akkastreamfileprocessingapi.models.ModelsBuilder.{buildPersonModel, buildTvSerieModel} import fr.natan.akkastreamfileprocessingapi.service.AkkaStreamComponents.{buildAllPersonsSink, buildAllTvSeriesSink, buildAndValidateSource, buildPersonFlow, buildPersonSink, buildSource, buildTvSerieFlow, buildTvSeriesSink, filterByPersonIdFlow, filterByPersonNameFlow, filterByTvSeriePrimaryTitleFlow} import org.slf4j.LoggerFactory import org.springframework.stereotype.Component import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration.DurationInt import scala.concurrent.{Await, Future} import scala.language.postfixOps import scala.util.{Failure, Success} @Component class AkkaStreamFileProcessingImpl extends AkkaStreamFileProcessing { implicit val actorSystem: ActorSystem = ActorSystem("AkkaStreamActor") implicit val logger: Logger = Logger(LoggerFactory.getLogger(this.getClass)) override def getAllPersons() = { val personSource: Source[Map[String, String], _] = buildSource(inputFile = nameBasics) //graph val startTime: Long = System.currentTimeMillis() val result: Future[Done] = personSource .via(flow = buildPersonFlow()) .runWith(sink = buildAllPersonsSink(logger = logger)) result.onComplete { case Failure(exception) => logger.error(s"$exception") case Success(value) => logger.info(s"$value") val time: Long =(System.currentTimeMillis()-startTime)/100 logger.info(s"elapsed time: $time") } } override def getPersonById(nconst: String): Future[Person] = { val source: Source[Map[String, String], _] = buildSource(inputFile = nameBasics) val startTime: Long = System.currentTimeMillis() val res = source .via(flow = filterByPersonIdFlow(nconst = nconst)) .runWith(sink = buildPersonSink()) res.andThen { case Failure(exception) => logger.info(s"error!!!!!!!!!!!!!$exception") case Success(value) => logger.info(s"$value") } res } override def getPersonByName(primaryName: String): Unit = { val source: Source[Map[String, String], _] = buildSource(inputFile = nameBasics) source .via( flow = filterByPersonNameFlow(personName = primaryName) ) .runWith(sink = buildPersonSink()) .andThen { case Failure(exception) => logger.info(s"$exception") case Success(value) => logger.info(s"$value: Successufully processing") } } override def getAllTvSeries(): Unit = { val source: Source[Map[String, String], _] = buildAndValidateSource(inputFile = titleBasics) val sink: Sink[TvSeries, Future[Done]] = buildAllTvSeriesSink(logger = logger) val startingTime: Long = System.currentTimeMillis() //graph sink->flow->sink source .via(flow = buildTvSerieFlow()) .runWith(sink = sink) .andThen { case Success(value) => val elapsedTime: Long = (System.currentTimeMillis() - startingTime) / 1000 logger.info(s"$value: successfully processing file, elapsed time $titleBasics: $elapsedTime sec") case Failure(error: Error) => logger.error(s"$error") } } override def getByTvSeriePrimaryTitle(tvSeriePrimaryTitle: String):Future[IndexedSeq[TvSeries]] = { val tvSeriesSource: Source[Map[String, String], _] = buildAndValidateSource(inputFile = titleBasics) val filterByTvSerieTitle: Flow[Map[String, String], TvSeries, NotUsed] = filterByTvSeriePrimaryTitleFlow(tvSeriePrimaryTitle = tvSeriePrimaryTitle) val tvSrie :Future[IndexedSeq[TvSeries]] = tvSeriesSource .via(flow = filterByTvSerieTitle) .runWith(Sink.collection) tvSrie.andThen { case Failure(exception) => logger.info(s"$exception") case Success(value) => logger.info(s"$value") } } private def getTvSerieIdByPrimaryTitle(primaryTitle: String): String = { val source: Source[Map[String, String], _] = buildSource(inputFile = titleBasics) var tvSerie: TvSeries = null val res: Future[Option[String]] = source .filter((rowMap: Map[String, String]) => { rowMap.getOrElse(key = "primaryTitle", "") == primaryTitle }) .map((rowMap: Map[String, String]) => { tvSerie = buildTvSerieModel(rowMap) rowMap.get("tconst") }) .runWith(Sink.head) Await.result(res, 1 minutes) val tconst: String = res.value.get.get.get logger.info(s"TvSerie ID: $tconst, $tvSerie") tconst } private def getListOfPersonsIDByTvSerieID(tconst: String): List[Option[String]]={ val source: Source[Map[String, String], _] = buildSource(inputFile = titlePrincipalsBasics) val startTime : Long = System.currentTimeMillis() val res: Future[IndexedSeq[Option[String]]] = source .filter((rowMaps: Map[String, String])=>{ rowMaps.getOrElse(key = "tconst", default = "")==tconst }) .map((rowMap: Map[String, String])=>{ rowMap.get(key = "nconst") }) .runWith(Sink.collection) .andThen { case Success(value) => logger.info(s"$value") val elapsedTime : Long = (System.currentTimeMillis() - startTime)/1000 logger.info(s"success, elapsed time: $elapsedTime") case Failure(error: Error) => logger.error(s"$error") } Await.result(res, 3 minutes) val listPersonsID: List[Option[String]] = res.value.get.get.toList listPersonsID } private def getListOfPersonsForTvSerie(nconstList: List[Option[String]]): List[Person] = { val source: Source[Map[String, String], _] = buildSource(inputFile = nameBasics) val startTime: Long = System.currentTimeMillis() val res : Future[IndexedSeq[Person]]= source .filter((rowMaps: Map[String, String])=>{ nconstList.contains(rowMaps.get(key = "nconst")) }) .map((rowMap: Map[String, String])=>{ buildPersonModel(rowMap) }) .runWith(Sink.collection) .andThen { case Failure(exception) => logger.error(s"$exception") case Success(value) => val elapsedTime: Long = (System.currentTimeMillis()-startTime)/1000 logger.info(s"END: Successfully, elapsed time: $elapsedTime") } Await.result(res, 4 minutes) val listPersons: List[Person] = res.value.get.get.toList listPersons } override def getPersonsTeamForTvSerie(tvSeriePrimaryTitle: String):Unit={ val tvSerieID: String = getTvSerieIdByPrimaryTitle(primaryTitle = tvSeriePrimaryTitle) val listPersonIDs: List[Option[String]] = getListOfPersonsIDByTvSerieID(tconst = tvSerieID) val personsTeam: List[Person] = getListOfPersonsForTvSerie(nconstList = listPersonIDs) logger.info(s"Team size:${personsTeam.size}") personsTeam.foreach((person: Person)=>{ logger.info(s"${person.toString}") }) } }