From f1e68c6dba3615fbe31f64610703b0a2300781a9 Mon Sep 17 00:00:00 2001 From: Guntis Smaukstelis Date: Fri, 11 Aug 2023 16:22:15 +0300 Subject: [PATCH] Limit all dates asking to 3 months range --- src/main/scala/db/DBService.scala | 14 ++++++++++++++ src/main/scala/db/DataService.scala | 2 ++ src/main/scala/server/Server.scala | 8 +++++++- src/main/scala/server/ValidateRoutes.scala | 15 +++++++++++++++ src/test/scala/db/DBServiceSpec.scala | 21 +++++++++++++++++++++ web/src/fileManager/Calendar.tsx | 19 +++++++++++++------ web/src/fileManager/DateList.tsx | 19 ++++++++++++++----- 7 files changed, 86 insertions(+), 12 deletions(-) diff --git a/src/main/scala/db/DBService.scala b/src/main/scala/db/DBService.scala index 807a3af..1079025 100644 --- a/src/main/scala/db/DBService.scala +++ b/src/main/scala/db/DBService.scala @@ -81,6 +81,20 @@ class DBService(log: Logger[IO]) extends DataServiceTrait { } yield dates.sorted } + def getDatesByMonths(monthList: List[LocalDate]): IO[List[LocalDate]] = { + val dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd") + val monthFormatter = DateTimeFormatter.ofPattern("yyyyMM") + val monthStrList = monthList.map(_.format(monthFormatter)) + for { + fileNames <- readFileNames(dataPath) + datesStr <- IO(fileNames.map(_.take(8)).distinct) // take yyyyMMdd + filteredDatesStr = datesStr.filter(date => monthStrList.contains(date.take(6))) + dates <- filteredDatesStr.traverse { str => + IO(LocalDate.parse(str, dateFormatter)).option + }.map(_.flatten) + } yield dates.sorted + } + def getDateFileNames(date: LocalDate): IO[List[String]] = { val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd") val dateStr: String = date.format(formatter) diff --git a/src/main/scala/db/DataService.scala b/src/main/scala/db/DataService.scala index 1d17dda..2a0dc26 100644 --- a/src/main/scala/db/DataService.scala +++ b/src/main/scala/db/DataService.scala @@ -63,6 +63,8 @@ class DataService private( def getDates: IO[List[LocalDate]] = dbService.getDates + def getDatesByMonths(monthList: List[LocalDate]): IO[List[LocalDate]] = dbService.getDatesByMonths(monthList) + def getDateFileNames(date: LocalDate): IO[List[String]] = dbService.getDateFileNames(date) // TODO implement getting full data from state diff --git a/src/main/scala/server/Server.scala b/src/main/scala/server/Server.scala index cbf538a..ff2a6ca 100644 --- a/src/main/scala/server/Server.scala +++ b/src/main/scala/server/Server.scala @@ -6,7 +6,7 @@ import com.comcast.ip4s.IpLiteralSyntax import db.DataService import fetch.FetchService import parse.{Aggregate, Parser, WeatherData} -import server.ValidateRoutes.{AggKey, CityList, DateTimeRange, Granularity, ValidDate} +import server.ValidateRoutes.{AggKey, CityList, DateTimeRange, Granularity, ValidDate, ValidateMonths} import io.circe.{Json, Printer} import org.http4s._ import org.http4s.dsl.io._ @@ -89,6 +89,12 @@ class Server(dataService: DataService, fetch: FetchService, log: Logger[IO]) { Ok(dates.asJson.pretty) ) + // http://0.0.0.0:8080/api/show/months/202304,202305,202306 + case GET -> Root / "show" / "months" / ValidateMonths(monthList) => + dataService.getDatesByMonths(monthList).flatMap(dates => + Ok(dates.asJson.pretty) + ) + // http://0.0.0.0:8080/api/show/date/20230423 case GET -> Root / "show" / "date" / ValidDate(date) => dataService.getDateFileNames(date).flatMap(fileNames => diff --git a/src/main/scala/server/ValidateRoutes.scala b/src/main/scala/server/ValidateRoutes.scala index 010fb1e..3cc1d46 100644 --- a/src/main/scala/server/ValidateRoutes.scala +++ b/src/main/scala/server/ValidateRoutes.scala @@ -27,6 +27,21 @@ object ValidateRoutes { } } + object ValidateMonths { + def unapply(str: String): Option[List[LocalDate]] = { + val formatter = DateTimeFormatter.ofPattern("yyyyMMdd") + val monthList = str + .split(",") + .toList + .flatMap(str => Try(LocalDate.parse(str + "01", formatter)).toOption) + + monthList match { + case Nil => None + case list => Some(list) + } + } + } + object CityList { def unapply(str: String): Option[List[String]] = { str.split(",").toList match { diff --git a/src/test/scala/db/DBServiceSpec.scala b/src/test/scala/db/DBServiceSpec.scala index 16f8eaa..850555a 100644 --- a/src/test/scala/db/DBServiceSpec.scala +++ b/src/test/scala/db/DBServiceSpec.scala @@ -34,6 +34,27 @@ class DBServiceSpec extends AnyFunSuite with Matchers { lines.length shouldBe 2142 } + test("getDatesByMonths should return filtered dates") { + val monthFormatter = DateTimeFormatter.ofPattern("yyyyMMdd") + val monthList = List( + LocalDate.parse("20230401", monthFormatter), + LocalDate.parse("20230501", monthFormatter), + LocalDate.parse("20230601", monthFormatter), + ) + val dbService = DBService.of.unsafeRunSync() + val dates = dbService.getDatesByMonths(monthList).unsafeRunSync() + + dates should not be empty + + val exportFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") + val validPrefixes = Set("2023-04", "2023-05", "2023-06") + val invalidDates = dates + .map(_.format(exportFormatter)) + .filterNot(date => validPrefixes.exists(prefix => date.startsWith(prefix))) + + invalidDates shouldBe empty + } + test("DBService.save should return correct result") { val dbService = DBService.of.unsafeRunSync() val fileName = "testFile.txt" diff --git a/web/src/fileManager/Calendar.tsx b/web/src/fileManager/Calendar.tsx index f846162..c217ead 100644 --- a/web/src/fileManager/Calendar.tsx +++ b/web/src/fileManager/Calendar.tsx @@ -1,24 +1,31 @@ import moment from "moment"; -import { Accessor, Component, createSignal, Setter } from "solid-js"; +import { Accessor, Component, Setter } from "solid-js"; import "../css/calendar.css"; export const Calendar: Component<{ getSelectedDate: Accessor, setSelectedDate: Setter, - datesWithData: Date[], -}> = ({getSelectedDate, setSelectedDate, datesWithData}) => { - const dateStrArr = () => datesWithData.map(d => d.toDateString()); + setCurrentMonth: Setter, + datesWithData: () => Date[], +}> = ({getSelectedDate, setSelectedDate, setCurrentMonth, datesWithData}) => { + const dateStrArr = () => datesWithData().map(d => d.toDateString()); + + function changeMonths(delta: number): void { + const newDate = moment(getSelectedDate()).add(delta, "months").toDate(); + setCurrentMonth(newDate); + setSelectedDate(newDate); + } return (

Calendar

- +
{moment(getSelectedDate()).format("YYYY, MMM")}
- +
{ getPaddedMonth(getSelectedDate()).map(d => { diff --git a/web/src/fileManager/DateList.tsx b/web/src/fileManager/DateList.tsx index 589dd62..8ca7f15 100644 --- a/web/src/fileManager/DateList.tsx +++ b/web/src/fileManager/DateList.tsx @@ -1,18 +1,26 @@ -import { Accessor, Component, createResource, Setter } from "solid-js"; +import moment from "moment"; +import { Accessor, Component, createResource, createSignal, Setter } from "solid-js"; + import { apiHost } from "../consts"; import "../css/spinner.css"; import { Calendar } from "./Calendar"; export const DateList: Component<{getDate: Accessor, setDate: Setter;}> = (props) => { + const [getCurrentMonth, setCurrentMonth] = createSignal(new Date()); const fetchDates = async () => { + const months = [ + moment(getCurrentMonth()).format("yyyyMM"), + moment(getCurrentMonth()).subtract(1, "months").format("yyyyMM"), + moment(getCurrentMonth()).add(1, "months").format("yyyyMM"), + ]; await new Promise(resolve => setTimeout(resolve, 500)) - const response = await fetch(`${apiHost}/api/show/all_dates`); + const response = await fetch(`${apiHost}/api/show/months/${months.join(",")}`); const json = await response.json(); return json; } - const [datesResource] = createResource(fetchDates); + const [datesResource] = createResource(getCurrentMonth, fetchDates); return (
@@ -25,10 +33,11 @@ export const DateList: Component<{getDate: Accessor, setDate: Setter { datesResource.error && (
Error while loading dates: ${datesResource.error}
)} - { datesResource() && new Date(str))} + setCurrentMonth={setCurrentMonth} + datesWithData={() => datesResource().map((str: string) => new Date(str))} /> } {/* Backup date view */}