From c9120db28a5318517ce17234c2f29591491bc602 Mon Sep 17 00:00:00 2001 From: Guntis Smaukstelis Date: Sat, 21 Oct 2023 21:12:58 +0300 Subject: [PATCH] Create, drop, insert table in postgres --- build.sbt | 5 +- .../resources/db/create_weather_table.sql | 19 +++++ src/main/resources/db/drop_weather_table.sql | 1 + .../resources/db/insert_weather_table.sql | 18 +++++ src/main/scala/db/Postgres.scala | 81 +++++++++++++++++++ 5 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/db/create_weather_table.sql create mode 100644 src/main/resources/db/drop_weather_table.sql create mode 100644 src/main/resources/db/insert_weather_table.sql create mode 100644 src/main/scala/db/Postgres.scala diff --git a/build.sbt b/build.sbt index 0f1c1ef..8bbf265 100644 --- a/build.sbt +++ b/build.sbt @@ -8,6 +8,7 @@ lazy val root = (project in file(".")) Compile / mainClass := Some("app.Main") ) +val doobieVersion = "1.0.0-RC1" val http4sVersion = "0.23.18" val circeVersion = "0.14.1" @@ -18,11 +19,13 @@ libraryDependencies ++= Seq( "org.http4s" %% "http4s-ember-client" % http4sVersion, "org.http4s" %% "http4s-circe" % http4sVersion, + "org.tpolecat" %% "doobie-core" % doobieVersion, + "org.tpolecat" %% "doobie-postgres" % doobieVersion, + "ch.qos.logback" % "logback-classic" % "1.2.9", "org.scala-lang" % "scala-reflect" % "2.13.10", "com.github.pureconfig" %% "pureconfig" % "0.17.4", - "io.circe" %% "circe-core" % circeVersion, "io.circe" %% "circe-generic" % circeVersion, "io.circe" %% "circe-generic-extras" % circeVersion, diff --git a/src/main/resources/db/create_weather_table.sql b/src/main/resources/db/create_weather_table.sql new file mode 100644 index 0000000..69fd721 --- /dev/null +++ b/src/main/resources/db/create_weather_table.sql @@ -0,0 +1,19 @@ +CREATE TABLE IF NOT EXISTS weather ( + dateTime TIMESTAMPTZ NOT NULL, + city VARCHAR(255) NOT NULL, + tempMax DOUBLE PRECISION, + tempMin DOUBLE PRECISION, + tempAvg DOUBLE PRECISION, + precipitation DOUBLE PRECISION, + windAvg DOUBLE PRECISION, + windMax DOUBLE PRECISION, + visibilityMin DOUBLE PRECISION, + visibilityAvg DOUBLE PRECISION, + snowAvg DOUBLE PRECISION, + atmPressure DOUBLE PRECISION, + dewPoint DOUBLE PRECISION, + humidity DOUBLE PRECISION, + sunDuration DOUBLE PRECISION, + phenomena TEXT[], + UNIQUE(city, dateTime) +) \ No newline at end of file diff --git a/src/main/resources/db/drop_weather_table.sql b/src/main/resources/db/drop_weather_table.sql new file mode 100644 index 0000000..aec8f0a --- /dev/null +++ b/src/main/resources/db/drop_weather_table.sql @@ -0,0 +1 @@ +DROP TABLE weather \ No newline at end of file diff --git a/src/main/resources/db/insert_weather_table.sql b/src/main/resources/db/insert_weather_table.sql new file mode 100644 index 0000000..fc4ed35 --- /dev/null +++ b/src/main/resources/db/insert_weather_table.sql @@ -0,0 +1,18 @@ +INSERT INTO weather (dateTime, city, tempMax, tempMin, tempAvg, precipitation, windAvg, windMax, visibilityMin, visibilityAvg, snowAvg, atmPressure, dewPoint, humidity, sunDuration, phenomena) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT (city, dateTime) + DO UPDATE SET + tempMax = excluded.tempMax, + tempMin = excluded.tempMin, + tempAvg = excluded.tempAvg, + precipitation = excluded.precipitation, + windAvg = excluded.windAvg, + windMax = excluded.windMax, + visibilityMin = excluded.visibilityMin, + visibilityAvg = excluded.visibilityAvg, + snowAvg = excluded.snowAvg, + atmPressure = excluded.atmPressure, + dewPoint = excluded.dewPoint, + humidity = excluded.humidity, + sunDuration = excluded.sunDuration, + phenomena = excluded.phenomena; \ No newline at end of file diff --git a/src/main/scala/db/Postgres.scala b/src/main/scala/db/Postgres.scala new file mode 100644 index 0000000..3dae26c --- /dev/null +++ b/src/main/scala/db/Postgres.scala @@ -0,0 +1,81 @@ +package db + +import cats.effect._ +import cats.effect.unsafe.implicits.global +import doobie._ +import doobie.implicits._ + +import java.time.format.DateTimeFormatter +import java.time.{LocalDateTime, ZoneId, ZonedDateTime} +import doobie.postgres.implicits._ + +case class SqlContent(value: String) + +object Postgres { + def transactor[F[_]: Async]: Transactor[F] = Transactor.fromDriverManager[F]( + "org.postgresql.Driver", + "jdbc:postgresql://localhost:5432/weather-tool", + "postgres", + "mysecretpassword" + ) + + def getResourceContent(path: String): IO[String] = { + val streamResource = Resource.make(IO(getClass.getResourceAsStream(path))) { stream => + IO(stream.close()).handleErrorWith(_ => IO.unit) + } + + streamResource.use { stream => + IO(scala.io.Source.fromInputStream(stream).mkString) + } + } + +// def findUserById(userId: Int)(implicit xa: Transactor[IO]): IO[Option[User]] = { +// sql"SELECT id, name FROM users WHERE id = $userId" +// .query[User] +// .option +// .transact(xa) +// } + + def createWeatherTable(implicit xa: Transactor[IO]): IO[Int] = { + for { + createTableSql <- getResourceContent("/db/create_weather_table.sql") + result <- Update0(createTableSql, None).run.transact(xa) + } yield result + } + + implicit val doubleOptionMeta: Meta[Option[Double]] = Meta[Double].imap(Option(_))(_.getOrElse(Double.NaN)) + + def insertInWeatherTable(xa: Transactor[IO]): IO[Int] = { + val formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmm") + val dateTime = LocalDateTime.parse("20230516_1500", formatter) + + val rigaZone = ZoneId.of("Europe/Riga") + val zonedTime: ZonedDateTime = dateTime.atZone(rigaZone) + + // in pgAdmin run: ```SET TIMEZONE = 'Europe/Riga';``` + + for { + insertTableSql <- getResourceContent("/db/insert_weather_table.sql") + result <- Update[(ZonedDateTime, String, Option[Double], Option[Double], Option[Double], Option[Double], Option[Double], Option[Double], Option[Double], Option[Double], Option[Double], Option[Double], Option[Double], Option[Double], Option[Double], List[String])]( + insertTableSql + ).run((zonedTime, "Rīga", Some(20.2), Some(8.8), Some(15.6), None, None, None, None, None, None, None, None, None, None, List("hail", "rain"))).transact(xa) + } yield result + } + + def dropWeatherTable(implicit xa: Transactor[IO]): IO[Int] = { + for { + dropTableSql <- getResourceContent("/db/drop_weather_table.sql") + result <- Update0(dropTableSql, None).run.transact(xa) + } yield result + } + + def main(args: Array[String]): Unit = { + val xa = transactor[IO] + +// val result = createWeatherTable(xa).unsafeRunSync() + val result = insertInWeatherTable(xa).unsafeRunSync() +// val result = dropWeatherTable(xa).unsafeRunSync() + + println(s"result: $result") + } +}