diff --git a/src/main/scala/db/DataService.scala b/src/main/scala/db/DataService.scala index 3340802..3710ccd 100644 --- a/src/main/scala/db/DataService.scala +++ b/src/main/scala/db/DataService.scala @@ -81,16 +81,21 @@ class DataService private( def readFile(fileName: String): IO[List[String]] = fileService.readFile(fileName) + def getDateTimeEntries(dateTime: LocalDateTime): IO[List[String]] = postgresService.getDateTimeEntries(dateTime) + def getInRange(from: LocalDateTime, to: LocalDateTime): IO[List[String]] = fileService.getInRange(from, to) def getDates: IO[List[LocalDate]] = fileService.getDates def getDatesByMonths(monthList: List[LocalDate]): IO[List[LocalDate]] = { - fileService.getDatesByMonths(monthList) -// postgresService.getDatesByMonths(monthList) +// fileService.getDatesByMonths(monthList) + postgresService.getDatesByMonths(monthList) } - def getDateFileNames(date: LocalDate): IO[List[String]] = fileService.getDateFileNames(date) + def getDateFileNames(date: LocalDate): IO[List[String]] = { +// fileService.getDateFileNames(date) + postgresService.getDateFileNames(date) + } // TODO implement getting full data from state // def getLast24Hours: IO[List[String]] = { diff --git a/src/main/scala/db/Main.scala b/src/main/scala/db/Main.scala index 1b17ba2..8b2349e 100644 --- a/src/main/scala/db/Main.scala +++ b/src/main/scala/db/Main.scala @@ -6,6 +6,9 @@ import doobie._ import doobie.implicits._ import doobie.postgres.implicits._ +import java.time.{LocalDate, LocalDateTime} +import java.time.format.DateTimeFormatter + object Main { def transactor[F[_]: Async]: Transactor[F] = Transactor.fromDriverManager[F]( "org.postgresql.Driver", @@ -15,13 +18,21 @@ object Main { ) def main(args: Array[String]): Unit = { -// val xa = transactor[IO] + val xa = transactor[IO] +// val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") +// val localDate = LocalDate.parse("2023-12-10", formatter) // val result = for { // postgresService <- PostgresService.of(xa) -// re <- postgresService.selectWeatherTable +// re <- postgresService.getDateFileNames(localDate) // } yield re - println(System.getenv("DATABASE_URL")) + val formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmm") + val dateTime = LocalDateTime.parse("20231210_1300", formatter) + val result = for { + postgresService <- PostgresService.of(xa) + re <- postgresService.getDateTimeEntries(dateTime) + } yield re + println(result.unsafeRunSync().toString()) // val result = createWeatherTable(xa).unsafeRunSync() // val result = insertInWeatherTable(xa).unsafeRunSync() diff --git a/src/main/scala/db/PostgresService.scala b/src/main/scala/db/PostgresService.scala index ee76f36..88eb89d 100644 --- a/src/main/scala/db/PostgresService.scala +++ b/src/main/scala/db/PostgresService.scala @@ -7,7 +7,7 @@ import doobie._ import doobie.implicits._ import java.time.format.DateTimeFormatter -import java.time.{LocalDate, LocalDateTime, ZoneId, ZonedDateTime} +import java.time.{LocalDate, LocalDateTime, OffsetDateTime, ZoneId, ZonedDateTime} import doobie.postgres.implicits._ import org.typelevel.log4cats.Logger import org.typelevel.log4cats.slf4j.Slf4jLogger @@ -64,7 +64,40 @@ class PostgresService(transactor: Transactor[IO], log: Logger[IO]) extends DataS def getDates: IO[List[LocalDate]] = ??? - def getDateFileNames(date: LocalDate): IO[List[String]] = ??? + def getDateTimeEntries(dateTime: LocalDateTime): IO[List[String]] = { + fr""" + SELECT datetime || ';' || city || ';' || COALESCE(tempmax::text, '' ) || ';' || COALESCE(tempmin::text, '' ) || ';' || COALESCE(tempavg::text, '' ) || ';' || COALESCE(precipitation::text, '' ) || ';' || COALESCE(windavg::text, '' ) || ';' || COALESCE(windmax::text, '' ) || ';' || COALESCE(tempmax::text, '' ) || ';' || COALESCE(visibilitymin::text, '' ) || ';' || COALESCE(visibilityavg::text, '' ) || ';' || COALESCE(snowavg::text, '' ) || ';' || COALESCE(atmpressure::text, '' ) || ';' || COALESCE(dewpoint::text, '' ) || ';' || COALESCE(humidity::text, '' ) || ';' || COALESCE(sunduration::text, '' ) || ';' || COALESCE(phenomena::text, '' ) + FROM weather + WHERE dateTime = $dateTime + ORDER BY city + """ + .query[String] + .to[List] + .transact(transactor) + } + + def getDateFileNames(date: LocalDate): IO[List[String]] = { + val year = date.atStartOfDay.atZone(rigaZone).getYear + val month = date.atStartOfDay.atZone(rigaZone).getMonthValue + val day = date.atStartOfDay.atZone(rigaZone).getDayOfMonth + val whereClauses = fr"(EXTRACT(DAY FROM dateTime) = $day AND EXTRACT(MONTH FROM dateTime) = $month AND EXTRACT(YEAR FROM dateTime) = $year)" + val formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HH00") + + ( + fr""" + SELECT DISTINCT dateTime + FROM weather + WHERE """ ++ whereClauses ++ + fr"ORDER BY dateTime" + ) + .query[OffsetDateTime] + .to[List] + .transact(transactor) + .map { dateTimes => { + dateTimes.map(_.format(formatter)) + } + } + } def getResourceContent(path: String): IO[String] = { val streamResource = Resource.make(IO(getClass.getResourceAsStream(path))) { stream => IO(stream.close()).handleErrorWith(_ => IO.unit) @@ -85,7 +118,6 @@ class PostgresService(transactor: Transactor[IO], log: Logger[IO]) extends DataS implicit val doubleOptionMeta: Meta[Option[Double]] = Meta[Double].imap(Option(_))(_.getOrElse(Double.NaN)) def insertInWeatherTable(data: List[WeatherStationData]): IO[Int] = { -// IO.blocking { getResourceContent("/db/insert_weather_table.sql").flatMap { insertTableSql => val insertData = data.map(line => { val zonedTime: ZonedDateTime = line.timestamp.atZone(rigaZone) @@ -97,7 +129,6 @@ class PostgresService(transactor: Transactor[IO], log: Logger[IO]) extends DataS .updateMany(insertData) .transact(transactor) } -// }.flatten } def selectWeatherTable(): IO[List[(String, Option[Double])]] = { diff --git a/src/main/scala/server/Server.scala b/src/main/scala/server/Server.scala index 4a9369f..486667f 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, ValidateMonths} +import server.ValidateRoutes.{AggKey, CityList, DateTimeRange, Granularity, ValidateDate, ValidateDateTime, ValidateMonths} import io.circe.{Json, Printer} import org.http4s._ import org.http4s.dsl.io._ @@ -61,7 +61,7 @@ class Server(dataService: DataService, fetch: FetchService, log: Logger[IO]) { .flatMap(responseWrapper => Ok(responseWrapper.asJson.pretty)) // http://0.0.0.0:8080/api/fetch/date/20230514 - case GET -> Root / "fetch" / "date" / ValidDate(date) => + case GET -> Root / "fetch" / "date" / ValidateDate(date) => val result = for { fetchResultEither <- fetch.fetchFromDate(date).attempt fetchServiceError = fetchResultEither.left.toOption.map(e => s"FetchServiceError: ${e.getMessage}").toList @@ -97,12 +97,16 @@ class Server(dataService: DataService, fetch: FetchService, log: Logger[IO]) { ) // http://0.0.0.0:8080/api/show/date/20230423 - case GET -> Root / "show" / "date" / ValidDate(date) => + case GET -> Root / "show" / "date" / ValidateDate(date) => dataService.getDateFileNames(date).flatMap(fileNames => Ok(fileNames.asJson.pretty) ) - // http://0.0.0.0:8080/api/show/file/20230423_12:30.csv + // http://0.0.0.0:8080/api/show/datetime/20230423_1300 + case GET -> Root / "show" / "datetime" / ValidateDateTime(datetime) => + dataService.getDateTimeEntries(datetime).flatMap(content => Ok(content.asJson)) + + // http://0.0.0.0:8080/api/show/file/20230423_12:30 case GET -> Root / "show" / "file" / (fileName: String) => dataService.readFile(fileName).flatMap(content => Ok(content.asJson)) diff --git a/src/main/scala/server/ValidateRoutes.scala b/src/main/scala/server/ValidateRoutes.scala index 3cc1d46..6f3193a 100644 --- a/src/main/scala/server/ValidateRoutes.scala +++ b/src/main/scala/server/ValidateRoutes.scala @@ -20,13 +20,20 @@ object ValidateRoutes { } } - object ValidDate { + object ValidateDate { def unapply(str: String): Option[LocalDate] = { val formatter = DateTimeFormatter.ofPattern("yyyyMMdd") Try(LocalDate.parse(str, formatter)).toOption } } + object ValidateDateTime { + def unapply(str: String): Option[LocalDateTime] = { + val formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmm") + Try(LocalDateTime.parse(str, formatter)).toOption + } + } + object ValidateMonths { def unapply(str: String): Option[List[LocalDate]] = { val formatter = DateTimeFormatter.ofPattern("yyyyMMdd") diff --git a/web/src/fileManager/FileContent.tsx b/web/src/fileManager/FileContent.tsx index 06fa0e5..3425792 100644 --- a/web/src/fileManager/FileContent.tsx +++ b/web/src/fileManager/FileContent.tsx @@ -6,7 +6,7 @@ export const FileContent: Component<{getFileName: Accessor}> = (props) = const fetchFileContent = async (fileName: string) => { if (fileName === "") return; await new Promise(resolve => setTimeout(resolve, 500)) - const response = await fetch(`${apiHost}/api/show/file/${fileName}`); + const response = await fetch(`${apiHost}/api/show/datetime/${fileName}`); const text = await response.json(); return text; }