From 23c0990495019c03c49c3192cee77a06cce39a38 Mon Sep 17 00:00:00 2001 From: Guntis Smaukstelis Date: Thu, 8 Feb 2024 22:48:27 +0200 Subject: [PATCH] Query and display phenomena --- src/main/scala/db/Main.scala | 12 ++++----- src/main/scala/db/PostgresService.scala | 34 ++++++++++++++++++++++--- src/main/scala/parse/Aggregate.scala | 4 ++- web/src/cities/result/GridResult.tsx | 15 ++++++----- 4 files changed, 48 insertions(+), 17 deletions(-) diff --git a/src/main/scala/db/Main.scala b/src/main/scala/db/Main.scala index 680982e..59a42d9 100644 --- a/src/main/scala/db/Main.scala +++ b/src/main/scala/db/Main.scala @@ -45,22 +45,22 @@ object Main { // } yield re val formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmm") - val from = LocalDateTime.parse("20240110_1000", formatter) - val to = LocalDateTime.parse("20240111_1000", formatter) -// val cities = NonEmptyList.of("Ainaži", "Rīga", "Kolka", "Vičaki") + val from = LocalDateTime.parse("20240207_1000", formatter) + val to = LocalDateTime.parse("20240208_1400", formatter) + val cities = NonEmptyList.of("Ainaži", "Rīga", "Kolka", "Vičaki") // val cities = NonEmptyList.of("Rīga") -// val query = UserQuery(cities, "tempMax", AggregateKey.List, ChronoUnit.DAYS, from, to) + val query = UserQuery(cities, "phenomena", AggregateKey.List, ChronoUnit.DAYS, from, to) val re = for { postgresService <- PostgresService.of(xa) - re <- postgresService.queryCityAllFields("Rīga", from, to) + re <- postgresService.query(query) } yield re // val jssson = re.map(result => ResponseWrapper(result, query)) // .map(responseWrapper => responseWrapper.asJson.pretty) - println(re.unsafeRunSync()) + println(re.unsafeRunSync().asJson.pretty) // println(jssson.unsafeRunSync()) diff --git a/src/main/scala/db/PostgresService.scala b/src/main/scala/db/PostgresService.scala index 42fce3d..05238ef 100644 --- a/src/main/scala/db/PostgresService.scala +++ b/src/main/scala/db/PostgresService.scala @@ -11,8 +11,12 @@ import java.time.{LocalDate, LocalDateTime, OffsetDateTime} import doobie.postgres.implicits._ import org.typelevel.log4cats.Logger import org.typelevel.log4cats.slf4j.Slf4jLogger -import parse.Aggregate.{AggregateKey, AggregateValue, DoubleValue, TimeDoubleList, UserQuery} +import parse.Aggregate.{AggregateKey, AggregateValue, DistinctStringList, DoubleValue, TimeDoubleList, UserQuery} import parse.{Parser, WeatherStationData} +import shapeless.syntax.std.tuple.productTupleOps + +import io.circe._ +import io.circe.parser._ import java.time.temporal.ChronoUnit import scala.util.matching.Regex @@ -101,9 +105,31 @@ class PostgresService(transactor: Transactor[IO], log: Logger[IO]) { } def query(userQuery: UserQuery): IO[Map[String, Option[AggregateValue]]] = { - if (userQuery.field == "phenomena") { // this handles strings - // TODO query list and distinct values from phenomena - IO(Map()) // empty result + if (userQuery.field == "phenomena") { + val query = + fr""" + SELECT city, array_to_json(array_agg(phenomena)) AS aggregated_phenomena + FROM weather + WHERE """ ++ Fragments.in(fr"city", userQuery.cities) ++ + fr" AND dateTime BETWEEN ${userQuery.from} AND ${userQuery.to}" ++ + fr" GROUP BY city, phenomena" + + query.query[(String, Option[String])] + .to[List] + .transact(transactor) + .map { resultList => + resultList + .flatMap { + case (city, jsonOption) => + jsonOption.flatMap { jsonString => + parse(jsonString).toOption.flatMap(_.as[List[List[String]]].toOption.map(_.flatten.distinct)) + }.map(phenomena => (city, phenomena.filter(_.nonEmpty))) + } + .groupBy(_._1) + .view.mapValues(_.flatMap(_._2).distinct).toMap + .map { case (city, phenomenaList) => city -> Option(DistinctStringList(phenomenaList)) } + } + } else if (List( AggregateKey.Max, AggregateKey.Min, diff --git a/src/main/scala/parse/Aggregate.scala b/src/main/scala/parse/Aggregate.scala index 52fefb6..30c78d9 100644 --- a/src/main/scala/parse/Aggregate.scala +++ b/src/main/scala/parse/Aggregate.scala @@ -93,7 +93,9 @@ object Aggregate { implicit val stringListListEncoder: Encoder[StringListList] = Encoder.instance { sllValue => Json.arr(sllValue.list.map(_.asJson): _*) } - implicit val distinctStringListEncoder: Encoder[DistinctStringList] = deriveEncoder[DistinctStringList] + implicit val distinctStringListEncoder: Encoder[DistinctStringList] = Encoder.instance { dslistValue => + Json.arr(dslistValue.list.map(_.asJson): _*) + } } private def convertDateTime(granularity: ChronoUnit)(data: (LocalDateTime, _)): String = { diff --git a/web/src/cities/result/GridResult.tsx b/web/src/cities/result/GridResult.tsx index f3ba866..0a5be63 100644 --- a/web/src/cities/result/GridResult.tsx +++ b/web/src/cities/result/GridResult.tsx @@ -1,4 +1,4 @@ -import { Component } from "solid-js"; +import { Component, Match, Switch } from "solid-js"; import { CityChart } from "../../components/chart/CityChart"; import { DataQuery } from "../helpers"; @@ -7,11 +7,14 @@ export const GridResult: Component<{ city: string, query: DataQuery, result: any return (

{ city }

- { - isDateNumber(result) - ? city} data={()=>result} query={()=>query}/> - :

{ result }

- } + { result }

}> + + { result.join(", ") } + + + city} data={()=>result} query={()=>query}/> + +
); }