ZReviewTender 1.3.5 → 1.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aa2d4d55b850012e36f3368bfdf84e301acb719914bdfe9873af9bd5ab6ab2ee
4
- data.tar.gz: b101d60289e1bea2f5d02f77ae1dda04d5faaec33289a72aa3c0b661d4858c58
3
+ metadata.gz: 77c4a5c74b6076acbf3ec3ea98046ab703f1df3485c4590c9f1de62bda77fad9
4
+ data.tar.gz: 1b8bc52b07eac4add166d360b648fa5991264d501b72a39c1dc7a412da51413c
5
5
  SHA512:
6
- metadata.gz: d9b16dd294304c8e220ddaa07933ddb0b135965030ac72fd6669e02d675ab117e93759c28e456e29ccd50c99f65fde9615bb3a6ecc598bb8eb5a0c50f8ea4ddd
7
- data.tar.gz: 72c179f0bf24134eec589857c59f29c23f8b621683a808564e5fc49c0f65ac9463ca4c184fb91d62aeca1adfe11f8ce1a93f1774b423155f88720d71592e6260
6
+ metadata.gz: 8bba8ffe64bb4342689ade1b868a4a369e7879641c927466792cfef62f1860b3508efb1d289c5fea8c0bb6741780e2ce491c063814b327e4b85c2b699851dff7
7
+ data.tar.gz: 5cea45b9203f52e95ff67169fec881981e501e68b8e7cbc2298798e483963a44684d17e0337a7536b99ed2ba6f08f99bf531b762cf0848d8326c4864ba5bdc99
data/.version CHANGED
@@ -1 +1 @@
1
- 1.3.5
1
+ 1.3.7
@@ -29,8 +29,11 @@ processors:
29
29
  enable: false # enable
30
30
  googleSheetAPIKeyFilePath: "" # Google Translate API Service Account Credential .json File Path
31
31
  googleSheetTimeZoneOffset: "+08:00" # Review Created Date TimeZone
32
- googleSheetID: "" # Google Sheet ID, you can get it on google sheet url: e.g. https://docs.google.com/spreadsheets/d/googleSheetID/
33
- googleSheetName: "Sheet1" # Sheet Name
32
+ googleSheetInsertStyle:
33
+ - type: "append" # Google Sheet Insert type, append or insert
34
+ - at: 0 # required if type is insert, where index should insert at
35
+ - sheetID: null # required if type is insert, the sheet ID, you can get it on google sheet url: e.g. https://docs.google.com/spreadsheets/d/googleSpreadsheetID/edit#gid=sheetID
36
+ - sheetName: "Sheet1" # required if type is append, a.k.a google sheet tab name
34
37
  values: ["%RATING%","%TITLE%\n%BODY%","%APPVERSION%","%CREATEDDATE%"] # Columns Data, you can uses magic variable below to compose string.
35
38
  # %TITLE% for review's title
36
39
  # %BODY% for review's content
@@ -43,6 +46,30 @@ processors:
43
46
  # %APPVERSION% for review's reviewer app version
44
47
  # %CREATEDDATE% for review's created date
45
48
 
49
+ keywordsInclude: [] # keywords you want to filter out
50
+ ratingsInclude: [] # ratings you want to filter out
51
+ territoriesInclude: [] # territories you want to filter out(language for android e.g. zh-Hant, en)
52
+ - AsanaProcessor:
53
+ class: "AsanaProcessor"
54
+ enable: false # enable
55
+ asanaTimeZoneOffset: "+08:00"
56
+ asanaToken: "" # Asana Personal Access Token, get it here -> https://app.asana.com/0/my-apps
57
+ asanaProjectID: "" # Asana Project ID, get it in project url -> https://app.asana.com/0/asanaProjectID/list
58
+ asanaSectionName: "" # Task Target Project - Section Name, optional
59
+ asanaTaskTitleTemplate: "%PLATFORM% - %RATING% ⭐️ - %TITLE%" # Asana Task Title Template
60
+ asanaTaskBodyTemplate: "Title: %TITLE%\n---\nBody:\n%BODY%\n---\n- UserName: %USERNAME%\n- App Version: %APPVERSION%\n- Date:%CREATEDDATE%" # Asana Task Body Template
61
+ # you can uses magic variable below to compose string.
62
+ # %TITLE% for review's title
63
+ # %BODY% for review's content
64
+ # %RATING% for review's rating 1~5
65
+ # %PLATFORM% for review's platform Apple or Android
66
+ # %ID% for review's ID
67
+ # %USERNAME% for review's reviewer username
68
+ # %URL% for link to review
69
+ # %TERRITORY% for review's territory (territory for Apple e.g. TWN)
70
+ # %APPVERSION% for review's reviewer app version
71
+ # %CREATEDDATE% for review's created date
72
+
46
73
  keywordsInclude: [] # keywords you want to filter out
47
74
  ratingsInclude: [] # ratings you want to filter out
48
75
  territoriesInclude: [] # territories you want to filter out(language for android e.g. zh-Hant, en)
@@ -29,8 +29,12 @@ processors:
29
29
  enable: false # enable
30
30
  googleSheetAPIKeyFilePath: "" # Google Translate API Service Account Credential .json File Path
31
31
  googleSheetTimeZoneOffset: "+08:00" # Review Created Date TimeZone
32
- googleSheetID: "" # Google Sheet ID, you can get it on google sheet url: e.g. https://docs.google.com/spreadsheets/d/googleSheetID/
33
- googleSheetName: "Sheet1" # Sheet Name
32
+ googleSpreadsheetID: "1_lc82p-epecVKwpUWlXh1yyNWaSrygzDb8I0QH2xkTI" # Google Sheet SpreadSheet ID, you can get it on google sheet url: e.g. https://docs.google.com/spreadsheets/d/googleSpreadsheetID/
33
+ googleSheetInsertStyle:
34
+ - type: "append" # Google Sheet Insert type, append or insert
35
+ - at: 0 # required if type is insert, where index should insert at
36
+ - sheetID: null # required if type is insert, the sheet ID, you can get it on google sheet url: e.g. https://docs.google.com/spreadsheets/d/googleSpreadsheetID/edit#gid=sheetID
37
+ - sheetName: "Sheet1" # required if type is append, a.k.a google sheet tab name
34
38
  values: ["%RATING%","%TITLE%\n%BODY%","%APPVERSION%","%CREATEDDATE%"] # Columns Data, you can uses magic variable below to compose string.
35
39
  # %TITLE% for review's title
36
40
  # %BODY% for review's content
@@ -43,6 +47,30 @@ processors:
43
47
  # %APPVERSION% for review's reviewer app version
44
48
  # %CREATEDDATE% for review's created date
45
49
 
50
+ keywordsInclude: [] # keywords you want to filter out
51
+ ratingsInclude: [] # ratings you want to filter out
52
+ territoriesInclude: [] # territories you want to filter out (territory for Apple e.g. TWN)
53
+ - AsanaProcessor:
54
+ class: "AsanaProcessor"
55
+ enable: false # enable
56
+ asanaTimeZoneOffset: "+08:00"
57
+ asanaToken: "" # Asana Personal Access Token, get it here -> https://app.asana.com/0/my-apps
58
+ asanaProjectID: "" # Asana Project ID, get it in project url -> https://app.asana.com/0/asanaProjectID/list
59
+ asanaSectionName: "" # Task Target Project - Section Name, optional
60
+ asanaTaskTitleTemplate: "%PLATFORM% - %RATING% ⭐️ - %TITLE%" # Asana Task Title Template
61
+ asanaTaskBodyTemplate: "Title: %TITLE%\n---\nBody:\n%BODY%\n---\n- UserName: %USERNAME%\n- App Version: %APPVERSION%\n- Date:%CREATEDDATE%" # Asana Task Body Template
62
+ # you can uses magic variable below to compose string.
63
+ # %TITLE% for review's title
64
+ # %BODY% for review's content
65
+ # %RATING% for review's rating 1~5
66
+ # %PLATFORM% for review's platform Apple or Android
67
+ # %ID% for review's ID
68
+ # %USERNAME% for review's reviewer username
69
+ # %URL% for link to review
70
+ # %TERRITORY% for review's territory (territory for Apple e.g. TWN)
71
+ # %APPVERSION% for review's reviewer app version
72
+ # %CREATEDDATE% for review's created date
73
+
46
74
  keywordsInclude: [] # keywords you want to filter out
47
75
  ratingsInclude: [] # ratings you want to filter out
48
76
  territoriesInclude: [] # territories you want to filter out (territory for Apple e.g. TWN)
@@ -13,4 +13,19 @@ class Processor
13
13
  def processReviews(reviews, platform)
14
14
 
15
15
  end
16
+
17
+ def renderReview(templateText, review, timeZoneOffset)
18
+ templateText = templateText.gsub("%TITLE%", review.title || "")
19
+ templateText = templateText.gsub("%BODY%", review.body || "")
20
+ templateText = templateText.gsub("%RATING%", review.rating.nil? ? "" :review.rating.to_s)
21
+ templateText = templateText.gsub("%PLATFORM%", review.platform || "")
22
+ templateText = templateText.gsub("%ID%", review.id || "")
23
+ templateText = templateText.gsub("%USERNAME%", review.userName || "")
24
+ templateText = templateText.gsub("%URL%", review.url || "")
25
+ templateText = templateText.gsub("%TERRITORY%", review.territory || "")
26
+ templateText = templateText.gsub("%APPVERSION%", review.appVersion || "")
27
+ templateText = templateText.gsub("%CREATEDDATE%", review.createdDateTimestamp.nil? ? "" : Time.at(review.createdDateTimestamp).getlocal(timeZoneOffset).to_s)
28
+
29
+ return templateText
30
+ end
16
31
  end
@@ -20,7 +20,15 @@ class ReviewFetcher
20
20
 
21
21
  def processReviews(reviews, platform)
22
22
  processors.each do |processor|
23
- reviews = processor.processReviews(reviews, platform)
23
+ begin
24
+ reviews = processor.processReviews(reviews, platform)
25
+ rescue => e
26
+ errorMessage = "# Processor Error"
27
+ errorMessage += "#Error Message: #{e.message}\n"
28
+ errorMessage += "#Error Class: #{e.class}\n"
29
+ errorMessage += "#Backtrace Start#\n#{e.backtrace.join("\n")}\n#Backtrace End#\n"
30
+ logger.logError(errorMessage)
31
+ end
24
32
  end
25
33
  end
26
34
 
@@ -0,0 +1,131 @@
1
+ $lib = File.expand_path('../lib', File.dirname(__FILE__))
2
+
3
+ require "Models/Review"
4
+ require "Models/Processor"
5
+ require "Helper"
6
+ require "pathname"
7
+ require "GoogleAPI"
8
+
9
+ class AsanaProcessor < Processor
10
+
11
+ attr_accessor :keywordsInclude, :ratingsInclude, :territoriesInclude, :logger, :timeZoneOffset, :token, :projectID, :sectionName, :taskTitleTemplate, :taskBodyTemplate, :asanaAPIURL
12
+
13
+ def initialize(config, configFilePath, baseExecutePath)
14
+ @config = config
15
+ @configFilePath = configFilePath
16
+ @baseExecutePath = baseExecutePath
17
+ @logger = ZLogger.new(baseExecutePath)
18
+
19
+ @asanaAPIURL = "https://app.asana.com/api/1.0"
20
+ @token = Helper.unwrapRequiredParameter(config, "asanaToken")
21
+ @projectID = Helper.unwrapRequiredParameter(config, "asanaProjectID")
22
+ @taskTitleTemplate = Helper.unwrapRequiredParameter(config, "asanaTaskTitleTemplate")
23
+ @taskBodyTemplate = Helper.unwrapRequiredParameter(config, "asanaTaskBodyTemplate")
24
+ @timeZoneOffset = Helper.unwrapRequiredParameter(config, "asanaTimeZoneOffset")
25
+ @sectionName = nil
26
+ if !config["asanaSectionName"].nil? && config["asanaSectionName"] != ""
27
+ @sectionName = config["asanaSectionName"].strip
28
+ end
29
+
30
+ @keywordsInclude = []
31
+ if !config["keywordsInclude"].nil?
32
+ @keywordsInclude = config["keywordsInclude"]
33
+ end
34
+
35
+ @ratingsInclude = []
36
+ if !config["ratingsInclude"].nil?
37
+ @ratingsInclude = config["ratingsInclude"]
38
+ end
39
+
40
+ @territoriesInclude = []
41
+ if !config["territoriesInclude"].nil?
42
+ @territoriesInclude = config["territoriesInclude"]
43
+ end
44
+
45
+ puts "[AsanaProcessor] Init Success."
46
+ end
47
+
48
+ def processReviews(reviews, platform)
49
+ if reviews.length < 1
50
+ return reviews
51
+ end
52
+
53
+ sectionID = findSectionIDFromSectionName(sectionName)
54
+
55
+ filterReviews = reviews
56
+
57
+ if ratingsInclude.length > 0
58
+ filterReviews = filterReviews.select{ |review| ratingsInclude.map{ |rating| rating.to_i }.include? review.rating }
59
+ end
60
+
61
+ if territoriesInclude.length > 0
62
+ filterReviews = filterReviews.select{ |review| territoriesInclude.map{ |territory| territory.upcase }.include? review.territory.upcase }
63
+ end
64
+
65
+ if keywordsInclude.length > 0
66
+ keywordsInclude.select{ |keywordsInclude| keywordsInclude != "" }.each do |keywordInclude|
67
+ filterReviews = filterReviews.select{ |review| review.body.include? keywordInclude }
68
+ end
69
+ end
70
+
71
+ filterReviews.each do |review|
72
+ title = renderReview(taskTitleTemplate, review, timeZoneOffset)
73
+ body = renderReview(taskBodyTemplate, review, timeZoneOffset)
74
+
75
+ requestTaskData = {
76
+ "name": title,
77
+ "notes": body,
78
+ "projects": [projectID.to_s]
79
+ }
80
+
81
+ if !review.createdDateTimestamp.nil?
82
+ requestTaskData['due_at'] = Time.at(review.createdDateTimestamp).iso8601
83
+ end
84
+
85
+ taskData = asanaAPI("/tasks", "POST", requestTaskData)
86
+ taskData = taskData["data"]
87
+
88
+ if !sectionID.nil? && !taskData.nil?
89
+ asanaAPI("/sections/#{sectionID}/addTask", "POST", {"task": taskData["gid"]})
90
+ end
91
+
92
+ puts "[AsanaProcessor] Insert Review #{title} as a task to asana project."
93
+ end
94
+
95
+ return reviews
96
+ end
97
+
98
+ private
99
+ def findSectionIDFromSectionName(sectionName)
100
+ if !sectionName.nil?
101
+ sections = asanaAPI("/projects/#{projectID}/sections")["data"]
102
+ section = sections.find{ |section| section["name"].strip == sectionName }
103
+ if !section.nil?
104
+ return section["gid"]
105
+ end
106
+ end
107
+
108
+ return nil
109
+ end
110
+
111
+ private
112
+ def asanaAPI(path, method = "GET", data = nil)
113
+ uri = URI(asanaAPIURL+path)
114
+ https = Net::HTTP.new(uri.host, uri.port)
115
+ https.use_ssl = true
116
+
117
+ request = Net::HTTP::Get.new(uri)
118
+ if method.upcase == "POST"
119
+ request = Net::HTTP::Post.new(uri)
120
+ if !data.nil?
121
+ request['Content-Type'] = 'application/json'
122
+ request.body = JSON.dump({"data": data})
123
+ end
124
+ end
125
+
126
+ request['Authorization'] = "Bearer #{token}";
127
+
128
+ response = https.request(request).read_body
129
+ result = JSON.parse(response)
130
+ end
131
+ end
@@ -8,7 +8,7 @@ require "GoogleAPI"
8
8
 
9
9
  class GoogleSheetProcessor < Processor
10
10
 
11
- attr_accessor :keywordsInclude, :ratingsInclude, :territoriesInclude, :logger, :googleAPI, :sheetID, :sheetName, :formatValues, :timeZoneOffset
11
+ attr_accessor :keywordsInclude, :ratingsInclude, :territoriesInclude, :logger, :googleAPI, :spreadsheetID, :sheetInsertType, :sheetId, :sheetName, :sheetInsertAt, :formatValues, :timeZoneOffset
12
12
 
13
13
  def initialize(config, configFilePath, baseExecutePath)
14
14
  @config = config
@@ -41,8 +41,31 @@ class GoogleSheetProcessor < Processor
41
41
  end
42
42
 
43
43
  @timeZoneOffset = Helper.unwrapRequiredParameter(config, "googleSheetTimeZoneOffset")
44
- @sheetID = Helper.unwrapRequiredParameter(config, "googleSheetID")
45
- @sheetName = Helper.unwrapRequiredParameter(config, "googleSheetName")
44
+ @spreadsheetID = Helper.unwrapRequiredParameter(config, "googleSpreadsheetID")
45
+
46
+ sheetInsertStyle = Helper.unwrapRequiredParameter(config, "googleSheetInsertStyle")
47
+ if !sheetInsertStyle.is_a? Array
48
+ raise "googleSheetInsertStyle must specify as Array in GoogleSheetProcessor."
49
+ end
50
+
51
+ sheetInsertStyles = {}
52
+ sheetInsertStyle.each do |value|
53
+ value.keys.each do |key|
54
+ sheetInsertStyles[key] = value[key]
55
+ end
56
+ end
57
+
58
+ @sheetInsertType = Helper.unwrapRequiredParameter(sheetInsertStyles, "type")
59
+
60
+ if sheetInsertType != "insert" && sheetInsertType != "append"
61
+ raise "googleSheetInsertStyle.type only accept insert or append in GoogleSheetProcessor."
62
+ elsif sheetInsertType == "insert"
63
+ @sheetInsertAt = Helper.unwrapRequiredParameter(sheetInsertStyles, "at").to_i
64
+ @sheetId = Helper.unwrapRequiredParameter(sheetInsertStyles, "sheetID")
65
+ elsif sheetInsertType == "append"
66
+ @sheetName = Helper.unwrapRequiredParameter(sheetInsertStyles, "sheetName")
67
+ end
68
+
46
69
  @formatValues = []
47
70
  if !config["values"].nil?
48
71
  @formatValues = config["values"]
@@ -74,33 +97,68 @@ class GoogleSheetProcessor < Processor
74
97
  end
75
98
 
76
99
  values = []
77
- filterReviews.each do |review|
100
+ sortedFilterReviews = filterReviews
101
+ if sheetInsertType == "insert"
102
+ sortedFilterReviews = sortedFilterReviews.sort! { |a, b| b.createdDateTimestamp <=> a.createdDateTimestamp }
103
+ end
104
+
105
+ sortedFilterReviews.each do |review|
78
106
  cols = []
79
107
  formatValues.each do |formatValue|
80
- formatValue = formatValue.gsub("%TITLE%", review.title || "")
81
- formatValue = formatValue.gsub("%BODY%", review.body || "")
82
- formatValue = formatValue.gsub("%RATING%", review.rating.nil? ? "" :review.rating.to_s)
83
- formatValue = formatValue.gsub("%PLATFORM%", review.platform || "")
84
- formatValue = formatValue.gsub("%ID%", review.id || "")
85
- formatValue = formatValue.gsub("%USERNAME%", review.userName || "")
86
- formatValue = formatValue.gsub("%URL%", review.url || "")
87
- formatValue = formatValue.gsub("%TERRITORY%", review.territory || "")
88
- formatValue = formatValue.gsub("%APPVERSION%", review.appVersion || "")
89
- formatValue = formatValue.gsub("%CREATEDDATE%", review.createdDateTimestamp.nil? ? "" : Time.at(review.createdDateTimestamp).getlocal(timeZoneOffset).to_s)
90
-
108
+ formatValue = renderReview(formatValue, review, timeZoneOffset)
91
109
  cols.append(formatValue)
92
110
  end
93
111
  values.append(cols)
94
112
  end
95
113
 
96
114
  page = 1
97
- limit = 500
115
+ limit = 100
98
116
  values.each_slice(limit) do |value|
99
- puts "[GoogleSheetProcessor] Insert rows(#{page}/#{(values.length/limit).ceil + 1}) to #{sheetID}-#{sheetName}"
117
+ puts "[GoogleSheetProcessor] Insert rows page:(#{page}/#{(values.length/limit).ceil + 1}) #{sheetInsertType} to #{spreadsheetID}"
100
118
  page += 1
101
- googleAPI.request("https://sheets.googleapis.com/v4/spreadsheets/#{sheetID}/values/#{sheetName}!A1:append?valueInputOption=RAW", "POST", {:values => value})
119
+ if sheetInsertType == "insert"
120
+ googleAPI.request("https://sheets.googleapis.com/v4/spreadsheets/#{spreadsheetID}:batchUpdate", "POST", {
121
+ "requests": [
122
+ {
123
+ "insertRange": {
124
+ "range": {
125
+ "sheetId": sheetId,
126
+ "startRowIndex": sheetInsertAt,
127
+ "endRowIndex": sheetInsertAt + value.length
128
+ },
129
+ "shiftDimension": "ROWS"
130
+ }
131
+ },
132
+ {
133
+ "pasteData": {
134
+ "data": rowsToString(value),
135
+ "type": "PASTE_NORMAL",
136
+ "delimiter": ",",
137
+ "coordinate": {
138
+ "sheetId": sheetId,
139
+ "rowIndex": sheetInsertAt,
140
+ }
141
+ }
142
+ }
143
+ ]
144
+ })
145
+ elsif sheetInsertType == "append"
146
+ googleAPI.request("https://sheets.googleapis.com/v4/spreadsheets/#{spreadsheetID}/values/#{sheetName}!A1:append?valueInputOption=RAW", "POST", {:values => value})
147
+ end
102
148
  end
103
149
 
104
150
  return reviews
105
151
  end
152
+
153
+ private
154
+ def rowsToString(rows)
155
+ string = ""
156
+ rows.each do |row|
157
+ if string != ""
158
+ string += "\n"
159
+ end
160
+ string += "\"#{row.map{ |v| v.gsub('"','\"') }.join('","')}\""
161
+ end
162
+ return string
163
+ end
106
164
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ZReviewTender
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.5
4
+ version: 1.3.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - ZhgChgLi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-10-04 00:00:00.000000000 Z
11
+ date: 2022-12-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-http
@@ -59,6 +59,7 @@ files:
59
59
  - lib/Models/Review.rb
60
60
  - lib/Models/ReviewFetcher.rb
61
61
  - lib/Models/Version.rb
62
+ - lib/Processors/AsanaProcessor.rb
62
63
  - lib/Processors/FilterProcessor.rb
63
64
  - lib/Processors/GoogleSheetProcessor.rb
64
65
  - lib/Processors/GoogleTranslateProcessor.rb