package fr.natan.akkastreamfileprocessingapi.service import akka.NotUsed import akka.actor.ActorSystem import akka.stream.alpakka.csv.scaladsl.{CsvParsing, CsvToMap} import akka.stream.scaladsl.{Compression, FileIO, Flow, Sink, Source} import fr.natan.akkastreamfileprocessingapi.businessexceptions.FileNotFoundException import fr.natan.akkastreamfileprocessingapi.models.ModelsAndJsonMap.{Episode, Person, TvSerie} import fr.natan.akkastreamfileprocessingapi.models.ModelsBuilder.{buildEpisodeModel, buildPersonModel, buildTvSerieModel} import fr.natan.akkastreamfileprocessingapi.valitator.Validators.fileExists import java.io.File import java.nio.file.Paths import scala.concurrent.Future //noinspection SpellCheckingInspection object AkkaStreamComponents { implicit val actorSystem: ActorSystem = ActorSystem("AkkaStreamActor") //flows building def buildPersonFlow (): Flow[Map[String, String], Person, NotUsed] = { val personFlow: Flow[Map[String, String], Person, NotUsed] = Flow[Map[String, String]] .map((rowMap: Map[String, String]) => { buildPersonModel(rowMap) }) personFlow } def filterPersonByIdFlow(personID: String): Flow[Map[String, String], Person, NotUsed] = { val personFlowFilter: Flow[Map[String, String], Person, NotUsed] = Flow[Map[String, String]] .filter((rowMap: Map[String, String]) => { rowMap.getOrElse(key = "nconst", default = "") == personID }) .map(rowMap => { buildPersonModel(personMap = rowMap) }) personFlowFilter } def filterPersonByNameFlow(primaryName: String): Flow[Map[String, String], Person, NotUsed] = { val personFlowFilter: Flow[Map[String, String], Person, NotUsed] = Flow[Map[String, String]] .filter((rowMap: Map[String, String]) => { rowMap.getOrElse(key = "primaryName", default = "") == primaryName }) .map((rowMap: Map[String, String]) => { buildPersonModel(personMap = rowMap) }) personFlowFilter } def buildTvSerieFlow(): Flow[Map[String, String], TvSerie, NotUsed] = { val tvSerieFlow: Flow[Map[String, String], TvSerie, NotUsed] = Flow[Map[String, String]] .map((rowMap: Map[String, String]) => { buildTvSerieModel(tvSerieMap = rowMap) }) tvSerieFlow } def buildEpisodeFlow (): Flow[Map[String, String], Episode, NotUsed] ={ val episodeFlow: Flow[Map[String, String], Episode, NotUsed] = Flow[Map[String, String]] .map((rowMap: Map[String, String])=>{ buildEpisodeModel(episodeMap = rowMap) }) episodeFlow } //filters flow def filterTvSerieByIdFlow(tvSerieID: String): Flow[Map[String, String], TvSerie, NotUsed] = { val tvSerieFlowFilter: Flow[Map[String, String], TvSerie, NotUsed] = Flow[Map[String, String]] .filter((rowMap: Map[String, String]) => rowMap.getOrElse(key = "tconst", default = "") == tvSerieID) .map((rowMap: Map[String, String]) => { buildTvSerieModel(tvSerieMap = rowMap) }) tvSerieFlowFilter } def filterTvSerieByPrimaryTitleFlow(tvSeriePrimaryTitle: String): Flow[Map[String, String], TvSerie, NotUsed] = { val filterFlowFilter: Flow[Map[String, String], TvSerie, NotUsed] = Flow[Map[String, String]] .filter((rows: Map[String, String]) => { rows.getOrElse(key = "primaryTitle", default = "") == tvSeriePrimaryTitle }) .map(rowMap => { val tvSerie: TvSerie = buildTvSerieModel(tvSerieMap = rowMap) tvSerie }) filterFlowFilter } //source building private def buildSource(inputFile: File): Source[Map[String, String], NotUsed] = { var datasource: Source[Map[String, String], NotUsed] = null if (!fileExists(inputFile.getPath)) { return null } datasource = Source .single(inputFile) .flatMapConcat((filename: File) => { FileIO.fromPath( Paths.get(filename.getPath) ) } ) .via(Compression.gunzip()) .via(CsvParsing.lineScanner(CsvParsing.Tab, CsvParsing.DoubleQuote, CsvParsing.DoubleQuote)) .via(CsvToMap.toMapAsStrings()) datasource } def buildAndValidateSource(inputFile: File): Source[Map[String, String], _] = { val source: Source[Map[String, String], _] = buildSource(inputFile = inputFile) if (source == null) { throw new FileNotFoundException(filename = inputFile.getPath) } source } //sinks building def buildMaxPersonsSink(): Sink[Person, Future[IndexedSeq[Person]]] = { val listPersonsSink: Sink[Person, Future[IndexedSeq[Person]]] = Sink.collection[Person, IndexedSeq[Person]] listPersonsSink } def buildMaxTvSeriesSink(): Sink[TvSerie, Future[IndexedSeq[TvSerie]]] = { val tvSeriesSink: Sink[TvSerie, Future[IndexedSeq[TvSerie]]] = Sink .collection[TvSerie, IndexedSeq[TvSerie]] tvSeriesSink } def buildMaxEpisodesSink(): Sink[Episode, Future[IndexedSeq[Episode]]] = { val episodeSink: Sink[Episode, Future[IndexedSeq[Episode]]] = Sink .collection[Episode, IndexedSeq[Episode]] episodeSink } }