diff --git a/.env b/.env index 66169c4..cd50678 100644 --- a/.env +++ b/.env @@ -1,5 +1,5 @@ PORT=8080 LOGGING_LEVEL=INFO -MONGODB_URI=mongodb://localhost:27017,localhost:27018,localhost:27019/electicity-prices +MONGODB_URI=mongodb://localhost:27017,localhost:27018,localhost:27019/electricity-prices SPRING_PROFILES_ACTIVE=price-sync SYNC_START_DATE=2023-01-01 \ No newline at end of file diff --git a/.version b/.version index 30f101c..d19d089 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -1.14.1 \ No newline at end of file +1.15.0 \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0d5627e --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +help: + @egrep -h '\s#@\s' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?#@ "}; {printf "\033[36m %-30s\033[0m %s\n", $$1, $$2}' + +build-local: #@ Build local + ./gradlew clean build +build-image: #@ Build docker image + docker build -t electricity-prices . --load \ No newline at end of file diff --git a/src/main/kotlin/ie/daithi/electricityprices/service/AlexSkillService.kt b/src/main/kotlin/ie/daithi/electricityprices/service/AlexSkillService.kt index 9e6047f..f6de95e 100644 --- a/src/main/kotlin/ie/daithi/electricityprices/service/AlexSkillService.kt +++ b/src/main/kotlin/ie/daithi/electricityprices/service/AlexSkillService.kt @@ -6,6 +6,7 @@ import ie.daithi.electricityprices.utils.* import org.apache.logging.log4j.LogManager import org.springframework.context.MessageSource import org.springframework.stereotype.Service +import java.time.LocalDate import java.time.LocalDateTime import java.util.* import kotlin.math.roundToInt @@ -27,7 +28,7 @@ class AlexSkillService(private val priceService: PriceService, private val messa } // Today's price and rating - val thirtyDayAverage = priceService.getThirtyDayAverage() + val thirtyDayAverage = priceService.getThirtyDayAverage(now.toLocalDate()) responses.add(getTodayRating(now, pricesToday, thirtyDayAverage, locale)) // Get next good 3-hour period @@ -50,7 +51,7 @@ class AlexSkillService(private val priceService: PriceService, private val messa fun getTodayRating( dateTime: LocalDateTime = LocalDateTime.now(), pricesToday: List = priceService.getPrices(dateTime.toLocalDate()), - thirtyDayAverage: Double = priceService.getThirtyDayAverage(), + thirtyDayAverage: Double = priceService.getThirtyDayAverage(dateTime.toLocalDate()), locale: Locale ): String { val dailyAverage = calculateAverage(pricesToday) @@ -73,7 +74,7 @@ class AlexSkillService(private val priceService: PriceService, private val messa fun getTomorrowRating( dateTime: LocalDateTime = LocalDateTime.now().plusDays(1), pricesTomorrow: List = priceService.getPrices(dateTime.toLocalDate()), - thirtyDayAverage: Double = priceService.getThirtyDayAverage(), + thirtyDayAverage: Double = priceService.getThirtyDayAverage(dateTime.toLocalDate()), locale: Locale ): Pair { @@ -217,7 +218,7 @@ class AlexSkillService(private val priceService: PriceService, private val messa fun getNextCheapPeriod( dateTime: LocalDateTime = LocalDateTime.now(), pricesToday: List = priceService.getPrices(dateTime.toLocalDate()), - thirtyDayAverage: Double = priceService.getThirtyDayAverage(), + thirtyDayAverage: Double = priceService.getThirtyDayAverage(dateTime.toLocalDate()), locale: Locale ): String { val cheapPeriods = getCheapPeriods(pricesToday, thirtyDayAverage) @@ -256,7 +257,7 @@ class AlexSkillService(private val priceService: PriceService, private val messa fun getNextExpensivePeriod( dateTime: LocalDateTime = LocalDateTime.now(), pricesToday: List = priceService.getPrices(dateTime.toLocalDate()), - thirtyDayAverage: Double = priceService.getThirtyDayAverage(), + thirtyDayAverage: Double = priceService.getThirtyDayAverage(dateTime.toLocalDate()), locale: Locale ): String { val expensivePeriods = @@ -307,7 +308,7 @@ class AlexSkillService(private val priceService: PriceService, private val messa } fun getThirtyDayAverage( - thirtyDayAverage: Double = priceService.getThirtyDayAverage(), locale: Locale + thirtyDayAverage: Double = priceService.getThirtyDayAverage(LocalDate.now()), locale: Locale ): String { val thirtyDayAverageRounded = thirtyDayAverage.times(100).roundToInt() diff --git a/src/main/kotlin/ie/daithi/electricityprices/service/PriceService.kt b/src/main/kotlin/ie/daithi/electricityprices/service/PriceService.kt index 6e3338c..c26d2ec 100644 --- a/src/main/kotlin/ie/daithi/electricityprices/service/PriceService.kt +++ b/src/main/kotlin/ie/daithi/electricityprices/service/PriceService.kt @@ -22,28 +22,32 @@ class PriceService( private val logger = LogManager.getLogger(this::class.simpleName) + fun getTodayPrices(): List { + logger.info("Getting today's prices") + val now = LocalDate.now() + return getPrices(now) + } + fun getPrices(date: LocalDate): List { logger.info("Getting prices for $date") - val startDate = date.atStartOfDay().minusSeconds(1) - val endDate = date.plusDays(1).atStartOfDay().minusSeconds(1) - return priceRepo.dateTimeBetween(startDate, endDate) + val start = date.atStartOfDay().minusSeconds(1) + val end = date.plusDays(1).atStartOfDay().minusSeconds(1) + return getPrices(start, end) } - fun getPrices(start: String?, end: String?): List { - val today = LocalDate.now() - val startDate = (start?.let { LocalDate.parse(it, dateFormatter) } ?: today).atStartOfDay().minusSeconds(1) - val endDate = - (end?.let { LocalDate.parse(it, dateFormatter) } ?: today).plusDays(1).atStartOfDay().minusSeconds(1) + fun getPrices(start: LocalDateTime, end: LocalDateTime): List { + logger.info("Getting prices between $start and $end") + return priceRepo.dateTimeBetween(start, end) + } - logger.info("Getting prices between $startDate and $endDate") - return priceRepo.dateTimeBetween(startDate, endDate) + fun getTodayPriceInfo(): DailyPriceInfo { + return getDailyPriceInfo(LocalDate.now()) } - fun getDailyPriceInfo(dateStr: String?): DailyPriceInfo? { - val date = dateStr?.let { LocalDate.parse(it, dateFormatter) } ?: LocalDate.now() - val prices = getPrices(start = dateStr, end = dateStr) - if (prices.isEmpty()) return null - val thirtyDayAverage: Double = getThirtyDayAverage(date.atStartOfDay()) + fun getDailyPriceInfo(date: LocalDate): DailyPriceInfo { + val prices = getPrices(date) + if (prices.isEmpty()) throw DataNotAvailableYetException("Data is not available yet for $date") + val thirtyDayAverage: Double = getThirtyDayAverage(date) val cheapestPeriods = getCheapPeriods(prices, thirtyDayAverage) val expensivePeriods = getExpensivePeriods(prices, thirtyDayAverage) @@ -97,8 +101,10 @@ class PriceService( } } - fun getThirtyDayAverage(date: LocalDateTime? = LocalDateTime.now()): Double { - return getPrices(start = date?.toLocalDate()?.minusDays(30)?.format(dateFormatter), end = null) + fun getThirtyDayAverage(date: LocalDate): Double { + val start = date.atStartOfDay().minusSeconds(30) + val end = date.plusDays(1).atStartOfDay().minusSeconds(1) + return getPrices(start, end) .map { it.price }.average() } diff --git a/src/main/kotlin/ie/daithi/electricityprices/web/controller/PriceController.kt b/src/main/kotlin/ie/daithi/electricityprices/web/controller/PriceController.kt index 72779ea..7d605dc 100644 --- a/src/main/kotlin/ie/daithi/electricityprices/web/controller/PriceController.kt +++ b/src/main/kotlin/ie/daithi/electricityprices/web/controller/PriceController.kt @@ -5,6 +5,7 @@ import ie.daithi.electricityprices.exceptions.UnprocessableEntityException import ie.daithi.electricityprices.model.DailyPriceInfo import ie.daithi.electricityprices.model.Price import ie.daithi.electricityprices.service.PriceService +import ie.daithi.electricityprices.utils.dateFormatter import ie.daithi.electricityprices.web.validaton.ValidDateDay import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.Parameter @@ -18,6 +19,7 @@ import io.swagger.v3.oas.annotations.tags.Tag import org.springframework.http.HttpStatus import org.springframework.validation.annotation.Validated import org.springframework.web.bind.annotation.* +import java.time.LocalDate @Validated @RestController @@ -30,23 +32,14 @@ class PriceController( @GetMapping("/price") @ResponseStatus(value = HttpStatus.OK) @Operation( - summary = "Get price info", description = "Returns price info within the range provided. " + - "If no start date is provided the default is the start of the current day. If no end date is " + - "provided the default is the end of today. Dates should be given in a string form yyyy-MM-dd" + summary = "Get price info", description = "Returns price info for the day provided. If no day is provided it " + + "defaults to today. The day should be given in a string form yyyy-MM-dd" ) @Parameter( `in` = ParameterIn.QUERY, - name = "start", - schema = Schema(type = "string", pattern = "\\d{4}-\\d{2}-\\d{2}"), - description = "Start date for query (defaults to today)", - required = false, - example = "2023-08-30" - ) - @Parameter( - `in` = ParameterIn.QUERY, - name = "end", + name = "date", schema = Schema(type = "string", pattern = "\\d{4}-\\d{2}-\\d{2}"), - description = "End date for query (defaults to today)", + description = "The date to query (defaults to today)", required = false, example = "2023-08-30" ) @@ -74,10 +67,10 @@ class PriceController( ) @ResponseBody fun getPrices( - @ValidDateDay @RequestParam(required = false) start: String?, - @ValidDateDay @RequestParam(required = false) end: String? + @ValidDateDay @RequestParam(required = false) date: String?, ): List { - return priceSerice.getPrices(start, end) + date ?: return priceSerice.getTodayPrices() + return priceSerice.getPrices(LocalDate.parse(date, dateFormatter)) } @GetMapping("/price/dailyinfo") @@ -135,6 +128,7 @@ class PriceController( ) @ResponseBody fun getDailyPriceInfo(@ValidDateDay @RequestParam(required = false) date: String?): DailyPriceInfo { - return priceSerice.getDailyPriceInfo(date) ?: throw DataNotAvailableYetException("No data available for $date") + date ?: return priceSerice.getTodayPriceInfo() + return priceSerice.getDailyPriceInfo(LocalDate.parse(date, dateFormatter)) } } \ No newline at end of file diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index b46a553..e5cf5b5 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -8,7 +8,11 @@ spring: profiles: active: ${SPRING_PROFILES_ACTIVE:prod} - jackson.default-property-inclusion: non_null + jackson: + default-property-inclusion: non_null + serialization: + WRITE_DATES_AS_TIMESTAMPS: false + WRITE_DATES_WITH_ZONE_ID: true mvc.pathmatch.matching-strategy: ant_path_matcher data.mongodb.uri: ${MONGODB_URI:mongodb://localhost:27017/electricity-prices} diff --git a/src/test/kotlin/ie/daithi/electricityprices/service/PriceServiceTest.kt b/src/test/kotlin/ie/daithi/electricityprices/service/PriceServiceTest.kt index cde9ddc..ed93171 100644 --- a/src/test/kotlin/ie/daithi/electricityprices/service/PriceServiceTest.kt +++ b/src/test/kotlin/ie/daithi/electricityprices/service/PriceServiceTest.kt @@ -47,8 +47,8 @@ class PriceServiceTest { @Test fun `getPrices - start and end`() { - val start = "2023-08-25" - val end = "2023-08-26" + val start = LocalDateTime.of(2023, 8, 24, 23, 59, 59) + val end = LocalDateTime.of(2023, 8, 26, 23, 59, 59) val mockResponse: List = listOf(Price(LocalDateTime.now(), 1.0)) // Mocking the method @@ -60,8 +60,8 @@ class PriceServiceTest { // Verify the call to dateTimeBetween with specific arguments verify { priceRepo.dateTimeBetween( - LocalDate.parse(start, dateFormatter).atStartOfDay().minusSeconds(1), - LocalDate.parse(end, dateFormatter).plusDays(1).atStartOfDay().minusSeconds(1) + start, + end ) }