embulk-parser-twitter_ads_stats 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (25) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +21 -0
  3. data/README.md +8 -3
  4. data/build.gradle +1 -1
  5. data/build.sbt +10 -6
  6. data/src/main/scala/org/embulk/parser/twitter_ads_stats/Column.scala +10 -11
  7. data/src/main/scala/org/embulk/parser/twitter_ads_stats/MetricElementNames.scala +6 -3
  8. data/src/main/scala/org/embulk/parser/twitter_ads_stats/MetricsGroupJson.scala +1 -1
  9. data/src/main/scala/org/embulk/parser/twitter_ads_stats/ParseException.scala +9 -11
  10. data/src/main/scala/org/embulk/parser/twitter_ads_stats/TwitterAdsStatsParserPlugin.scala +9 -9
  11. data/src/main/scala/org/embulk/parser/twitter_ads_stats/define/Data.scala +13 -10
  12. data/src/main/scala/org/embulk/parser/twitter_ads_stats/define/IDData.scala +27 -20
  13. data/src/main/scala/org/embulk/parser/twitter_ads_stats/define/Request.scala +11 -10
  14. data/src/main/scala/org/embulk/parser/twitter_ads_stats/define/Root.scala +14 -12
  15. data/src/main/scala/org/embulk/parser/twitter_ads_stats/define/RootJson.scala +3 -8
  16. data/src/main/scala/org/embulk/parser/twitter_ads_stats/define/StatsDateTime.scala +25 -0
  17. data/src/main/scala/org/embulk/parser/twitter_ads_stats/package.scala +1 -1
  18. data/src/test/scala/org/embulk/parser/twitter_ads_stats/MetricElementNamesSpec.scala +1 -1
  19. data/src/test/scala/org/embulk/parser/twitter_ads_stats/MetricsGroupJsonSpec.scala +3 -3
  20. data/src/test/scala/org/embulk/parser/twitter_ads_stats/define/MetricsJsonSpec.scala +2 -2
  21. data/src/test/scala/org/embulk/parser/twitter_ads_stats/define/ParamsSpec.scala +3 -3
  22. data/src/test/scala/org/embulk/parser/twitter_ads_stats/define/RootJsonSpec.scala +1 -1
  23. data/src/test/scala/org/embulk/parser/twitter_ads_stats/define/RootSpec.scala +47 -47
  24. data/src/test/scala/org/embulk/parser/twitter_ads_stats/define/StatsDateTimeSpec.scala +21 -0
  25. metadata +6 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9f1833c6a364918557a8792567828b322c53c1b0
4
- data.tar.gz: 0a8b23dadeadcbf655be8f4da408ffe66cdfd034
3
+ metadata.gz: b01ca4d919c7b6a4b43fd5becbd2b2606a522a68
4
+ data.tar.gz: 3b013591fcb20ca7a2b3a0dd1bf45ce4e6687a0d
5
5
  SHA512:
6
- metadata.gz: 49e6ccdc2b3aa45b56b9a3908549eff06d0eed15dea8d6681bbb371a4b24642ee57ace7020e7647e55bf425c871415a34838f4352c399a924b783c72a51ef022
7
- data.tar.gz: 62c0e5ef405ae59c9545715f4845e22d95eb0b524977141eff0eccc6a006a00cb36a193ddf2478d70c85bf47f206851e95c82cdc2e5eddef8f425ab08fef95ee
6
+ metadata.gz: 00d138b86ec59902fc4cf150807af1343732d04541a6fe18d1915d8453226a37e21ee6f745e5ddd4df0384b8ab7f27151e486b00c5c437b0747f1551cd30bc58
7
+ data.tar.gz: 936209667f89677725f4d7b19c7efb86b7b4cf70ba57239b0abaee4cb3b7ad762ccb3fe4e210407744ea0c720289d061bc3b44ef447b079240d4de7a505422ad
data/.travis.yml ADDED
@@ -0,0 +1,21 @@
1
+ language: scala
2
+
3
+ scala:
4
+ - 2.12.3
5
+
6
+ script:
7
+ - >-
8
+ sbt
9
+ ++$TRAVIS_SCALA_VERSION
10
+ scalafmt::test
11
+ test:scalafmt::test
12
+ sbt:scalafmt::test
13
+ test
14
+
15
+ cache:
16
+ directories:
17
+ - $HOME/.ivy2/cache
18
+ - $HOME/.sbt/launchers
19
+ before_cache:
20
+ - find $HOME/.sbt -name "*.lock" | xargs rm
21
+ - find $HOME/.ivy2 -name "ivydata-*.properties" | xargs rm
data/README.md CHANGED
@@ -1,8 +1,13 @@
1
1
  # Twitter Ads Stats parser plugin for Embulk
2
2
 
3
+ [![Build Status](https://travis-ci.org/septeni-original/embulk-parser-twitter_ads_stats.svg?branch=master)](https://travis-ci.org/septeni-original/embulk-parser-twitter_ads_stats)
4
+
3
5
  This plugin parse [Twitter Ads Stats](https://developer.twitter.com/en/docs/ads/analytics/overview/metrics-and-segmentation) json file.
4
6
 
5
- ##Parse Logic
7
+ ## Notice
8
+ This plugin is an EXPERIMENTAL and support only Java8.
9
+
10
+ ## Parse Logic
6
11
  1. Flatten to metrics by date.
7
12
  2. Group Metrics into Metrics Group by json type.
8
13
 
@@ -28,7 +33,7 @@ I suppose this kind of [input file](https://github.com/septeni-original/embulk-p
28
33
  in:
29
34
  type: any file input plugin type
30
35
  parser:
31
- type: sample
36
+ type: twitter_ads_stats
32
37
  stop_on_invalid_record: true
33
38
  ```
34
39
 
@@ -75,5 +80,5 @@ sbt test
75
80
 
76
81
  ## Acknowledgement
77
82
 
78
- I developed this library with reference to [embulk-parser-firebase_avro](https://github.com/smdmts/embulk-parser-firebase_avro)
83
+ I developed this library with reference to [embulk-parser-firebase_avro](https://github.com/smdmts/embulk-parser-firebase_avro).
79
84
  Thank you very much.
data/build.gradle CHANGED
@@ -13,7 +13,7 @@ configurations {
13
13
  provided
14
14
  }
15
15
 
16
- version = "0.1.1"
16
+ version = "0.1.2"
17
17
 
18
18
  sourceCompatibility = 1.7
19
19
  targetCompatibility = 1.7
data/build.sbt CHANGED
@@ -1,15 +1,19 @@
1
- lazy val core = (project in file(".")).
2
- settings(
3
- inThisBuild(List(
1
+ lazy val core = (project in file(".")).settings(
2
+ inThisBuild(
3
+ List(
4
4
  organization := "jp.co.septeni-original",
5
5
  scalaVersion := "2.12.3",
6
6
  version := "0.1.0-SNAPSHOT"
7
- )),
8
- name := "embulk-parser-twitter_ads_stats"
9
- )
7
+ )
8
+ ),
9
+ name := "embulk-parser-twitter_ads_stats"
10
+ )
10
11
 
11
12
  enablePlugins(ScalafmtPlugin)
12
13
 
13
14
  resolvers += Resolver.jcenterRepo
14
15
  libraryDependencies ++= Dependencies.value
15
16
  scalacOptions += "-Xexperimental"
17
+
18
+ scalafmtVersion in ThisBuild := "1.2.0"
19
+ scalafmtOnCompile in ThisBuild := true
@@ -9,10 +9,10 @@ object Column {
9
9
  def createEmbulkColumns(metricElementNames: MetricElementNames): Seq[EmbulkColumn] = {
10
10
  @scala.annotation.tailrec
11
11
  def loop(
12
- curIndex: Int,
13
- curNames: List[String],
14
- acc: Seq[EmbulkColumn]
15
- ): Seq[EmbulkColumn] = {
12
+ curIndex: Int,
13
+ curNames: List[String],
14
+ acc: Seq[EmbulkColumn]
15
+ ): Seq[EmbulkColumn] = {
16
16
  curNames match {
17
17
  case Nil => acc.reverse
18
18
  case x :: xs =>
@@ -24,7 +24,6 @@ object Column {
24
24
  }
25
25
  }
26
26
 
27
-
28
27
  val baseColumns = Seq(
29
28
  new EmbulkColumn(0, "id", Types.STRING),
30
29
  new EmbulkColumn(1, "date", Types.STRING),
@@ -42,9 +41,9 @@ object Column {
42
41
  }
43
42
 
44
43
  case class Column(
45
- id: String,
46
- date: LocalDate,
47
- segment: Option[String],
48
- placement: String,
49
- metricsGroup: Map[String, MetricsGroup]
50
- )
44
+ id: String,
45
+ date: LocalDate,
46
+ segment: Option[String],
47
+ placement: String,
48
+ metricsGroup: Map[String, MetricsGroup]
49
+ )
@@ -18,9 +18,12 @@ case class MetricElementNames(names: Map[String, Seq[String]]) {
18
18
 
19
19
  import MetricElementNames._
20
20
 
21
- def getSortedMetricsGroupNames:List[String] = names.keys.toList.sorted
21
+ def getSortedMetricsGroupNames: List[String] = names.keys.toList.sorted
22
22
 
23
- private[twitter_ads_stats] def resolveMetrics(resolveMetricTimeSeries: (List[String], Option[JsValue]) => MetricTimeSeries, json: JsObject): Metrics =
23
+ private[twitter_ads_stats] def resolveMetrics(
24
+ resolveMetricTimeSeries: (List[String], Option[JsValue]) => MetricTimeSeries,
25
+ json: JsObject
26
+ ): Metrics =
24
27
  Metrics(
25
28
  names.flatMap { v =>
26
29
  v._2.map { value =>
@@ -44,4 +47,4 @@ object MetricElementNames {
44
47
  def replaceSeparator(name: String): String = {
45
48
  name.replaceAll("[.]", separator)
46
49
  }
47
- }
50
+ }
@@ -8,7 +8,7 @@ object MetricsGroupJson {
8
8
 
9
9
  private def toJValue(element: Option[Long]): JsValue = element match {
10
10
  case Some(e) => e.toJson
11
- case None => JsNull
11
+ case None => JsNull
12
12
  }
13
13
 
14
14
  override def write(obj: MetricsGroup): JsValue = {
@@ -1,25 +1,23 @@
1
1
  package org.embulk.parser.twitter_ads_stats
2
2
 
3
3
  sealed abstract class ParseException(
4
- message: String,
5
- cause: Throwable
6
- ) extends Throwable
7
-
4
+ message: String,
5
+ cause: Throwable
6
+ ) extends Throwable
8
7
 
9
8
  case class InvalidMetricTimeSeriesException(
10
- message: String,
11
- cause: Throwable
12
- ) extends ParseException(message, cause) {
9
+ message: String,
10
+ cause: Throwable
11
+ ) extends ParseException(message, cause) {
13
12
  def this(cause: Throwable, index: Int) = {
14
13
  this(s"Not Found index: $index", cause)
15
14
  }
16
15
  }
17
16
 
18
17
  case class InvalidInputFileException(
19
- message: String,
20
- cause: Throwable
21
- )
22
- extends ParseException(message, cause) {
18
+ message: String,
19
+ cause: Throwable
20
+ ) extends ParseException(message, cause) {
23
21
 
24
22
  def this(cause: Throwable) = {
25
23
  this(s"Input file can't parse", cause)
@@ -26,13 +26,13 @@ class TwitterAdsStatsParserPlugin extends ParserPlugin {
26
26
 
27
27
  override def run(taskSource: TaskSource, schema: Schema, input: FileInput, output: PageOutput): Unit = {
28
28
 
29
- val task = taskSource.loadTask(classOf[PluginTask])
29
+ val task = taskSource.loadTask(classOf[PluginTask])
30
30
  val stopOnInvalidRecord = task.getStopOnInvalidRecord
31
31
 
32
32
  LoanPattern(new PageBuilder(Exec.getBufferAllocator, schema, output)) { pb =>
33
33
  while (input.nextFile()) {
34
34
  (for {
35
- root <- createRootFrom(input)
35
+ root <- createRootFrom(input)
36
36
  columns <- root.resolveColumns(metricElementNames)
37
37
  } yield addRecord(pb, columns, root)) match {
38
38
  case Right(_) =>
@@ -54,15 +54,15 @@ class TwitterAdsStatsParserPlugin extends ParserPlugin {
54
54
  (column, embulkColumn.getName) match {
55
55
  case (Column(id, _, _, _, _), "id") =>
56
56
  pb.setString(embulkColumn, id)
57
- case (Column(_, date, _, _,_), "date") =>
57
+ case (Column(_, date, _, _, _), "date") =>
58
58
  pb.setString(embulkColumn, date.toString)
59
- case (Column(_, _, Some(segment),_, _), "segment") =>
59
+ case (Column(_, _, Some(segment), _, _), "segment") =>
60
60
  pb.setString(embulkColumn, segment)
61
- case (Column(_, _, None,_, _), "segment") =>
61
+ case (Column(_, _, None, _, _), "segment") =>
62
62
  pb.setNull(embulkColumn)
63
- case (Column(_, _, _,placement, _), "placement") =>
63
+ case (Column(_, _, _, placement, _), "placement") =>
64
64
  pb.setString(embulkColumn, placement)
65
- case (Column(_, _, _, _,metricsGroup), key) =>
65
+ case (Column(_, _, _, _, metricsGroup), key) =>
66
66
  metricsGroup.get(key) match {
67
67
  case Some(m) =>
68
68
  pb.setJson(
@@ -84,7 +84,7 @@ class TwitterAdsStatsParserPlugin extends ParserPlugin {
84
84
  val stream = new FileInputInputStream(input)
85
85
  try {
86
86
  val jsValue = scala.io.Source.fromInputStream(stream).mkString.parseJson
87
- val root = new RootJson(metricElementNames).RootReader.read(jsValue)
87
+ val root = new RootJson(metricElementNames).RootReader.read(jsValue)
88
88
  Right(root)
89
89
  } catch {
90
90
  case NonFatal(e) => Left(new InvalidInputFileException(e))
@@ -94,5 +94,5 @@ class TwitterAdsStatsParserPlugin extends ParserPlugin {
94
94
 
95
95
  object TwitterAdsStatsParserPlugin {
96
96
  val logger: Logger = Exec.getLogger(classOf[TwitterAdsStatsParserPlugin])
97
- val jsonParser = new JsonParser
97
+ val jsonParser = new JsonParser
98
98
  }
@@ -3,17 +3,20 @@ package org.embulk.parser.twitter_ads_stats.define
3
3
  import org.embulk.parser.twitter_ads_stats.{Column, MetricElementNames, ParseException}
4
4
 
5
5
  case class Data(id: String, id_data: Seq[IDData]) {
6
- private[define] def resolveColumns(metricElementNames: MetricElementNames, request: Request): Either[ParseException, Seq[Column]] = {
7
- id_data.map { idData =>
8
- idData.resolveColumns(id, metricElementNames, request)
9
- }.foldRight[Either[ParseException, Seq[Column]]](Right(Nil)) {
10
- case (Left(e), _) => Left(e)
11
- case (Right(_), Left(e)) => Left(e)
12
- case (Right(seq1), Right(seq2)) => Right(seq1 ++: seq2)
13
- }
6
+ private[define] def resolveColumns(metricElementNames: MetricElementNames,
7
+ request: Request): Either[ParseException, Seq[Column]] = {
8
+ id_data
9
+ .map { idData =>
10
+ idData.resolveColumns(id, metricElementNames, request)
11
+ }
12
+ .foldRight[Either[ParseException, Seq[Column]]](Right(Nil)) {
13
+ case (Left(e), _) => Left(e)
14
+ case (Right(_), Left(e)) => Left(e)
15
+ case (Right(seq1), Right(seq2)) => Right(seq1 ++: seq2)
16
+ }
14
17
  }
15
18
  }
16
19
 
17
20
  object Data {
18
- val fieldNames:Array[String] = FieldNameUtil.fieldList[Data]
19
- }
21
+ val fieldNames: Array[String] = FieldNameUtil.fieldList[Data]
22
+ }
@@ -6,28 +6,35 @@ import org.embulk.parser.twitter_ads_stats._
6
6
 
7
7
  case class IDData(metrics: Metrics, segment: Option[String]) {
8
8
  private def resolveColumn(
9
- id: String,
10
- metricElementNames: MetricElementNames,
11
- date: (LocalDate, Int),
12
- placement: String
13
- ): Either[ParseException, Column] = {
14
- metricElementNames.names.map { name =>
15
- (name._1, metrics.findMetricsGroup(date._2, name._2))
16
- }.foldRight[Either[ParseException, Map[String, MetricsGroup]]](Right(Map.empty)) {
17
- case ((_, Left(e)), _) => Left(e)
18
- case ((_, Right(_)), Left(e)) => Left(e)
19
- case ((s, Right(r)), Right(acc)) => Right(acc + (s -> r))
20
- }.map(Column(id, date._1, segment, placement, _))
9
+ id: String,
10
+ metricElementNames: MetricElementNames,
11
+ date: (LocalDate, Int),
12
+ placement: String
13
+ ): Either[ParseException, Column] = {
14
+ metricElementNames.names
15
+ .map { name =>
16
+ (name._1, metrics.findMetricsGroup(date._2, name._2))
17
+ }
18
+ .foldRight[Either[ParseException, Map[String, MetricsGroup]]](Right(Map.empty)) {
19
+ case ((_, Left(e)), _) => Left(e)
20
+ case ((_, Right(_)), Left(e)) => Left(e)
21
+ case ((s, Right(r)), Right(acc)) => Right(acc + (s -> r))
22
+ }
23
+ .map(Column(id, date._1, segment, placement, _))
21
24
  }
22
25
 
23
- private[define] def resolveColumns(id: String, metricElementNames: MetricElementNames, request: Request): Either[ParseException, Seq[Column]] = {
24
- request.params.targetDates.zipWithIndex.map { date =>
25
- resolveColumn(id, metricElementNames, date, request.params.placement)
26
- }.foldRight[Either[ParseException, Seq[Column]]](Right(Nil)) {
27
- case (Left(e), _) => Left(e)
28
- case (Right(_), Left(e)) => Left(e)
29
- case (Right(a), Right(seq)) => Right(a +: seq)
30
- }
26
+ private[define] def resolveColumns(id: String,
27
+ metricElementNames: MetricElementNames,
28
+ request: Request): Either[ParseException, Seq[Column]] = {
29
+ request.params.targetDates.zipWithIndex
30
+ .map { date =>
31
+ resolveColumn(id, metricElementNames, date, request.params.placement)
32
+ }
33
+ .foldRight[Either[ParseException, Seq[Column]]](Right(Nil)) {
34
+ case (Left(e), _) => Left(e)
35
+ case (Right(_), Left(e)) => Left(e)
36
+ case (Right(a), Right(seq)) => Right(a +: seq)
37
+ }
31
38
  }
32
39
  }
33
40
 
@@ -1,24 +1,25 @@
1
1
  package org.embulk.parser.twitter_ads_stats.define
2
2
 
3
- import java.time.{LocalDate, LocalDateTime}
3
+ import java.time.LocalDate
4
4
 
5
5
  case class Request(
6
- params: Params
7
- )
6
+ params: Params
7
+ )
8
8
 
9
9
  object Request {
10
10
  val fieldNames: Array[String] = FieldNameUtil.fieldList[Request]
11
11
  }
12
12
 
13
13
  case class Params(
14
- start_time: LocalDateTime,
15
- end_time: LocalDateTime,
16
- placement: String
17
- ) {
14
+ start_time: StatsDateTime,
15
+ end_time: StatsDateTime,
16
+ placement: String
17
+ ) {
18
18
  require(!start_time.isAfter(end_time))
19
+ require(start_time.isSameOffsetTime(end_time))
19
20
 
20
- val startDate = start_time.toLocalDate
21
- val endDate = end_time.toLocalDate
21
+ val startDate = start_time.adAccountLocalDate
22
+ val endDate = end_time.adAccountLocalDate
22
23
 
23
24
  /**
24
25
  * MetricTimeSeries の期間
@@ -29,7 +30,7 @@ case class Params(
29
30
  def loop(curDate: LocalDate, acc: List[LocalDate]): List[LocalDate] = {
30
31
  acc match {
31
32
  case x :: _ if !x.isBefore(endDate.minusDays(1)) => acc.reverse
32
- case _ => loop(curDate.plusDays(1), curDate :: acc)
33
+ case _ => loop(curDate.plusDays(1), curDate :: acc)
33
34
  }
34
35
  }
35
36
 
@@ -3,19 +3,21 @@ package org.embulk.parser.twitter_ads_stats.define
3
3
  import org.embulk.parser.twitter_ads_stats.{Column, MetricElementNames, ParseException}
4
4
 
5
5
  case class Root(
6
- data: Seq[Data],
7
- request: Request
8
- ) {
6
+ data: Seq[Data],
7
+ request: Request
8
+ ) {
9
9
  def resolveColumns(metricElementNames: MetricElementNames): Either[ParseException, Seq[Column]] = {
10
- data.map { d =>
11
- d.resolveColumns(metricElementNames, request)
12
- }.foldRight[Either[ParseException, Seq[Column]]](Right(Nil)) {
13
- case (Left(e), _) => Left(e)
14
- case (Right(_), Left(e)) => Left(e)
15
- case (Right(seq1), Right(seq2)) => Right(seq1 ++: seq2)
16
- }
10
+ data
11
+ .map { d =>
12
+ d.resolveColumns(metricElementNames, request)
13
+ }
14
+ .foldRight[Either[ParseException, Seq[Column]]](Right(Nil)) {
15
+ case (Left(e), _) => Left(e)
16
+ case (Right(_), Left(e)) => Left(e)
17
+ case (Right(seq1), Right(seq2)) => Right(seq1 ++: seq2)
18
+ }
17
19
  }
18
20
  }
19
21
  object Root {
20
- val fieldNames:Array[String] = FieldNameUtil.fieldList[Root]
21
- }
22
+ val fieldNames: Array[String] = FieldNameUtil.fieldList[Root]
23
+ }
@@ -1,8 +1,5 @@
1
1
  package org.embulk.parser.twitter_ads_stats.define
2
2
 
3
- import java.time.LocalDateTime
4
- import java.time.format.DateTimeFormatter
5
-
6
3
  import org.embulk.parser.twitter_ads_stats.{MetricElementNames, MetricTimeSeries}
7
4
  import spray.json.{DefaultJsonProtocol, DeserializationException, JsArray, JsString, JsValue, RootJsonReader}
8
5
 
@@ -13,7 +10,7 @@ class RootJson(metricElementNames: MetricElementNames) extends DefaultJsonProtoc
13
10
  private def readMetricTimeSeries(jsValue: JsValue): MetricTimeSeries = {
14
11
  jsValue match {
15
12
  case JsArray(arr) => Some(arr.map(_.convertTo[Long]))
16
- case _ => None
13
+ case _ => None
17
14
  }
18
15
  }
19
16
 
@@ -68,11 +65,9 @@ class RootJson(metricElementNames: MetricElementNames) extends DefaultJsonProtoc
68
65
  val fieldNames = Params.fieldNames.toList
69
66
  json.asJsObject.getFields(fieldNames: _*) match {
70
67
  case Seq(JsString(a), JsString(b), c) =>
71
- val formatter = DateTimeFormatter.ISO_DATE_TIME
72
- //todo DateTimeFormatterでフォーマットが出来ない場合の例外処理
73
68
  Params(
74
- LocalDateTime.parse(a, formatter),
75
- LocalDateTime.parse(b, formatter),
69
+ StatsDateTime(a),
70
+ StatsDateTime(b),
76
71
  c.convertTo[String]
77
72
  )
78
73
  case x => throw DeserializationException(msg = s"params can't deserialize json: $x", fieldNames = fieldNames)
@@ -0,0 +1,25 @@
1
+ package org.embulk.parser.twitter_ads_stats.define
2
+
3
+ import java.time.{LocalDate, LocalDateTime}
4
+ import java.time.format.DateTimeFormatter
5
+
6
+ /**
7
+ * @param iso8601DateTime This datetime represents midnight in the timezone of the advertiser's account.
8
+ */
9
+ case class StatsDateTime(iso8601DateTime: String) {
10
+
11
+ private val utcDateTime: LocalDateTime = LocalDateTime.parse(iso8601DateTime, DateTimeFormatter.ISO_DATE_TIME)
12
+
13
+ def adAccountLocalDate: LocalDate = utcDateTime.plusHours(StatsDateTime.DateLineOffsetHours).toLocalDate
14
+
15
+ def isAfter(that: StatsDateTime): Boolean = this.utcDateTime.isAfter(that.utcDateTime)
16
+
17
+ def isSameOffsetTime(that: StatsDateTime): Boolean = this.utcDateTime.toLocalTime == that.utcDateTime.toLocalTime
18
+
19
+ }
20
+
21
+ object StatsDateTime {
22
+
23
+ val DateLineOffsetHours = 12
24
+
25
+ }
@@ -2,7 +2,7 @@ package org.embulk.parser
2
2
 
3
3
  package object twitter_ads_stats {
4
4
  type MetricTimeSeries = Option[Vector[Long]]
5
- type MetricsGroup = Map[String, Option[Long]]
5
+ type MetricsGroup = Map[String, Option[Long]]
6
6
 
7
7
  val metricElementNames = MetricElementNames(
8
8
  Map(
@@ -6,7 +6,7 @@ class MetricElementNamesSpec extends UnitSpec {
6
6
  val names = MetricElementNames(
7
7
  Map("b" -> emptySeq, "a" -> emptySeq, "c" -> emptySeq)
8
8
  )
9
- val actual = names.getSortedMetricsGroupNames
9
+ val actual = names.getSortedMetricsGroupNames
10
10
  val expected = List("a", "b", "c")
11
11
 
12
12
  assert(actual == expected)
@@ -1,6 +1,6 @@
1
1
  package org.embulk.parser.twitter_ads_stats
2
2
 
3
- import spray.json.{JsNull, JsNumber, JsObject, pimpAny}
3
+ import spray.json.{pimpAny, JsNull, JsNumber, JsObject}
4
4
 
5
5
  class MetricsGroupJsonSpec extends UnitSpec {
6
6
  "json write" in {
@@ -11,8 +11,8 @@ class MetricsGroupJsonSpec extends UnitSpec {
11
11
  val actual = v.toJson
12
12
 
13
13
  val expected = JsObject(
14
- "a" -> JsNumber(3),
15
- "b" -> JsNumber(6),
14
+ "a" -> JsNumber(3),
15
+ "b" -> JsNumber(6),
16
16
  "c_e" -> JsNull
17
17
  )
18
18
  assert(actual == expected)
@@ -44,8 +44,8 @@ class MetricsJsonSpec extends UnitSpec {
44
44
  val actual = new RootJson(metricElementNames).MetricsReader.read(jsValue)
45
45
  val expected = Metrics(
46
46
  Map(
47
- "a" -> Some(Vector(510, 494, 364)),
48
- "b" -> Some(Vector(1, 2, 3)),
47
+ "a" -> Some(Vector(510, 494, 364)),
48
+ "b" -> Some(Vector(1, 2, 3)),
49
49
  "c_e" -> Some(Vector(1, 2, 3)),
50
50
  "c_f" -> None,
51
51
  "d_e" -> Some(Vector(2, 3, 4)),
@@ -1,12 +1,12 @@
1
1
  package org.embulk.parser.twitter_ads_stats.define
2
2
 
3
- import java.time.{LocalDate, LocalDateTime}
3
+ import java.time.LocalDate
4
4
 
5
5
  import org.embulk.parser.twitter_ads_stats.UnitSpec
6
6
 
7
7
  class ParamsSpec extends UnitSpec {
8
8
  "開始日~(終了日-1)の日程を取得する" in {
9
- val period = Params(LocalDateTime.of(2017, 1, 1, 0, 0, 0), LocalDateTime.of(2017, 1, 4, 23, 23, 23), "")
9
+ val period = Params(StatsDateTime("2016-12-31T15:00:00Z"), StatsDateTime("2017-01-03T15:00:00Z"), "")
10
10
  val actual = period.targetDates
11
11
  val expected = List(
12
12
  LocalDate.of(2017, 1, 1),
@@ -16,7 +16,7 @@ class ParamsSpec extends UnitSpec {
16
16
  assert(actual == expected)
17
17
  }
18
18
  "開始日から終了日までの全日程は、開始日から終了日が同一な場合は同一な日程となる" in {
19
- val period = Params(LocalDateTime.of(2017, 1, 1, 0, 0, 0), LocalDateTime.of(2017, 1, 1, 23, 23, 23), "")
19
+ val period = Params(StatsDateTime("2016-12-31T15:00:00Z"), StatsDateTime("2017-01-01T15:00:00Z"), "")
20
20
  val actual = period.targetDates
21
21
  val expected = List(
22
22
  LocalDate.of(2017, 1, 1)
@@ -17,4 +17,4 @@ class RootJsonSpec extends UnitSpec {
17
17
  val root = new RootJson(twitter_ads_stats.metricElementNames).RootReader.read(largeJsonSource)
18
18
  // println(root)
19
19
  }
20
- }
20
+ }
@@ -20,11 +20,11 @@ class RootSpec extends UnitSpec {
20
20
  "",
21
21
  Map(
22
22
  "media" -> Map(
23
- "media_views" -> Some(1),
23
+ "media_views" -> Some(1),
24
24
  "media_engagements" -> None
25
25
  ),
26
26
  "billing" -> Map(
27
- "billed_engagements" -> Some(1),
27
+ "billed_engagements" -> Some(1),
28
28
  "billed_charge_local_micro" -> Some(1)
29
29
  ),
30
30
  "web_conversion" -> Map(
@@ -39,11 +39,11 @@ class RootSpec extends UnitSpec {
39
39
  "",
40
40
  Map(
41
41
  "media" -> Map(
42
- "media_views" -> Some(2),
42
+ "media_views" -> Some(2),
43
43
  "media_engagements" -> None
44
44
  ),
45
45
  "billing" -> Map(
46
- "billed_engagements" -> Some(2),
46
+ "billed_engagements" -> Some(2),
47
47
  "billed_charge_local_micro" -> Some(2)
48
48
  ),
49
49
  "web_conversion" -> Map(
@@ -58,13 +58,13 @@ class RootSpec extends UnitSpec {
58
58
  "",
59
59
  Map(
60
60
  "media" -> Map(
61
- "media_views" -> Some(10),
61
+ "media_views" -> Some(10),
62
62
  "media_engagements" -> None
63
63
  ),
64
64
  "billing" -> Map(
65
- "billed_engagements" -> Some(10),
66
- "billed_charge_local_micro" -> Some(10)
67
- ),
65
+ "billed_engagements" -> Some(10),
66
+ "billed_charge_local_micro" -> Some(10)
67
+ ),
68
68
  "web_conversion" -> Map(
69
69
  "conversion_purchases_assisted" -> Some(10)
70
70
  )
@@ -77,11 +77,11 @@ class RootSpec extends UnitSpec {
77
77
  "",
78
78
  Map(
79
79
  "media" -> Map(
80
- "media_views" -> Some(20),
80
+ "media_views" -> Some(20),
81
81
  "media_engagements" -> None
82
82
  ),
83
83
  "billing" -> Map(
84
- "billed_engagements" -> Some(20),
84
+ "billed_engagements" -> Some(20),
85
85
  "billed_charge_local_micro" -> Some(20)
86
86
  ),
87
87
  "web_conversion" -> Map(
@@ -96,13 +96,13 @@ class RootSpec extends UnitSpec {
96
96
  "",
97
97
  Map(
98
98
  "media" -> Map(
99
- "media_views" -> Some(1),
99
+ "media_views" -> Some(1),
100
100
  "media_engagements" -> None
101
101
  ),
102
102
  "billing" -> Map(
103
- "billed_engagements" -> Some(1),
104
- "billed_charge_local_micro" -> Some(1)
105
- ),
103
+ "billed_engagements" -> Some(1),
104
+ "billed_charge_local_micro" -> Some(1)
105
+ ),
106
106
  "web_conversion" -> Map(
107
107
  "conversion_purchases_assisted" -> Some(1)
108
108
  )
@@ -115,11 +115,11 @@ class RootSpec extends UnitSpec {
115
115
  "",
116
116
  Map(
117
117
  "media" -> Map(
118
- "media_views" -> Some(2),
118
+ "media_views" -> Some(2),
119
119
  "media_engagements" -> None
120
120
  ),
121
121
  "billing" -> Map(
122
- "billed_engagements" -> Some(2),
122
+ "billed_engagements" -> Some(2),
123
123
  "billed_charge_local_micro" -> Some(2)
124
124
  ),
125
125
  "web_conversion" -> Map(
@@ -134,13 +134,13 @@ class RootSpec extends UnitSpec {
134
134
  "",
135
135
  Map(
136
136
  "media" -> Map(
137
- "media_views" -> Some(10),
137
+ "media_views" -> Some(10),
138
138
  "media_engagements" -> None
139
139
  ),
140
140
  "billing" -> Map(
141
- "billed_engagements" -> Some(10),
142
- "billed_charge_local_micro" -> Some(10)
143
- ),
141
+ "billed_engagements" -> Some(10),
142
+ "billed_charge_local_micro" -> Some(10)
143
+ ),
144
144
  "web_conversion" -> Map(
145
145
  "conversion_purchases_assisted" -> Some(10)
146
146
  )
@@ -153,11 +153,11 @@ class RootSpec extends UnitSpec {
153
153
  "",
154
154
  Map(
155
155
  "media" -> Map(
156
- "media_views" -> Some(20),
156
+ "media_views" -> Some(20),
157
157
  "media_engagements" -> None
158
158
  ),
159
159
  "billing" -> Map(
160
- "billed_engagements" -> Some(20),
160
+ "billed_engagements" -> Some(20),
161
161
  "billed_charge_local_micro" -> Some(20)
162
162
  ),
163
163
  "web_conversion" -> Map(
@@ -175,8 +175,8 @@ class RootSpec extends UnitSpec {
175
175
  val actual = createRoot(
176
176
  Request(
177
177
  params = Params(
178
- start_time = LocalDateTime.of(2017, 1, 1, 1, 1, 1),
179
- end_time = LocalDateTime.of(2017, 1, 4, 1, 1, 1),
178
+ start_time = StatsDateTime("2017-01-01T01:01:01Z"),
179
+ end_time = StatsDateTime("2017-01-04T01:01:01Z"),
180
180
  placement = ""
181
181
  )
182
182
  )
@@ -192,17 +192,17 @@ class RootSpec extends UnitSpec {
192
192
  val names = MetricElementNames(
193
193
  Map(
194
194
  "media" ->
195
- Seq(
196
- "media_views"
197
- )
195
+ Seq(
196
+ "media_views"
197
+ )
198
198
  )
199
199
  )
200
200
 
201
201
  val actual = createRoot(
202
202
  Request(
203
203
  params = Params(
204
- start_time = LocalDateTime.of(2017, 1, 1, 1, 1, 1),
205
- end_time = LocalDateTime.of(2017, 1, 1, 1, 1, 1),
204
+ start_time = StatsDateTime("2017-01-01T01:01:01Z"),
205
+ end_time = StatsDateTime("2017-01-01T01:01:01Z"),
206
206
  placement = ""
207
207
  )
208
208
  )
@@ -265,8 +265,8 @@ object RootSpec {
265
265
  val defaultRoot: Root = createRoot(
266
266
  Request(
267
267
  params = Params(
268
- start_time = LocalDateTime.of(2017, 1, 1, 1, 1, 1),
269
- end_time = LocalDateTime.of(2017, 1, 3, 1, 1, 1),
268
+ start_time = StatsDateTime("2017-01-01T01:01:01Z"),
269
+ end_time = StatsDateTime("2017-01-03T01:01:01Z"),
270
270
  placement = ""
271
271
  )
272
272
  )
@@ -281,10 +281,10 @@ object RootSpec {
281
281
  IDData(
282
282
  metrics = Metrics(
283
283
  Map(
284
- "billed_engagements" -> Some(Vector(1, 2)),
285
- "billed_charge_local_micro" -> Some(Vector(1, 2)),
286
- "media_views" -> Some(Vector(1, 2)),
287
- "media_engagements" -> None,
284
+ "billed_engagements" -> Some(Vector(1, 2)),
285
+ "billed_charge_local_micro" -> Some(Vector(1, 2)),
286
+ "media_views" -> Some(Vector(1, 2)),
287
+ "media_engagements" -> None,
288
288
  "conversion_purchases_assisted" -> Some(Vector(1, 2))
289
289
  )
290
290
  ),
@@ -293,10 +293,10 @@ object RootSpec {
293
293
  IDData(
294
294
  metrics = Metrics(
295
295
  Map(
296
- "billed_engagements" -> Some(Vector(10, 20)),
297
- "billed_charge_local_micro" -> Some(Vector(10, 20)),
298
- "media_views" -> Some(Vector(10, 20)),
299
- "media_engagements" -> None,
296
+ "billed_engagements" -> Some(Vector(10, 20)),
297
+ "billed_charge_local_micro" -> Some(Vector(10, 20)),
298
+ "media_views" -> Some(Vector(10, 20)),
299
+ "media_engagements" -> None,
300
300
  "conversion_purchases_assisted" -> Some(Vector(10, 20))
301
301
  )
302
302
  ),
@@ -310,10 +310,10 @@ object RootSpec {
310
310
  IDData(
311
311
  metrics = Metrics(
312
312
  Map(
313
- "billed_engagements" -> Some(Vector(1, 2)),
314
- "billed_charge_local_micro" -> Some(Vector(1, 2)),
315
- "media_views" -> Some(Vector(1, 2)),
316
- "media_engagements" -> None,
313
+ "billed_engagements" -> Some(Vector(1, 2)),
314
+ "billed_charge_local_micro" -> Some(Vector(1, 2)),
315
+ "media_views" -> Some(Vector(1, 2)),
316
+ "media_engagements" -> None,
317
317
  "conversion_purchases_assisted" -> Some(Vector(1, 2))
318
318
  )
319
319
  ),
@@ -322,10 +322,10 @@ object RootSpec {
322
322
  IDData(
323
323
  metrics = Metrics(
324
324
  Map(
325
- "billed_engagements" -> Some(Vector(10, 20)),
326
- "billed_charge_local_micro" -> Some(Vector(10, 20)),
327
- "media_views" -> Some(Vector(10, 20)),
328
- "media_engagements" -> None,
325
+ "billed_engagements" -> Some(Vector(10, 20)),
326
+ "billed_charge_local_micro" -> Some(Vector(10, 20)),
327
+ "media_views" -> Some(Vector(10, 20)),
328
+ "media_engagements" -> None,
329
329
  "conversion_purchases_assisted" -> Some(Vector(10, 20))
330
330
  )
331
331
  ),
@@ -0,0 +1,21 @@
1
+ package org.embulk.parser.twitter_ads_stats.define
2
+
3
+ import java.time.LocalDate
4
+
5
+ import org.embulk.parser.twitter_ads_stats.UnitSpec
6
+
7
+ class StatsDateTimeSpec extends UnitSpec {
8
+
9
+ "Twitter APIの日時パラメータをアドアカウントでのローカル日付に変換できる" should {
10
+ "2017-08-20T15:00:00Z は 2017/08/21になる(JST)" in {
11
+ assert(StatsDateTime("2017-08-20T15:00:00Z").adAccountLocalDate == LocalDate.of(2017, 8, 21))
12
+ }
13
+ "2017-08-20T08:00:00Z は 2017/08/20になる(PST)" in {
14
+ assert(StatsDateTime("2017-08-20T08:00:00Z").adAccountLocalDate == LocalDate.of(2017, 8, 20))
15
+ }
16
+ "2017-08-20T10:00:00Z は 2017/08/20になる(Pacific/Honolulu)" in {
17
+ assert(StatsDateTime("2017-08-20T10:00:00Z").adAccountLocalDate == LocalDate.of(2017, 8, 20))
18
+ }
19
+ }
20
+
21
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: embulk-parser-twitter_ads_stats
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - kimutyam
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-25 00:00:00.000000000 Z
11
+ date: 2017-11-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -47,6 +47,7 @@ extra_rdoc_files: []
47
47
  files:
48
48
  - .gitignore
49
49
  - .scalafmt.conf
50
+ - .travis.yml
50
51
  - LICENSE.txt
51
52
  - README.md
52
53
  - build.gradle
@@ -74,6 +75,7 @@ files:
74
75
  - src/main/scala/org/embulk/parser/twitter_ads_stats/define/Request.scala
75
76
  - src/main/scala/org/embulk/parser/twitter_ads_stats/define/Root.scala
76
77
  - src/main/scala/org/embulk/parser/twitter_ads_stats/define/RootJson.scala
78
+ - src/main/scala/org/embulk/parser/twitter_ads_stats/define/StatsDateTime.scala
77
79
  - src/main/scala/org/embulk/parser/twitter_ads_stats/package.scala
78
80
  - src/test/resources/test.json
79
81
  - src/test/scala/org/embulk/parser/twitter_ads_stats/ColumnSpec.scala
@@ -84,7 +86,8 @@ files:
84
86
  - src/test/scala/org/embulk/parser/twitter_ads_stats/define/ParamsSpec.scala
85
87
  - src/test/scala/org/embulk/parser/twitter_ads_stats/define/RootJsonSpec.scala
86
88
  - src/test/scala/org/embulk/parser/twitter_ads_stats/define/RootSpec.scala
87
- - classpath/embulk-parser-twitter_ads_stats-0.1.1.jar
89
+ - src/test/scala/org/embulk/parser/twitter_ads_stats/define/StatsDateTimeSpec.scala
90
+ - classpath/embulk-parser-twitter_ads_stats-0.1.2.jar
88
91
  - classpath/scala-library-2.12.3.jar
89
92
  - classpath/spray-json_2.12-1.3.3.jar
90
93
  homepage: https://github.com/septeni-original/embulk-parser-twitter_ads_stats