-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
325 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package _playground | ||
|
||
import cats.effect.{IO, IOApp} | ||
import cats.implicits._ | ||
|
||
import scala.concurrent.duration.DurationInt | ||
|
||
object Sketch11 extends App { | ||
|
||
val data = List(1, 2, 3, 4) | ||
|
||
val s: String = data | ||
.map(x => x.toString) | ||
.reduce( (s1, s2) => s1 + ", "+ s2 ) | ||
|
||
val min = data.reduce( (x1, x2) => math.min(x1, x2) ) | ||
val max = data.reduce( (x1, x2) => math.max(x1, x2) ) | ||
val sum = data.reduce( (x1, x2) => x1 + x2 ) | ||
val prd = data.reduce( (x1, x2) => x1 * x2 ) | ||
|
||
val m1: Int = data.min | ||
val m2: Option[Int] = data.minOption | ||
|
||
|
||
println(s) // "1, 2, 3, 4" | ||
println(min) // 1 | ||
println(max) // 4 | ||
println(sum) // 10 | ||
println(prd) // 24 | ||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
package gas104 | ||
|
||
import cats.effect.Concurrent | ||
import cats.effect.IO | ||
import cats.effect.Sync | ||
import cats.effect.kernel.Async | ||
import cats.effect.kernel.Resource | ||
import cats.implicits._ | ||
import gas104.domain._ | ||
import gas104.domain.api._ | ||
import io.circe.parser | ||
import io.circe.syntax.EncoderOps | ||
import java.time.Instant | ||
import java.time.LocalDateTime | ||
import org.http4s.Response | ||
import org.http4s.blaze.client.BlazeClientBuilder | ||
import org.http4s.client.Client | ||
import org.scalatest.Succeeded | ||
import org.scalatest.funspec.AnyFunSpec | ||
import org.scalatest.matchers.should.Matchers | ||
|
||
class DomainSpec extends AnyFunSpec with Matchers { | ||
|
||
describe("Row object") { | ||
|
||
val rowObj: Row = Row( | ||
"20.07.2023 07:00", | ||
"0.81", | ||
0, | ||
1689800400, | ||
) | ||
|
||
val rowRaw = | ||
""" | ||
|{ | ||
| "dt" : "20.07.2023 07:00", | ||
| "counter_reading" : "0.81", | ||
| "consumption" : 0.0, | ||
| "created_at_timestamp" : 1689800400 | ||
|} | ||
|""".stripMargin.trim | ||
|
||
it("encodes properly") { | ||
rowObj.asJson.spaces2 shouldBe rowRaw | ||
} | ||
|
||
it("decodes properly") { | ||
parser.decode[Row](rowRaw) shouldBe rowObj.asRight | ||
} | ||
|
||
} | ||
|
||
describe("data") { | ||
|
||
val dataObj: Data = Data( | ||
error = None, | ||
data = Seq( | ||
Row( | ||
"20.07.2023 07:00", | ||
"0.81", | ||
0, | ||
1689800400, | ||
), | ||
Row( | ||
"21.07.2023 07:00", | ||
"0.81", | ||
0, | ||
1689886800, | ||
) | ||
) | ||
) | ||
|
||
val rawData = | ||
""" | ||
|{ | ||
| "error" : null, | ||
| "data" : [ | ||
| { | ||
| "dt" : "20.07.2023 07:00", | ||
| "counter_reading" : "0.81", | ||
| "consumption" : 0.0, | ||
| "created_at_timestamp" : 1689800400 | ||
| }, | ||
| { | ||
| "dt" : "21.07.2023 07:00", | ||
| "counter_reading" : "0.81", | ||
| "consumption" : 0.0, | ||
| "created_at_timestamp" : 1689886800 | ||
| } | ||
| ] | ||
|} | ||
|""".stripMargin.trim | ||
|
||
it("encodes properly") { | ||
dataObj.asJson.spaces2 shouldBe rawData | ||
} | ||
|
||
it("decodes properly") { | ||
parser.decode[Data](rawData) shouldBe dataObj.asRight | ||
} | ||
|
||
} | ||
|
||
describe("convertor") { | ||
|
||
it("apiRow -> URow") { | ||
val rowFrom104: Row = Row( | ||
"20.07.2023 07:00", | ||
"0.81", | ||
0, | ||
1689800400, | ||
) | ||
|
||
val uRowExpected = URow( | ||
LocalDateTime.of(2023, 7, 20, 7, 0), | ||
counter = 0.81, | ||
delta = 0.0, | ||
createdAt = Instant.parse("2023-07-19T21:00:00Z") | ||
) | ||
|
||
URow.from(rowFrom104) shouldBe uRowExpected | ||
} | ||
|
||
} | ||
|
||
describe("instant playground") { | ||
val ts: Long = 1696539600L | ||
val instant: Instant = Instant.ofEpochSecond(ts) // 2023-10-05T21:00:00Z // "06.10.2023 07:00" | ||
|
||
it("3") { | ||
pprint.pprintln(instant) | ||
} | ||
|
||
} | ||
|
||
describe("http") { | ||
|
||
def body[F[_] : Concurrent](rs: Response[F]): F[String] = | ||
rs.body | ||
.through(fs2.text.utf8.decode[F]) | ||
.compile | ||
.foldMonoid | ||
|
||
def mkHttpClient[F[_]: Async]: Resource[F, Client[F]] = | ||
BlazeClientBuilder[F].resource | ||
|
||
def mkRequest[F[_]: Concurrent](client: Client[F]) = { | ||
import org.http4s.circe.CirceEntityCodec.circeEntityDecoder | ||
client.expect[Data](Htttp.rq[F]) | ||
} | ||
|
||
def representData[F[_]: Sync](payload: Data): F[Unit] = payload.data | ||
.traverse_ { x: Row => | ||
val r = URow.from(x) | ||
val line = (r.dateTime, r.counter, r.delta) | ||
Sync[F].delay(pprint.pprintln(line)) | ||
} | ||
|
||
it("1") { | ||
import cats.effect.unsafe.implicits.global | ||
mkHttpClient[IO] | ||
.use(mkRequest[IO]) | ||
.flatMap(representData[IO]) | ||
.unsafeRunSync() | ||
} | ||
|
||
it("stub tun trigger compilation") { | ||
Succeeded | ||
} | ||
|
||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package gas104 | ||
|
||
import cats.effect.IO | ||
import cats.effect.IOApp | ||
import io.circe.generic.AutoDerivation | ||
import io.circe.generic.extras.Configuration | ||
import io.circe.generic.extras.semiauto.{deriveConfiguredDecoder, deriveConfiguredEncoder} | ||
import io.circe.syntax.EncoderOps | ||
import io.circe.{Decoder, Encoder} | ||
|
||
object GasApp extends IOApp.Simple { | ||
|
||
|
||
override def run: IO[Unit] = IO{ | ||
|
||
println( | ||
|
||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package gas104 | ||
|
||
import org.http4s.Headers | ||
import org.http4s.MediaType | ||
import org.http4s.Method | ||
import org.http4s.Request | ||
import org.http4s.RequestCookie | ||
import org.http4s.UrlForm | ||
import org.http4s.headers._ | ||
import org.http4s.implicits.http4sLiteralsSyntax | ||
|
||
object Htttp { | ||
|
||
object PhpSessionId { | ||
def apply(value: String): RequestCookie = RequestCookie("PHPSESSID", value) | ||
} | ||
|
||
val uri = uri"https://ok.104.ua/ua/ajx/individual/meterage/history/remote" | ||
val headers = Headers( | ||
`Accept`(MediaType.application.json), | ||
`Content-Type`(MediaType.application.`x-www-form-urlencoded`), | ||
`Cookie`(PhpSessionId("")), | ||
) | ||
|
||
val payload = UrlForm( | ||
"account_no" -> "", // 0800xxxxxx | ||
"meter_no" -> "", | ||
"period_type" -> "y", | ||
"end_date" -> "2023-12-31T23:59:59.999Z" | ||
) | ||
|
||
def rq[F[_]] = Request[F](Method.POST, uri) | ||
.withHeaders(headers) | ||
.withEntity(payload) | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package gas104 | ||
|
||
import io.circe.Decoder | ||
import io.circe.Encoder | ||
import io.circe.generic.AutoDerivation | ||
import io.circe.generic.extras.Configuration | ||
import io.circe.generic.extras.semiauto.deriveConfiguredDecoder | ||
import io.circe.generic.extras.semiauto.deriveConfiguredEncoder | ||
import io.scalaland.chimney.dsl.TransformerOps | ||
|
||
import java.time.Instant | ||
import java.time.LocalDateTime | ||
import java.time.format.{DateTimeFormatter, DateTimeFormatterBuilder} | ||
import java.time.temporal.ChronoField | ||
|
||
object domain { | ||
|
||
object api { | ||
|
||
case class Row( | ||
dt: String, | ||
counterReading: String, | ||
consumption: Double, | ||
createdAtTimestamp: Long) | ||
|
||
object Row { | ||
implicit val config: Configuration = Configuration.default.withSnakeCaseMemberNames | ||
implicit val encoder: Encoder[Row] = deriveConfiguredEncoder | ||
implicit val decoder: Decoder[Row] = deriveConfiguredDecoder | ||
} | ||
|
||
case class Data(error: Option[String], data: Seq[Row]) | ||
|
||
object Data extends AutoDerivation | ||
|
||
} | ||
|
||
val gasDtFormatter: DateTimeFormatter = new DateTimeFormatterBuilder() | ||
.appendValue(ChronoField.DAY_OF_MONTH, 2) | ||
.appendLiteral('.') | ||
.appendValue(ChronoField.MONTH_OF_YEAR, 2) | ||
.appendLiteral('.') | ||
.appendValue(ChronoField.YEAR, 4) | ||
.appendLiteral(' ') | ||
.appendValue(ChronoField.HOUR_OF_DAY, 2) | ||
.appendLiteral(':') | ||
.appendValue(ChronoField.MINUTE_OF_HOUR, 2) | ||
.toFormatter | ||
|
||
case class URow(dateTime: LocalDateTime, counter: Double, delta: Double, createdAt: Instant) | ||
object URow { | ||
|
||
def from(apiRow: api.Row): URow = | ||
apiRow | ||
.into[URow] | ||
.withFieldComputed(_.dateTime, x => LocalDateTime.parse(x.dt, gasDtFormatter)) | ||
.withFieldComputed(_.counter, x => x.counterReading.toDouble) | ||
.withFieldRenamed(_.consumption, _.delta) | ||
.withFieldComputed(_.createdAt, x => Instant.ofEpochSecond(x.createdAtTimestamp)) | ||
.transform | ||
|
||
} | ||
|
||
} |