125 lines
4.7 KiB
Scala
125 lines
4.7 KiB
Scala
|
|
package parse
|
||
|
|
|
||
|
|
import cats.implicits.{catsSyntaxOptionId, toFoldableOps}
|
||
|
|
import io.circe.generic.semiauto.deriveEncoder
|
||
|
|
import io.circe.syntax.EncoderOps
|
||
|
|
import io.circe.{Encoder, Json}
|
||
|
|
import parse.Parser.parseLine
|
||
|
|
|
||
|
|
object Aggregate {
|
||
|
|
|
||
|
|
case class UserQuery(
|
||
|
|
cities: List[String],
|
||
|
|
field: String,
|
||
|
|
key: AggregateKey
|
||
|
|
)
|
||
|
|
|
||
|
|
sealed trait AggregateKey
|
||
|
|
object AggregateKey {
|
||
|
|
case object Min extends AggregateKey
|
||
|
|
case object Max extends AggregateKey
|
||
|
|
case object Avg extends AggregateKey
|
||
|
|
case object Sum extends AggregateKey
|
||
|
|
case object List extends AggregateKey
|
||
|
|
case object Distinct extends AggregateKey
|
||
|
|
|
||
|
|
def fromString(str: String): Option[AggregateKey] = str match {
|
||
|
|
case "min" => Some(AggregateKey.Min)
|
||
|
|
case "max" => Some(AggregateKey.Max)
|
||
|
|
case "avg" => Some(AggregateKey.Avg)
|
||
|
|
case "sum" => Some(AggregateKey.Sum)
|
||
|
|
case "list" => Some(AggregateKey.List)
|
||
|
|
case "distinct" => Some(AggregateKey.Distinct)
|
||
|
|
case _ => None
|
||
|
|
}
|
||
|
|
|
||
|
|
def getKeys: List[String] = {
|
||
|
|
val runtimeMirror = scala.reflect.runtime.currentMirror
|
||
|
|
val classSymbol = runtimeMirror.classSymbol(classOf[AggregateKey])
|
||
|
|
|
||
|
|
// Get all case classes that extend the AggregateKey trait
|
||
|
|
val keys = classSymbol.knownDirectSubclasses.map(_.name.toString).toList
|
||
|
|
|
||
|
|
keys
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
sealed trait AggregateValue
|
||
|
|
case class DoubleValue(value: Double) extends AggregateValue
|
||
|
|
case class ListOptionValue(list: List[Option[Double]]) extends AggregateValue
|
||
|
|
case class StringListList(list: List[List[String]]) extends AggregateValue
|
||
|
|
case class DistinctStringList(list: List[String]) extends AggregateValue
|
||
|
|
object AggregateValue {
|
||
|
|
def getKeys: List[String] = {
|
||
|
|
val runtimeMirror = scala.reflect.runtime.currentMirror
|
||
|
|
val classSymbol = runtimeMirror.classSymbol(classOf[AggregateValue])
|
||
|
|
|
||
|
|
// Get all case classes that extend the AggregateValue trait
|
||
|
|
val keys = classSymbol.knownDirectSubclasses.map(_.name.toString).toList
|
||
|
|
|
||
|
|
keys
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
object AggregateValueImplicits {
|
||
|
|
implicit val aggregateValueEncoder: Encoder[AggregateValue] = Encoder.instance {
|
||
|
|
case doubleValue: DoubleValue => doubleValue.asJson
|
||
|
|
case listOptionValue: ListOptionValue => listOptionValue.asJson
|
||
|
|
case stringListList: StringListList => stringListList.asJson
|
||
|
|
case distinctStringList: DistinctStringList => distinctStringList.asJson
|
||
|
|
}
|
||
|
|
|
||
|
|
implicit val doubleValueEncoder: Encoder[DoubleValue] = Encoder.instance {
|
||
|
|
case DoubleValue(value) if !value.isNaN => Json.fromDouble(value).get
|
||
|
|
case _ => Json.Null
|
||
|
|
}
|
||
|
|
implicit val listOptionValueEncoder: Encoder[ListOptionValue] = deriveEncoder[ListOptionValue]
|
||
|
|
implicit val stringListListEncoder: Encoder[StringListList] = deriveEncoder[StringListList]
|
||
|
|
implicit val distinctStringListEncoder: Encoder[DistinctStringList] = deriveEncoder[DistinctStringList]
|
||
|
|
}
|
||
|
|
|
||
|
|
def extractDoubleFieldValues(field: String, weatherData: List[WeatherData]): List[Option[Double]] =
|
||
|
|
field match {
|
||
|
|
case "tempMax" => weatherData.map(_.tempMax)
|
||
|
|
case "tempMin" => weatherData.map(_.tempMin)
|
||
|
|
case "tempAvg" => weatherData.map(_.tempAvg)
|
||
|
|
case "precipitation" => weatherData.map(_.precipitation)
|
||
|
|
case "windAvg" => weatherData.map(_.windAvg)
|
||
|
|
case "windMax" => weatherData.map(_.windMax)
|
||
|
|
case "visibilityMin" => weatherData.map(_.visibilityMin)
|
||
|
|
case "visibilityAvg" => weatherData.map(_.visibilityAvg)
|
||
|
|
case "snowAvg" => weatherData.map(_.snowAvg)
|
||
|
|
case "atmPressire" => weatherData.map(_.atmPressure)
|
||
|
|
case "dewPoint" => weatherData.map(_.dewPoint)
|
||
|
|
case "humidity" => weatherData.map(_.humidity)
|
||
|
|
case "sunDuration" => weatherData.map(_.sunDuration)
|
||
|
|
// case "phenomena" => weatherData.map(_..phenomena)
|
||
|
|
case _ => List.empty
|
||
|
|
}
|
||
|
|
|
||
|
|
def aggregateDoubleValues(
|
||
|
|
AggKey: AggregateKey,
|
||
|
|
values: List[Option[Double]]
|
||
|
|
): Option[AggregateValue] = {
|
||
|
|
val flatValues = values.flatten
|
||
|
|
AggKey match {
|
||
|
|
case AggregateKey.Min => flatValues.minimumOption.map(DoubleValue)
|
||
|
|
case AggregateKey.Max => flatValues.maximumOption.map(DoubleValue)
|
||
|
|
case AggregateKey.Avg => flatValues.reduceOption(_ + _).map(sum => DoubleValue(sum / flatValues.length))
|
||
|
|
case AggregateKey.Sum => flatValues.sum.some.map(DoubleValue)
|
||
|
|
case AggregateKey.List => ListOptionValue(values).some
|
||
|
|
case AggregateKey.Distinct => None
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
def aggregatePhenomenaValues(
|
||
|
|
aggregationType: AggregateKey,
|
||
|
|
values: List[List[String]]
|
||
|
|
): Option[AggregateValue] =
|
||
|
|
aggregationType match {
|
||
|
|
case AggregateKey.List => StringListList(values).some
|
||
|
|
case AggregateKey.Distinct => DistinctStringList(values.flatten.distinct).some
|
||
|
|
case _ => None
|
||
|
|
}
|
||
|
|
}
|