bamboozled-gitlab 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
  4. data/.github/ISSUE_TEMPLATE/question.md +19 -0
  5. data/.github/main.workflow +38 -0
  6. data/.github/pull_request_template.md +27 -0
  7. data/.gitignore +38 -0
  8. data/.rubocop.yml +39 -0
  9. data/.rubocop_todo.yml +305 -0
  10. data/.travis.yml +18 -0
  11. data/CHANGELOG.md +48 -0
  12. data/CONTRIBUTING.md +91 -0
  13. data/Dockerfile +8 -0
  14. data/Gemfile +16 -0
  15. data/Guardfile +21 -0
  16. data/LICENSE +22 -0
  17. data/README.md +170 -0
  18. data/Rakefile +11 -0
  19. data/bamboozled.gemspec +30 -0
  20. data/examples/employees_over_time.rb +53 -0
  21. data/lib/bamboozled.rb +24 -0
  22. data/lib/bamboozled/api/base.rb +101 -0
  23. data/lib/bamboozled/api/employee.rb +118 -0
  24. data/lib/bamboozled/api/field_collection.rb +107 -0
  25. data/lib/bamboozled/api/meta.rb +25 -0
  26. data/lib/bamboozled/api/report.rb +20 -0
  27. data/lib/bamboozled/api/time_off.rb +34 -0
  28. data/lib/bamboozled/api/time_tracking.rb +24 -0
  29. data/lib/bamboozled/base.rb +31 -0
  30. data/lib/bamboozled/errors.rb +33 -0
  31. data/lib/bamboozled/ext/yesno.rb +11 -0
  32. data/lib/bamboozled/version.rb +3 -0
  33. data/logos/bamboozled_logo_black.png +0 -0
  34. data/logos/bamboozled_logo_green.png +0 -0
  35. data/logos/skookum_mark_black.png +0 -0
  36. data/logos/skookum_mark_black.svg +175 -0
  37. data/relnotes/v0.1.0.md +13 -0
  38. data/spec/fixtures/add_employee_details.json +7 -0
  39. data/spec/fixtures/add_employee_response.json +4 -0
  40. data/spec/fixtures/add_employee_xml.yml +8 -0
  41. data/spec/fixtures/all_employees.json +58 -0
  42. data/spec/fixtures/custom_report.json +38 -0
  43. data/spec/fixtures/employee_emails.json +9 -0
  44. data/spec/fixtures/employee_table_details.json +17 -0
  45. data/spec/fixtures/job_info.xml +22 -0
  46. data/spec/fixtures/last_changed.json +28 -0
  47. data/spec/fixtures/meta_fields.json +5 -0
  48. data/spec/fixtures/meta_lists.json +5 -0
  49. data/spec/fixtures/meta_tables.json +5 -0
  50. data/spec/fixtures/meta_users.json +4 -0
  51. data/spec/fixtures/one_employee.json +9 -0
  52. data/spec/fixtures/time_off_estimate.json +23 -0
  53. data/spec/fixtures/time_tracking_add_200_response.json +7 -0
  54. data/spec/fixtures/time_tracking_add_empty_response.json +9 -0
  55. data/spec/fixtures/time_tracking_adjust_200_response.json +7 -0
  56. data/spec/fixtures/time_tracking_adjust_400_response.json +11 -0
  57. data/spec/fixtures/time_tracking_record_200_response.json +9 -0
  58. data/spec/fixtures/time_tracking_record_400_response.json +11 -0
  59. data/spec/fixtures/time_tracking_record_401_response.json +10 -0
  60. data/spec/fixtures/time_tracking_record_404_response.json +8 -0
  61. data/spec/fixtures/update_employee_details.json +7 -0
  62. data/spec/fixtures/update_employee_response.json +3 -0
  63. data/spec/fixtures/update_employee_table.json +8 -0
  64. data/spec/fixtures/update_employee_table_xml.yml +6 -0
  65. data/spec/fixtures/update_employee_xml.yml +8 -0
  66. data/spec/lib/bamboozled/api/base_spec.rb +18 -0
  67. data/spec/lib/bamboozled/api/employee_spec.rb +186 -0
  68. data/spec/lib/bamboozled/api/field_collection_spec.rb +17 -0
  69. data/spec/lib/bamboozled/api/meta_spec.rb +47 -0
  70. data/spec/lib/bamboozled/api/report_spec.rb +17 -0
  71. data/spec/lib/bamboozled/api/time_tracking_spec.rb +123 -0
  72. data/spec/lib/bamboozled/base_spec.rb +26 -0
  73. data/spec/lib/bamboozled_spec.rb +33 -0
  74. data/spec/spec_helper.rb +32 -0
  75. metadata +237 -0
@@ -0,0 +1,34 @@
1
+ module Bamboozled
2
+ module API
3
+ class TimeOff < Base
4
+ def requests(options = {})
5
+ allowed_parameters = %i[id action employeeId start end type status]
6
+ options = options.keep_if { |k, _| allowed_parameters.include? k }
7
+
8
+ # Convert non string dates to strings.
9
+ %i[start end].each do |action|
10
+ if options[action] && !options[action].is_a?(String)
11
+ options[action] = options[action].strftime("%F")
12
+ end
13
+ end
14
+
15
+ # Make sure all statuses are allowed
16
+ if options[:status]
17
+ allowed_statuses = %w[approved denied superceded requested canceled]
18
+ options[:status] = Array(options[:status])
19
+ .keep_if { |v| allowed_statuses.include? v }
20
+ .join(",")
21
+ end
22
+
23
+ request(:get, "time_off/requests?#{URI.encode_www_form(options)}")
24
+ end
25
+
26
+ def whos_out(start_date, end_date = nil)
27
+ start_date = start_date.strftime("%F") unless start_date.is_a?(String)
28
+ end_date = end_date.strftime("%F") unless end_date.nil? ||
29
+ end_date.is_a?(String)
30
+ request(:get, "time_off/whos_out?start=#{start_date}&end=#{end_date}")
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ module Bamboozled
2
+ module API
3
+ class TimeTracking < Base
4
+ def record(time_tracking_id)
5
+ request(:get, "timetracking/record/#{time_tracking_id}")
6
+ end
7
+
8
+ def add(time_tracking_details)
9
+ # details = generate_xml(time_tracking_details)
10
+ details = time_tracking_details.to_json
11
+ options = { body: details, headers: { 'Content-Type' => 'application/json' } }
12
+
13
+ request(:post, "timetracking/add/", options)
14
+ end
15
+
16
+ def adjust(time_tracking_id, hours_worked)
17
+ details = { timeTrackingId: time_tracking_id, hoursWorked: hours_worked }.to_json
18
+ options = { body: details, headers: { 'Content-Type' => 'application/json' } }
19
+
20
+ request(:put, "timetracking/adjust", options)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,31 @@
1
+ module Bamboozled
2
+ class Base
3
+ attr_reader :request
4
+
5
+ def initialize(subdomain: nil, api_key: nil, httparty_options: {})
6
+ @subdomain, @api_key = subdomain
7
+ @api_key = api_key
8
+ @httparty_options = httparty_options
9
+ end
10
+
11
+ def employee
12
+ @employee ||= Bamboozled::API::Employee.new(@subdomain, @api_key, @httparty_options)
13
+ end
14
+
15
+ def report
16
+ @report ||= Bamboozled::API::Report.new(@subdomain, @api_key, @httparty_options)
17
+ end
18
+
19
+ def meta
20
+ @meta ||= Bamboozled::API::Meta.new(@subdomain, @api_key, @httparty_options)
21
+ end
22
+
23
+ def time_off
24
+ @time_off ||= Bamboozled::API::TimeOff.new(@subdomain, @api_key, @httparty_options)
25
+ end
26
+
27
+ def time_tracking
28
+ @time_tracking ||= Bamboozled::API::TimeTracking.new(@subdomain, @api_key, @httparty_options)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,33 @@
1
+ module Bamboozled
2
+ class HTTPError < StandardError
3
+ attr_reader :response
4
+ attr_reader :params
5
+ attr_reader :hint
6
+
7
+ def initialize(response, params = {}, hint = nil)
8
+ @response = response
9
+ @params = params
10
+ @hint = hint
11
+ super(response)
12
+ end
13
+
14
+ def to_s
15
+ "#{self.class} : #{response.code} #{response.body}".tap do |msg|
16
+ msg << "\n#{hint}" if hint
17
+ end
18
+ end
19
+ end
20
+
21
+ class BadRequest < HTTPError; end
22
+ class Unauthorized < HTTPError; end
23
+ class AuthenticationFailed < HTTPError; end
24
+ class Forbidden < HTTPError; end
25
+ class NotFound < HTTPError; end
26
+ class NotAcceptable < HTTPError; end
27
+ class Conflict < HTTPError; end
28
+ class LimitExceeded < HTTPError; end
29
+ class InternalServerError < HTTPError; end
30
+ class GatewayError < HTTPError; end
31
+ class ServiceUnavailable < HTTPError; end
32
+ class InformBamboo < HTTPError; end
33
+ end
@@ -0,0 +1,11 @@
1
+ class TrueClass
2
+ def yesno
3
+ "yes"
4
+ end
5
+ end
6
+
7
+ class FalseClass
8
+ def yesno
9
+ "no"
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module Bamboozled
2
+ VERSION = "0.2.9".freeze
3
+ end
Binary file
Binary file
Binary file
@@ -0,0 +1,175 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
5
+ viewBox="0 0 808 808" enable-background="new 0 0 808 808" xml:space="preserve">
6
+ <g enable-background="new ">
7
+ <g>
8
+ <defs>
9
+ <rect id="SVGID_1_" x="120" y="163" width="570" height="486"/>
10
+ </defs>
11
+ <clipPath id="SVGID_2_">
12
+ <use xlink:href="#SVGID_1_" overflow="visible"/>
13
+ </clipPath>
14
+ <g clip-path="url(#SVGID_2_)">
15
+ <defs>
16
+ <rect id="SVGID_3_" width="808" height="808"/>
17
+ </defs>
18
+ <clipPath id="SVGID_4_">
19
+ <use xlink:href="#SVGID_3_" overflow="visible"/>
20
+ </clipPath>
21
+ <g clip-path="url(#SVGID_4_)">
22
+ <defs>
23
+ <path id="SVGID_5_" d="M593.3,648.6H423.4c-2.9,0-5.3-2.4-5.3-5.3l-0.1-269.1c0-1.2,1.4-1.3,1.6-0.1l13.7,91.6
24
+ c4.7,31,27.7,70.5,52.2,89.9l109.9,86.6C600.1,645.7,599.1,648.6,593.3,648.6"/>
25
+ </defs>
26
+ <clipPath id="SVGID_6_">
27
+ <use xlink:href="#SVGID_5_" overflow="visible"/>
28
+ </clipPath>
29
+ <g clip-path="url(#SVGID_6_)">
30
+ <defs>
31
+ <rect id="SVGID_7_" width="808" height="808"/>
32
+ </defs>
33
+ <clipPath id="SVGID_8_">
34
+ <use xlink:href="#SVGID_7_" overflow="visible"/>
35
+ </clipPath>
36
+ <rect x="413.1" y="368.2" clip-path="url(#SVGID_8_)" width="190.3" height="285.4"/>
37
+ </g>
38
+ </g>
39
+ </g>
40
+ <g clip-path="url(#SVGID_2_)">
41
+ <defs>
42
+ <rect id="SVGID_9_" width="808" height="808"/>
43
+ </defs>
44
+ <clipPath id="SVGID_10_">
45
+ <use xlink:href="#SVGID_9_" overflow="visible"/>
46
+ </clipPath>
47
+ <g clip-path="url(#SVGID_10_)">
48
+ <defs>
49
+ <path id="SVGID_11_" d="M388.7,374.1l-13.8,91.6c-4.7,31-27.7,70.5-52.2,89.9l-109.9,86.6c-4.6,3.6-3.6,6.5,2.2,6.5h169.9
50
+ c2.9,0,5.3-2.4,5.3-5.3l0.1-269.1C390.2,373,388.8,372.9,388.7,374.1"/>
51
+ </defs>
52
+ <clipPath id="SVGID_12_">
53
+ <use xlink:href="#SVGID_11_" overflow="visible"/>
54
+ </clipPath>
55
+ <g clip-path="url(#SVGID_12_)">
56
+ <defs>
57
+ <rect id="SVGID_13_" width="808" height="808"/>
58
+ </defs>
59
+ <clipPath id="SVGID_14_">
60
+ <use xlink:href="#SVGID_13_" overflow="visible"/>
61
+ </clipPath>
62
+ <rect x="204.9" y="368.2" clip-path="url(#SVGID_14_)" width="190.3" height="285.4"/>
63
+ </g>
64
+ </g>
65
+ </g>
66
+ <g clip-path="url(#SVGID_2_)">
67
+ <defs>
68
+ <rect id="SVGID_15_" width="808" height="808"/>
69
+ </defs>
70
+ <clipPath id="SVGID_16_">
71
+ <use xlink:href="#SVGID_15_" overflow="visible"/>
72
+ </clipPath>
73
+ <g clip-path="url(#SVGID_16_)">
74
+ <defs>
75
+ <path id="SVGID_17_" d="M686.3,484.6L497,167.7c-1.4-2.3-4.6-4.1-7.3-4.1h-170c-2.6,0-5.9,1.9-7.3,4.1l-189,316.8
76
+ c-2.9,4.9-0.9,7.4,4.5,5.7L283.5,440c12.8-4.1,28-18.9,32.6-31.6l63.8-178.1c1-2.7,4.1-5,7-5h34.8c2.9,0,6.1,2.3,7,5
77
+ l63.8,178.1c4.6,12.7,19.8,27.5,32.6,31.6l156.8,50.4C687.2,492,689.2,489.5,686.3,484.6"/>
78
+ </defs>
79
+ <clipPath id="SVGID_18_">
80
+ <use xlink:href="#SVGID_17_" overflow="visible"/>
81
+ </clipPath>
82
+ <g clip-path="url(#SVGID_18_)">
83
+ <defs>
84
+ <rect id="SVGID_19_" width="808" height="808"/>
85
+ </defs>
86
+ <clipPath id="SVGID_20_">
87
+ <use xlink:href="#SVGID_19_" overflow="visible"/>
88
+ </clipPath>
89
+ <rect x="117.2" y="158.6" clip-path="url(#SVGID_20_)" width="575.4" height="337.2"/>
90
+ </g>
91
+ </g>
92
+ </g>
93
+ <g clip-path="url(#SVGID_2_)">
94
+ <defs>
95
+ <rect id="SVGID_21_" width="808" height="808"/>
96
+ </defs>
97
+ <clipPath id="SVGID_22_">
98
+ <use xlink:href="#SVGID_21_" overflow="visible"/>
99
+ </clipPath>
100
+ <g clip-path="url(#SVGID_22_)">
101
+ <defs>
102
+ <path id="SVGID_23_" d="M347.4,461.4l13.6-90.2c0.2-1.3-1.5-2-2-0.5l-16.7,47.2c-7.4,20.7-29.5,42.1-50.2,48.8l-162.5,52.5
103
+ c-3.1,1-4.2,4-2.4,6.8l59.2,94c1.7,2.7,5.2,3.3,7.7,1.3l111.5-87.8C324.5,518.5,343.7,485.5,347.4,461.4"/>
104
+ </defs>
105
+ <clipPath id="SVGID_24_">
106
+ <use xlink:href="#SVGID_23_" overflow="visible"/>
107
+ </clipPath>
108
+ <g clip-path="url(#SVGID_24_)">
109
+ <defs>
110
+ <rect id="SVGID_25_" width="808" height="808"/>
111
+ </defs>
112
+ <clipPath id="SVGID_26_">
113
+ <use xlink:href="#SVGID_25_" overflow="visible"/>
114
+ </clipPath>
115
+ <rect x="121.2" y="364.9" clip-path="url(#SVGID_26_)" width="244.8" height="262.6"/>
116
+ </g>
117
+ </g>
118
+ </g>
119
+ <g clip-path="url(#SVGID_2_)">
120
+ <defs>
121
+ <rect id="SVGID_27_" width="808" height="808"/>
122
+ </defs>
123
+ <clipPath id="SVGID_28_">
124
+ <use xlink:href="#SVGID_27_" overflow="visible"/>
125
+ </clipPath>
126
+ <g clip-path="url(#SVGID_28_)">
127
+ <defs>
128
+ <path id="SVGID_29_" d="M678.9,519.1l-162.5-52.5c-20.8-6.7-42.8-28.2-50.2-48.8l-16.7-47.2c-0.5-1.5-2.2-0.8-2,0.5l13.5,90.2
129
+ c3.7,24,22.8,57.1,41.9,72l111.5,87.8c2.5,2,6,1.4,7.7-1.3l59.3-94C683,523.2,681.9,520.1,678.9,519.1"/>
130
+ </defs>
131
+ <clipPath id="SVGID_30_">
132
+ <use xlink:href="#SVGID_29_" overflow="visible"/>
133
+ </clipPath>
134
+ <g clip-path="url(#SVGID_30_)">
135
+ <defs>
136
+ <rect id="SVGID_31_" width="808" height="808"/>
137
+ </defs>
138
+ <clipPath id="SVGID_32_">
139
+ <use xlink:href="#SVGID_31_" overflow="visible"/>
140
+ </clipPath>
141
+ <rect x="442.4" y="364.9" clip-path="url(#SVGID_32_)" width="244.7" height="262.6"/>
142
+ </g>
143
+ </g>
144
+ </g>
145
+ <g clip-path="url(#SVGID_2_)">
146
+ <defs>
147
+ <rect id="SVGID_33_" width="808" height="808"/>
148
+ </defs>
149
+ <clipPath id="SVGID_34_">
150
+ <use xlink:href="#SVGID_33_" overflow="visible"/>
151
+ </clipPath>
152
+ <g clip-path="url(#SVGID_34_)">
153
+ <defs>
154
+ <path id="SVGID_35_" d="M590.5,246.3c0-9.2,7.3-16.8,16.2-16.8s16.2,7.6,16.2,16.8s-7.3,16.8-16.2,16.8
155
+ S590.5,255.5,590.5,246.3z M586,246.3c0,11.8,9.3,21.3,20.7,21.3c11.5,0,20.7-9.5,20.7-21.3c0-11.8-9.3-21.3-20.7-21.3
156
+ C595.3,225,586,234.5,586,246.3z M610.2,242c0,3.2-2.9,3.4-6.9,3.4v-6.6C608.4,238.8,610.2,239.1,610.2,242z M616.8,258
157
+ l-6.7-9.5c3-1,4.6-3.8,4.6-6.6c0-4.8-2.8-6.8-11.2-6.8h-4.6V258h4.4v-9.3h1.5l6.7,9.3H616.8z"/>
158
+ </defs>
159
+ <clipPath id="SVGID_36_">
160
+ <use xlink:href="#SVGID_35_" overflow="visible"/>
161
+ </clipPath>
162
+ <g clip-path="url(#SVGID_36_)">
163
+ <defs>
164
+ <rect id="SVGID_37_" width="808" height="808"/>
165
+ </defs>
166
+ <clipPath id="SVGID_38_">
167
+ <use xlink:href="#SVGID_37_" overflow="visible"/>
168
+ </clipPath>
169
+ <rect x="581" y="220" clip-path="url(#SVGID_38_)" fill="#393939" width="51.5" height="52.7"/>
170
+ </g>
171
+ </g>
172
+ </g>
173
+ </g>
174
+ </g>
175
+ </svg>
@@ -0,0 +1,13 @@
1
+ ### New Features
2
+
3
+ * New method `Employee#add` allows client to create new employees. ([@kylefdoherty][])
4
+ * New method `Employee#update` allows client to update new employees. ([@kylefdoherty][])
5
+
6
+ ### Changes
7
+
8
+ ### Bug Fixes
9
+
10
+ * Added missing documentation for `Employee#add`. ([@mjording][])
11
+
12
+ [@kylefdoherty]: https://github.com/kylefdoherty
13
+ [@mjording]: https://github.com/mjording
@@ -0,0 +1,7 @@
1
+ {
2
+ "firstName": "Bruce",
3
+ "lastName": "Wayne",
4
+ "workEmail": "bruce.wayne@gmail.com",
5
+ "jobTitle": "Batman",
6
+ "city": "Gotham"
7
+ }
@@ -0,0 +1,4 @@
1
+ HTTP/1.1 200 OK
2
+ content-type: application/json; charset=utf-8
3
+ date: Tue, 17 Jun 2014 19:25:35 UTC
4
+ location: https://api.bamboohr.com/api/gateway.php/alphasights/v1/employees/44259
@@ -0,0 +1,8 @@
1
+ body:
2
+ <employee><field id='firstName'>Bruce</field><field id='lastName'>Wayne</field><field id='workEmail'>bruce.wayne@gmail.com</field><field id='jobTitle'>Batman</field><field id='city'>Gotham</field></employee>
3
+ headers:
4
+ Accept:
5
+ - application/json
6
+ User-Agent:
7
+ - Bamboozled/0.2.0
8
+
@@ -0,0 +1,58 @@
1
+ HTTP/1.1 200 OK
2
+ content-type: application/json; charset=utf-8
3
+ date: Tue, 17 Jun 2014 19:25:35 UTC
4
+
5
+ {
6
+ "fields": [
7
+ {
8
+ "id": "displayName",
9
+ "type": "text",
10
+ "name": "Display Name"
11
+ },
12
+ {
13
+ "id": "firstName",
14
+ "type": "text",
15
+ "name": "First Name"
16
+ },
17
+ {
18
+ "id": "lastName",
19
+ "type": "text",
20
+ "name": "Last Name"
21
+ },
22
+ {
23
+ "id": "jobTitle",
24
+ "type": "list",
25
+ "name": "Job Title"
26
+ },
27
+ {
28
+ "id": "workPhone",
29
+ "type": "text",
30
+ "name": "Work Phone"
31
+ },
32
+ {
33
+ "id": "workPhoneExtension",
34
+ "type": "text",
35
+ "name": "Work Extension"
36
+ }
37
+ ],
38
+ "employees": [
39
+ {
40
+ "id":123,
41
+ "displayName":"John Doe",
42
+ "firstName":"John",
43
+ "lastName":"Doe",
44
+ "jobTitle":"Customer Service Representative",
45
+ "workPhone":"555-555-5555",
46
+ "workPhoneExtension":null
47
+ },
48
+ {
49
+ "id":124,
50
+ "displayName":"John Doe",
51
+ "firstName":"John",
52
+ "lastName":"Doe",
53
+ "jobTitle":"Customer Service Representative",
54
+ "workPhone":"555-555-5555",
55
+ "workPhoneExtension":null
56
+ }
57
+ ]
58
+ }
@@ -0,0 +1,38 @@
1
+ HTTP/1.1 200 OK
2
+ content-type: application/json; charset=utf-8
3
+ date: Tue, 17 Jun 2014 19:25:35 UTC
4
+
5
+ {
6
+ "employees": [
7
+ {
8
+ "bestEmail": "rachel@example.com",
9
+ "birthday": "05-04",
10
+ "employeeNumber": "100",
11
+ "id": "3"
12
+ },
13
+ {
14
+ "bestEmail": "bob@example.com",
15
+ "birthday": "08-29",
16
+ "employeeNumber": "101",
17
+ "id": "4"
18
+ }
19
+ ],
20
+ "fields": [
21
+ {
22
+ "id": "bestEmail",
23
+ "name": "Email",
24
+ "type": "text"
25
+ },
26
+ {
27
+ "id": "employeeNumber",
28
+ "name": "Employee #",
29
+ "type": "employee_number"
30
+ },
31
+ {
32
+ "id": "birthday",
33
+ "name": "Birthday",
34
+ "type": "text"
35
+ }
36
+ ],
37
+ "title": "Report"
38
+ }