diff --git a/src/main/scala/UI.scala b/src/main/scala/UI.scala index 299a83f..9341e03 100644 --- a/src/main/scala/UI.scala +++ b/src/main/scala/UI.scala @@ -9,117 +9,15 @@ import PowerTemperatureAnalysis._ */ object UI { - /** Menu options with their associated actions to perform - * @return - * a chunk of tuples (name, action), action being a function that takes LoadedData and returns a ZIO effect that will return Unit - */ - val menuOptions: Chunk[(String, (LoadedData) => ZIO[Any, Any, Unit])] = Chunk( - "Get stats for a specific day" -> ((data) => - for { - _ <- printLine("Please enter a date (dd/MM/yyyy): ") - date <- readDate - _ <- printLine(s"Stats for $date:") - } yield () - ), - "Global statistics for a given period" -> ((data) => - for { - _ <- printLine("Please enter a start date (dd/MM/yyyy): ") - startDate <- readDate - _ <- printLine("Please enter an end date (dd/MM/yyyy): ") - endDate <- readDate - _ <- printLine( - GlobalStatisticsAnalysis.formatStatisticsTable( - "Environmental impact of electricity production and consumption", - GlobalStatisticsAnalysis.getCarbonIntensityStatistics(data, startDate, endDate), - s"Data between $startDate and $endDate" - ) - ) - _ <- printLine( - "\n\n" + - GlobalStatisticsAnalysis.formatStatisticsTable( - "Electricty consumption and temperature", - GlobalStatisticsAnalysis.getTemperatureAndConsumptionStatistics(data, startDate, endDate), - s"Data between $startDate and $endDate" - ) - ) - _ <- printLine( - "\n\n" + - GlobalStatisticsAnalysis.formatStatisticsTable( - "Production (MW) by supply chain", - GlobalStatisticsAnalysis.getProductionBySupplyChain(data, startDate, endDate), - s"Data between $startDate and $endDate" - ) - ) - } yield () - ), - "Case study: Temperature vs power peak" -> ((data) => printLine(formatPowerPeakAndTemperature(data))) - ) - - /** Retrieves the information about maximum power peak and minimum temperature entries for each year and formats it into a newline-separated string. - * - * @param data - * The loaded data containing daily power peak with temperature information. - * @return - * A formatted string containing information about maximum power peak and minimum temperature entries for each year, with each entry on a new line. - */ - def formatPowerPeakAndTemperature(data: LoadedData): String = { - val title = "+-------------------------------------------------------------------------+\n" + - "| Case Study : Power and Temperature Summary |\n" + - "+-------------------------------------------------------------------------+\n" + - "| We want to know the maximum power peak and the minimum temperature for |\n" + - "| each year. |\n" + - "+-------------------------------------------------------------------------+\n" - - val output = powerPeakTemperatureGroupedByYear(data) - .flatMap { - case (year, yearData) => { - val (maxPowerEntry, minTemperatureEntry) = maxPowerPeakAndMinTemperature(yearData) - Some( - f"| Year $year |\n" + - "+-------------------------------------------------------------------------+\n" + - f"| Max Power Peak: ${maxPowerEntry.powerPeak}%10.2f MW on ${maxPowerEntry.date} |\n" + - f"| Min Temperature: ${minTemperatureEntry.meanTemperature}%10.2f C on ${minTemperatureEntry.date} |\n" + - "+-------------------------------------------------------------------------+\n".stripMargin.trim - ) - } - } - .mkString("\n") - - val pearsonCoef = temperatureAndPowerPeakPearsonCorrelation(data.dailyPowerPeakWithTemperature) - val pearsonCoefFormatted = - f"\n| Temperature And PowerPeak Pearson Correlation: $pearsonCoef%5.2f |\n" + - "+-------------------------------------------------------------------------+\n" - - val conclusion = - "| Final Conclusion |\n" + - "+-------------------------------------------------------------------------+\n" + - "| - The correlation coefficient indicates a strong correlation. |\n" + - "| - As temperatures decrease, power peak tends to increase. |\n" + - "| - People tend to use more electricity for heating purposes when |\n" + - "| temperatures fall |\n" + - "+-------------------------------------------------------------------------+\n" - - title + output + pearsonCoefFormatted + conclusion - } - - val choiceMenu: String = - "Welcome to our energy analysis tool!\n\nHere are all the interesting interactions you can have with it:\n" - + menuOptions.zipWithIndex - .map { case ((name, _), index) => - s"${index + 1}. $name" - } - .mkString("\n") - + "\n\nPlease enter your choice: " - /** Main ZIO console loop * @return * a ZIO effect that will return Unit */ def consoleLoop(data: LoadedData): ZIO[Any, Any, Unit] = { for { - _ <- printLine(choiceMenu) + _ <- printLine(menuString) input <- readLine - _ <- menuOptions.lift(input.toInt - 1) match { + _ <- menuChoices.lift(input.toInt - 1) match { case Some((_, action)) => action(data) case None => printLineError("Invalid choice") } @@ -127,6 +25,30 @@ object UI { } yield () } + /** Menu options with their associated actions to perform + * @return + * a chunk of tuples (name, action), action being a function that takes LoadedData and returns a ZIO effect that will return Unit + */ + val menuChoices: Chunk[(String, (LoadedData) => ZIO[Any, Any, Unit])] = + Chunk( + ("Get stats for a specific day", printDailyStats), + ("Global statistics for a given period", printGlobalStats), + ("Case study: Temperature vs power peak", printCaseStudy) + ) + + /** Menu string + * @return + * a String containing the menu and its options + */ + val menuString: String = + "Welcome to our energy analysis tool!\n\nHere are all the interesting interactions you can have with it:\n" + + menuChoices.zipWithIndex + .map { case ((name, _), index) => + s"${index + 1}. $name" + } + .mkString("\n") + + "\n\nPlease enter your choice: " + /** Reads a date from the console * @return * a ZIO effect that will return a LocalDate @@ -139,4 +61,36 @@ object UI { case None => printLineError("You entered an invalid date, retry.\n") *> readDate } } yield date + + def printDailyStats(data: LoadedData): ZIO[Any, Any, Unit] = { + for { + _ <- printLine("Please enter a date (dd/MM/yyyy): ") + date <- readDate + _ <- printLine(s"Stats for $date:") + } yield () + } + + def printGlobalStats(data: LoadedData): ZIO[Any, Any, Unit] = { + for { + _ <- printLine("Please enter a start date (dd/MM/yyyy): ") + startDate <- readDate + _ <- printLine("Please enter an end date (dd/MM/yyyy): ") + endDate <- readDate + + _ <- printLine("\n\n" + GlobalStatisticsAnalysis.carbonIntensityTab(data, startDate, endDate)) + _ <- printLine("\n\n" + GlobalStatisticsAnalysis.productionBySupplyChainTab(data, startDate, endDate)) + _ <- printLine("\n\n" + GlobalStatisticsAnalysis.consumptionAndTemperatureTab(data, startDate, endDate) + "\n") + } yield () + } + + def printCaseStudy(data: LoadedData): ZIO[Any, Any, Unit] = { + for { + _ <- printLine( + formatPowerPeakAndTemperature( + maxPowerPeaksAndMinTemperaturesByYear(data), + temperatureAndPowerPeakPearsonCorrelation(data) + ) + ) + } yield () + } } diff --git a/src/main/scala/analysis/GlobalStatisticsAnalysis.scala b/src/main/scala/analysis/GlobalStatisticsAnalysis.scala index 4a11b1a..1a140c1 100644 --- a/src/main/scala/analysis/GlobalStatisticsAnalysis.scala +++ b/src/main/scala/analysis/GlobalStatisticsAnalysis.scala @@ -62,7 +62,7 @@ object GlobalStatisticsAnalysis { val count = values.size Statistics(fieldName, average, stdDev, min, max, count) - }.toList + }.toList } /** Get a formatted table of statistics. @@ -215,4 +215,58 @@ object GlobalStatisticsAnalysis { end ) } + + /** Get a formatted table of statistics about the carbon intensity of electricity production and consumption, given a period of time. + * + * @param data + * The data to analyse. + * @param startDate + * The start date of the period. + * @param endDate + * The end date of the period. + * @return + * A formatted table + */ + def carbonIntensityTab(data: LoadedData, startDate: LocalDate, endDate: LocalDate): String = + GlobalStatisticsAnalysis.formatStatisticsTable( + "Environmental impact of electricity production and consumption", + GlobalStatisticsAnalysis.getCarbonIntensityStatistics(data, startDate, endDate), + s"Data between $startDate and $endDate" + ) + + /** Get a formatted table of statistics about the temperature and power consumption, given a period of time. + * + * @param data + * The data to analyse. + * @param startDate + * The start date of the period. + * @param endDate + * The end date of the period. + * @return + * A formatted table + */ + def consumptionAndTemperatureTab(data: LoadedData, startDate: LocalDate, endDate: LocalDate): String = + GlobalStatisticsAnalysis.formatStatisticsTable( + "Electricty consumption and temperature", + GlobalStatisticsAnalysis.getTemperatureAndConsumptionStatistics(data, startDate, endDate), + s"Data between $startDate and $endDate" + ) + + /** Get a formatted table of statistics about the production by supply chain, given a period of time. + * + * @param data + * The data to analyse. + * @param startDate + * The start date of the period. + * @param endDate + * The end date of the period. + * @return + * A formatted table + */ + def productionBySupplyChainTab(data: LoadedData, startDate: LocalDate, endDate: LocalDate): String = + GlobalStatisticsAnalysis.formatStatisticsTable( + "Production (MW) by supply chain", + GlobalStatisticsAnalysis.getProductionBySupplyChain(data, startDate, endDate), + s"Data between $startDate and $endDate" + ) } diff --git a/src/main/scala/analysis/PowerTemperatureAnalysis.scala b/src/main/scala/analysis/PowerTemperatureAnalysis.scala index 2ed965e..0cd0b42 100644 --- a/src/main/scala/analysis/PowerTemperatureAnalysis.scala +++ b/src/main/scala/analysis/PowerTemperatureAnalysis.scala @@ -12,8 +12,8 @@ object PowerTemperatureAnalysis { * @return * A Map where the key is the year and the value is a List of daily power peak with temperature entries for that year. */ - def powerPeakTemperatureGroupedByYear(data: LoadedData): Map[Int, List[DailyPowerPeakWithTemperature]] = { - data.dailyPowerPeakWithTemperature.toList.groupBy(pp => pp.date.getYear) + def powerPeakTemperatureGroupedByYear(data: LoadedData): Map[Int, Chunk[DailyPowerPeakWithTemperature]] = { + data.dailyPowerPeakWithTemperature.groupBy(pp => pp.date.getYear) } /** Finds the maximum power peak and minimum temperature entries in the given list of daily power peak with temperature data for a specific year. @@ -23,9 +23,27 @@ object PowerTemperatureAnalysis { * @return * A tuple containing the maximum power peak entry and the minimum temperature entry for the specified year. */ - def maxPowerPeakAndMinTemperature(yearData: List[DailyPowerPeakWithTemperature]): (DailyPowerPeakWithTemperature, DailyPowerPeakWithTemperature) = + def maxPowerPeakAndMinTemperatureOfYear(yearData: Chunk[DailyPowerPeakWithTemperature]): (DailyPowerPeakWithTemperature, DailyPowerPeakWithTemperature) = (yearData.maxBy(_.powerPeak), yearData.minBy(_.meanTemperature)) + /** Retrieves the information about maximum power peak and minimum temperature entries for each year. + * + * @param data + * The loaded data containing daily power peak with temperature information. + * @return + * A chunk of tuples containing the year, the maximum power peak entry for that year and the minimum temperature entry for that year. + */ + def maxPowerPeaksAndMinTemperaturesByYear(data: LoadedData): Chunk[(Int, DailyPowerPeakWithTemperature, DailyPowerPeakWithTemperature)] = + Chunk + .fromIterable( + powerPeakTemperatureGroupedByYear(data) + .map((year, yearData) => { + val (maxPowerPeak, minTemperature) = maxPowerPeakAndMinTemperatureOfYear(yearData) + (year, maxPowerPeak, minTemperature) + }) + ) + .sortBy(_._1) + /** Calculates the Pearson correlation coefficient between the given power peaks and temperatures. * * @param data @@ -33,8 +51,49 @@ object PowerTemperatureAnalysis { * @return * The Pearson correlation coefficient between the given power peaks and temperatures. */ - def temperatureAndPowerPeakPearsonCorrelation(data: Chunk[DailyPowerPeakWithTemperature]): Double = - val temperatures: Chunk[Temperature.Celsius] = data.map(line => line.meanTemperature) - val powerPeaks: Chunk[Power.MW] = data.map(line => line.powerPeak) + def temperatureAndPowerPeakPearsonCorrelation(data: LoadedData): Double = + val temperatures: Chunk[Temperature.Celsius] = data.dailyPowerPeakWithTemperature.map(line => line.meanTemperature) + val powerPeaks: Chunk[Power.MW] = data.dailyPowerPeakWithTemperature.map(line => line.powerPeak) linearCorrelationCoefficient(powerPeaks, temperatures) + + /** Retrieves the information about maximum power peak and minimum temperature entries for each year and formats it into a newline-separated string. + * + * @param data + * The loaded data containing daily power peak with temperature information. + * @return + * A formatted string containing information about maximum power peak and minimum temperature entries for each year, with each entry on a new line. + */ + def formatPowerPeakAndTemperature(yearsData: Chunk[(Int, DailyPowerPeakWithTemperature, DailyPowerPeakWithTemperature)], correlationCoef: Double): String = { + val title = "+-------------------------------------------------------------------------+\n" + + "| Case Study : Power and Temperature Summary |\n" + + "+-------------------------------------------------------------------------+\n" + + "| We want to know the maximum power peak and the minimum temperature for |\n" + + "| each year. |\n" + + "+-------------------------------------------------------------------------+\n" + + val yearsSummaries = yearsData + .map((year, maxPowerEntry, minTemperatureEntry) => + f"| Year $year |\n" + + "+-------------------------------------------------------------------------+\n" + + f"| Max Power Peak: ${maxPowerEntry.powerPeak}%10.2f MW on ${maxPowerEntry.date} |\n" + + f"| Min Temperature: ${minTemperatureEntry.meanTemperature}%10.2f C on ${minTemperatureEntry.date} |\n" + + "+-------------------------------------------------------------------------+\n".stripMargin.trim + ) + .mkString("\n") + + val pearsonCoefFormatted = + f"\n| Temperature And PowerPeak Pearson Correlation: $correlationCoef%5.2f |\n" + + "+-------------------------------------------------------------------------+\n" + + val conclusion = + "| Final Conclusion |\n" + + "+-------------------------------------------------------------------------+\n" + + "| - The correlation coefficient indicates a strong enough correlation. |\n" + + "| - As daily temperatures decrease, daily power peak tends to increase. |\n" + + "| - People tend to use more electricity for heating purposes when |\n" + + "| temperatures fall |\n" + + "+-------------------------------------------------------------------------+\n" + + title + yearsSummaries + pearsonCoefFormatted + conclusion + } }