Skip to content

Commit

Permalink
Add toTSV method (close #97)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjben committed Nov 4, 2020
1 parent 88f5684 commit 6d827c1
Show file tree
Hide file tree
Showing 10 changed files with 754 additions and 134 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import com.snowplowanalytics.snowplow.analytics.scalasdk.Event
@State(Scope.Thread)
@BenchmarkMode(Array(Mode.AverageTime, Mode.Throughput))
@OutputTimeUnit(TimeUnit.MICROSECONDS)
class BenchmarkAnalyticsSDK {
class OrderedBenchmark {
@Benchmark
def ordered(state : States.AtomicEventState): Unit = {
state.event.ordered
Expand All @@ -44,4 +44,4 @@ object States {
event = Event.minimal(uuid, timestamp, vCollector, vTracker)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.snowplowanalytics.snowplow.analytics.scalasdk.benchmark

import org.openjdk.jmh.annotations._

import java.util.concurrent.TimeUnit
import java.util.UUID
import java.time.Instant

import com.snowplowanalytics.snowplow.analytics.scalasdk.Event

@State(Scope.Thread)
@BenchmarkMode(Array(Mode.AverageTime, Mode.Throughput))
@OutputTimeUnit(TimeUnit.MICROSECONDS)
class ToTsvBenchmark {
@Benchmark
def toTsv(state : ToTsvBenchmark.AtomicEventState): Unit = {
state.event.toTsv
}
}

object ToTsvBenchmark {
@State(Scope.Benchmark)
class AtomicEventState {
var event: Event = _

@Setup(Level.Trial)
def init(): Unit = {
val uuid = UUID.randomUUID()
val timestamp = Instant.now()
val vCollector = "2.0.0"
val vTracker = "scala_0.7.0"
event = Event.minimal(uuid, timestamp, vCollector, vTracker)
}
}
}
4 changes: 3 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ lazy val root = project.in(file("."))
Dependencies.circeParser,
Dependencies.circeGeneric,
// Scala (test only)
Dependencies.specs2
Dependencies.specs2,
Dependencies.specs2Scalacheck,
Dependencies.scalacheck
)
)

Expand Down
3 changes: 3 additions & 0 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ object Dependencies {
val circe = "0.13.0"
// Scala (test only)
val specs2 = "4.8.0"
val scalaCheck = "1.15.0"
}

val igluCore = "com.snowplowanalytics" %% "iglu-core-circe" % V.igluCore
Expand All @@ -28,4 +29,6 @@ object Dependencies {
val circeGeneric = "io.circe" %% "circe-generic" % V.circe
// Scala (test only)
val specs2 = "org.specs2" %% "specs2-core" % V.specs2 % Test
val specs2Scalacheck = "org.specs2" %% "specs2-scalacheck" % V.specs2 % Test
val scalacheck = "org.scalacheck" %% "scalacheck" % V.scalaCheck % Test
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016-2019 Snowplow Analytics Ltd. All rights reserved.
* Copyright (c) 2016-2020 Snowplow Analytics Ltd. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
Expand All @@ -15,6 +15,7 @@ package com.snowplowanalytics.snowplow.analytics.scalasdk
// java
import java.time.Instant
import java.util.UUID
import java.time.format.DateTimeFormatter

// circe
import io.circe.{Encoder, Json, JsonObject, Decoder}
Expand All @@ -30,6 +31,7 @@ import com.snowplowanalytics.iglu.core.circe.implicits._
import com.snowplowanalytics.snowplow.analytics.scalasdk.decode.{Parser, DecodeResult}
import com.snowplowanalytics.snowplow.analytics.scalasdk.SnowplowEvent.{Contexts, UnstructEvent}
import com.snowplowanalytics.snowplow.analytics.scalasdk.SnowplowEvent._
import com.snowplowanalytics.snowplow.analytics.scalasdk.encode.TsvEncoder

/**
* Case class representing a canonical Snowplow event.
Expand Down Expand Up @@ -228,6 +230,9 @@ case class Event(app_id: Option[String],
this.asJson
}

/** Create the TSV representation of this event. */
def toTsv: String = TsvEncoder.encode(this)

/**
* This event as a map of keys to Circe JSON values
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016-2019 Snowplow Analytics Ltd. All rights reserved.
* Copyright (c) 2016-2020 Snowplow Analytics Ltd. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
/*
* Copyright (c) 2020-2020 Snowplow Analytics Ltd. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/
package com.snowplowanalytics.snowplow.analytics.scalasdk.encode

import java.time.format.DateTimeFormatter
import java.time.Instant
import java.util.UUID

import io.circe.syntax._

import com.snowplowanalytics.snowplow.analytics.scalasdk.SnowplowEvent._
import com.snowplowanalytics.snowplow.analytics.scalasdk.Event

object TsvEncoder {
sealed trait FieldEncoder[T] {
def encodeField(t: T): String
}

implicit object StringEncoder extends FieldEncoder[String] {
def encodeField(str: String) = str
}

implicit object InstantEncoder extends FieldEncoder[Instant] {
def encodeField(inst: Instant): String =
DateTimeFormatter.ISO_INSTANT
.format(inst)
.replace("T", " ")
.dropRight(1) // remove trailing 'Z'
}

implicit object UuidEncoder extends FieldEncoder[UUID] {
def encodeField(uuid: UUID): String = uuid.toString
}

implicit object IntEncoder extends FieldEncoder[Int] {
def encodeField(int: Int): String = int.toString
}

implicit object DoubleEncoder extends FieldEncoder[Double] {
def encodeField(doub: Double): String = doub.toString
}

implicit object BooleanEncoder extends FieldEncoder[Boolean] {
def encodeField(bool: Boolean): String = if(bool) "1" else "0"
}

implicit object ContextsEncoder extends FieldEncoder[Contexts] {
def encodeField(ctxts: Contexts): String =
if (ctxts.data.isEmpty)
""
else
ctxts.asJson.noSpaces
}

implicit object UnstructEncoder extends FieldEncoder[UnstructEvent] {
def encodeField(unstruct: UnstructEvent): String =
if (unstruct.data.isDefined)
unstruct.asJson.noSpaces
else
""
}

def encode[A](a: A)(implicit ev: FieldEncoder[A]): String =
ev.encodeField(a)

def encode[A](optA: Option[A])(implicit ev: FieldEncoder[A]): String =
optA.map(a => ev.encodeField(a)).getOrElse("")

def encode(event: Event): String =
encode(event.app_id) + "\t" +
encode(event.platform) + "\t" +
encode(event.etl_tstamp) + "\t" +
encode(event.collector_tstamp) + "\t" +
encode(event.dvce_created_tstamp) + "\t" +
encode(event.event) + "\t" +
encode(event.event_id) + "\t" +
encode(event.txn_id) + "\t" +
encode(event.name_tracker) + "\t" +
encode(event.v_tracker) + "\t" +
encode(event.v_collector) + "\t" +
encode(event.v_etl) + "\t" +
encode(event.user_id) + "\t" +
encode(event.user_ipaddress) + "\t" +
encode(event.user_fingerprint) + "\t" +
encode(event.domain_userid) + "\t" +
encode(event.domain_sessionidx) + "\t" +
encode(event.network_userid) + "\t" +
encode(event.geo_country) + "\t" +
encode(event.geo_region) + "\t" +
encode(event.geo_city) + "\t" +
encode(event.geo_zipcode) + "\t" +
encode(event.geo_latitude) + "\t" +
encode(event.geo_longitude) + "\t" +
encode(event.geo_region_name) + "\t" +
encode(event.ip_isp) + "\t" +
encode(event.ip_organization) + "\t" +
encode(event.ip_domain) + "\t" +
encode(event.ip_netspeed) + "\t" +
encode(event.page_url) + "\t" +
encode(event.page_title) + "\t" +
encode(event.page_referrer) + "\t" +
encode(event.page_urlscheme) + "\t" +
encode(event.page_urlhost) + "\t" +
encode(event.page_urlport) + "\t" +
encode(event.page_urlpath) + "\t" +
encode(event.page_urlquery) + "\t" +
encode(event.page_urlfragment) + "\t" +
encode(event.refr_urlscheme) + "\t" +
encode(event.refr_urlhost) + "\t" +
encode(event.refr_urlport) + "\t" +
encode(event.refr_urlpath) + "\t" +
encode(event.refr_urlquery) + "\t" +
encode(event.refr_urlfragment) + "\t" +
encode(event.refr_medium) + "\t" +
encode(event.refr_source) + "\t" +
encode(event.refr_term) + "\t" +
encode(event.mkt_medium) + "\t" +
encode(event.mkt_source) + "\t" +
encode(event.mkt_term) + "\t" +
encode(event.mkt_content) + "\t" +
encode(event.mkt_campaign) + "\t" +
encode(event.contexts) + "\t" +
encode(event.se_category) + "\t" +
encode(event.se_action) + "\t" +
encode(event.se_label) + "\t" +
encode(event.se_property) + "\t" +
encode(event.se_value) + "\t" +
encode(event.unstruct_event) + "\t" +
encode(event.tr_orderid) + "\t" +
encode(event.tr_affiliation) + "\t" +
encode(event.tr_total) + "\t" +
encode(event.tr_tax) + "\t" +
encode(event.tr_shipping) + "\t" +
encode(event.tr_city) + "\t" +
encode(event.tr_state) + "\t" +
encode(event.tr_country) + "\t" +
encode(event.ti_orderid) + "\t" +
encode(event.ti_sku) + "\t" +
encode(event.ti_name) + "\t" +
encode(event.ti_category) + "\t" +
encode(event.ti_price) + "\t" +
encode(event.ti_quantity) + "\t" +
encode(event.pp_xoffset_min) + "\t" +
encode(event.pp_xoffset_max) + "\t" +
encode(event.pp_yoffset_min) + "\t" +
encode(event.pp_yoffset_max) + "\t" +
encode(event.useragent) + "\t" +
encode(event.br_name) + "\t" +
encode(event.br_family) + "\t" +
encode(event.br_version) + "\t" +
encode(event.br_type) + "\t" +
encode(event.br_renderengine) + "\t" +
encode(event.br_lang) + "\t" +
encode(event.br_features_pdf) + "\t" +
encode(event.br_features_flash) + "\t" +
encode(event.br_features_java) + "\t" +
encode(event.br_features_director) + "\t" +
encode(event.br_features_quicktime) + "\t" +
encode(event.br_features_realplayer) + "\t" +
encode(event.br_features_windowsmedia) + "\t" +
encode(event.br_features_gears) + "\t" +
encode(event.br_features_silverlight) + "\t" +
encode(event.br_cookies) + "\t" +
encode(event.br_colordepth) + "\t" +
encode(event.br_viewwidth) + "\t" +
encode(event.br_viewheight) + "\t" +
encode(event.os_name) + "\t" +
encode(event.os_family) + "\t" +
encode(event.os_manufacturer) + "\t" +
encode(event.os_timezone) + "\t" +
encode(event.dvce_type) + "\t" +
encode(event.dvce_ismobile) + "\t" +
encode(event.dvce_screenwidth) + "\t" +
encode(event.dvce_screenheight) + "\t" +
encode(event.doc_charset) + "\t" +
encode(event.doc_width) + "\t" +
encode(event.doc_height) + "\t" +
encode(event.tr_currency) + "\t" +
encode(event.tr_total_base) + "\t" +
encode(event.tr_tax_base) + "\t" +
encode(event.tr_shipping_base) + "\t" +
encode(event.ti_currency) + "\t" +
encode(event.ti_price_base) + "\t" +
encode(event.base_currency) + "\t" +
encode(event.geo_timezone) + "\t" +
encode(event.mkt_clickid) + "\t" +
encode(event.mkt_network) + "\t" +
encode(event.etl_tags) + "\t" +
encode(event.dvce_sent_tstamp) + "\t" +
encode(event.refr_domain_userid) + "\t" +
encode(event.refr_dvce_tstamp) + "\t" +
encode(event.derived_contexts) + "\t" +
encode(event.domain_sessionid) + "\t" +
encode(event.derived_tstamp) + "\t" +
encode(event.event_vendor) + "\t" +
encode(event.event_name) + "\t" +
encode(event.event_format) + "\t" +
encode(event.event_version) + "\t" +
encode(event.event_fingerprint) + "\t" +
encode(event.true_tstamp)
}
Loading

0 comments on commit 6d827c1

Please sign in to comment.