projector_pws 0.1.0
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 +7 -0
- data/.gitignore +3 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +21 -0
- data/README.md +213 -0
- data/Rakefile +9 -0
- data/lib/projector_pws/version.rb +9 -0
- data/lib/projector_pws.rb +259 -0
- data/projector_pws.gemspec +25 -0
- metadata +122 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 45d785b1f6f1ff2701b369ef782874d78c1205c2
|
4
|
+
data.tar.gz: d09550953bff128a3ab30daf083a5e91af78ac01
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dd771285d83de784fecd46c54ee9ba749c53a899b9ac097c4d285a45a3b3c45b01c33a0538dad3e0d8c6af99f919b5348c7c04f35d71dce93e9ba594103554ee
|
7
|
+
data.tar.gz: 7e414dfbaaab808c0a984b1c4e76c144e616348a6a0e5b10f729c9c3db95e54a7f745ae0f80eca76246c2448bd39b7651ab6cf36efe953e024c38017fb97fc00
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Paul Duncan
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,213 @@
|
|
1
|
+
# ProjectorPWS
|
2
|
+
|
3
|
+
Client library for accessing Projector PSA\'s Web Services (PWS)
|
4
|
+
|
5
|
+
## Presentation
|
6
|
+
|
7
|
+
This library provides access to Projector PSA\'s Web Services (PWS).
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
### Gemfile
|
12
|
+
```ruby
|
13
|
+
gem 'projector_pws'
|
14
|
+
```
|
15
|
+
|
16
|
+
### Terminal
|
17
|
+
```bash
|
18
|
+
gem install -V projector_pws
|
19
|
+
```
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
### Symbolize Keys
|
24
|
+
|
25
|
+
A _sym_keys_ method is monkey-patched into the *Array* and *Hash* classes, and provides a way to recursively convert *keys* to *symbols*.
|
26
|
+
This is mostly useful when loading Hashes from external resources (YAML, CSV, JSON, etc...), where keys will be stored as strings.
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
a = { 'foo' => 'bar', 'nested' => [{ 'problems' => 'none' }] }
|
30
|
+
a.sym_keys
|
31
|
+
# => {:foo=>"bar", :nested=>[{:problems=>"none"}]}
|
32
|
+
```
|
33
|
+
|
34
|
+
### Deep-clone
|
35
|
+
|
36
|
+
A _dclone_ method is monkey-patched into the *Array* and *Hash* classes, and provides a way to recursively *deep-clone* an instance.
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
a = { foo: 'bar', bar: :kill_me, nested: [{ problems: 'none' }] }
|
40
|
+
b = a.dclone
|
41
|
+
b[:nested] << :foo
|
42
|
+
b[:nested][:problems] = 'no way'
|
43
|
+
b[:test] = :bork
|
44
|
+
b[:foo] = nil
|
45
|
+
b.delete :bar
|
46
|
+
a
|
47
|
+
# => {:foo=>nil, :nested=>[{:problems=>"no way"}, :foo], :test => :bork}
|
48
|
+
b
|
49
|
+
# => {:foo=>"bar", :nested=>[{:problems=>"none"}]}
|
50
|
+
```
|
51
|
+
|
52
|
+
### Right/Left Padding
|
53
|
+
|
54
|
+
Two padding methods _rpad_ and _lpad_ are monkey-patched into the *String* class, and provide a way to pad any string to a given length using a given filler string.
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
a = 'foo'
|
58
|
+
a.rpad 10
|
59
|
+
# => "foo "
|
60
|
+
a.rpad 10, '-'
|
61
|
+
# => "foo-------"
|
62
|
+
a.rpad 2, '-'
|
63
|
+
# => "fo"
|
64
|
+
a.lpad 10
|
65
|
+
# => " foo"
|
66
|
+
a.lpad 10, '-'
|
67
|
+
# => "-------foo"
|
68
|
+
a.rpad 2, '-'
|
69
|
+
# => "oo"
|
70
|
+
```
|
71
|
+
|
72
|
+
### Object Try (Undefined Method Call)
|
73
|
+
|
74
|
+
A _try_ method is monkey-patched into the *Object* root class, providing a means to call undefined methods on objects without raising anything (returning *nil* instead).
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
'foobar'.try :foo
|
78
|
+
# => nil
|
79
|
+
32.try :to_s
|
80
|
+
# => '32'
|
81
|
+
32.try :+, 3
|
82
|
+
# => 35
|
83
|
+
['foo', 'bar'].try(:collect) { |e| "-#{e}-" }
|
84
|
+
# => ['-foo-', '-bar-']
|
85
|
+
```
|
86
|
+
|
87
|
+
### Get Non-Empty String
|
88
|
+
|
89
|
+
A _nstr_ method is monkey-patched into the *String* class. This method returns nil when the String is empty, the String otherwise.
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
''.nstr
|
93
|
+
# => nil
|
94
|
+
'foo'.nstr
|
95
|
+
# => 'foo'
|
96
|
+
nil.try :nstr
|
97
|
+
# => nil
|
98
|
+
'bar'.try :nstr
|
99
|
+
# => 'bar'
|
100
|
+
```
|
101
|
+
|
102
|
+
### String Case Conversions
|
103
|
+
|
104
|
+
Case-conversion methods are monkey-patched into the *String* class. The following methods are made available:
|
105
|
+
* camelcase ('foo-bar' => 'FooBar')
|
106
|
+
* snakecase ('FooBar' => 'foo-bar')
|
107
|
+
* kebabcase ('FooBar' => 'foo_bar')
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
'foo_bar'.camelcase
|
111
|
+
# => 'FooBar'
|
112
|
+
'foo-bar'.camelcase
|
113
|
+
# => 'FooBar'
|
114
|
+
|
115
|
+
'FooBar'.snakecase
|
116
|
+
# => 'foo-bar'
|
117
|
+
'foo_bar'.snakecase
|
118
|
+
# => 'foo-bar'
|
119
|
+
|
120
|
+
'FooBar'.kebabcase
|
121
|
+
# => 'foo_bar'
|
122
|
+
'foo-bar'.kebabcase
|
123
|
+
# => 'foo_bar'
|
124
|
+
```
|
125
|
+
|
126
|
+
### Module utilities
|
127
|
+
|
128
|
+
#### Get Module Name
|
129
|
+
|
130
|
+
A _mod_name_ method is monkey-patched into the *Module* class. This allows obtaining the name of any module, without its hierarchical tree.
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
module Foo
|
134
|
+
module Bar
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
Foo.name
|
139
|
+
# => 'Foo'
|
140
|
+
|
141
|
+
Foo.mod_name
|
142
|
+
# => 'Foo'
|
143
|
+
|
144
|
+
Foo::Bar.name
|
145
|
+
# => 'Foo::Bar'
|
146
|
+
|
147
|
+
Foo::Bar.mod_name # <= This is where it gets interesting
|
148
|
+
# => 'Bar'
|
149
|
+
```
|
150
|
+
|
151
|
+
#### Get Module Parent Name
|
152
|
+
|
153
|
+
A _mod_parent_name_ method is monkey-patched into the *Module* class. This allows obtaining the name of any module's parent.
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
module Foo
|
157
|
+
module Bar
|
158
|
+
module Test
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
Foo.mod_parent_name
|
164
|
+
# => ''
|
165
|
+
|
166
|
+
Foo::Bar.mod_parent_name
|
167
|
+
# => 'Foo'
|
168
|
+
|
169
|
+
Foo::Bar::Test.mod_parent_name
|
170
|
+
# => 'Foo::Bar'
|
171
|
+
```
|
172
|
+
|
173
|
+
#### Get Module Parent
|
174
|
+
|
175
|
+
A _mod_parent_ method is monkey-patched into the *Module* class. This allows obtaining any module's parent.
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
module Foo
|
179
|
+
module Bar
|
180
|
+
module Test
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
Foo.mod_parent
|
186
|
+
# => nil
|
187
|
+
|
188
|
+
Foo::Bar.mod_parent
|
189
|
+
# => Foo
|
190
|
+
|
191
|
+
Foo::Bar::Test.mod_parent_name
|
192
|
+
# => Foo::Bar
|
193
|
+
```
|
194
|
+
|
195
|
+
### Time Duration
|
196
|
+
|
197
|
+
A _duration_ method is monkey-patched into the 'Numeric' class. This method returns a textual string representation of the time duration.
|
198
|
+
An optional *elements* argument allows setting the maximum number of time-elements (days, hours, minutes, ...) to display.
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
86400.duration
|
202
|
+
# => '1 Day'
|
203
|
+
((3600 * 3) + 60 + 12).duration
|
204
|
+
# => '3 Hours 1 Minute 12 Seconds'
|
205
|
+
((3600 * 3) + 60 + 12).duration 1
|
206
|
+
# => '3 Hours'
|
207
|
+
((3600 * 3) + 60 + 12).duration 2
|
208
|
+
# => '3 Hours 1 Minute'
|
209
|
+
```
|
210
|
+
|
211
|
+
## License
|
212
|
+
|
213
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,259 @@
|
|
1
|
+
# ProjectorPWS
|
2
|
+
# by Eresse <eresse@eresse.net>
|
3
|
+
|
4
|
+
# External Includes
|
5
|
+
require 'savon'
|
6
|
+
require 'aromat'
|
7
|
+
|
8
|
+
# Internal Includes
|
9
|
+
require 'projector_pws/version'
|
10
|
+
|
11
|
+
# ProjectorPWS Module:
|
12
|
+
# Root Module for ProjectorPWS.
|
13
|
+
module ProjectorPWS
|
14
|
+
|
15
|
+
# Service Definition
|
16
|
+
WSDL = 'https://secure.projectorpsa.com/OpsProjectorWcfSvc/PwsProjectorServices.svc?wsdl'
|
17
|
+
|
18
|
+
# Base URL
|
19
|
+
BASE_URL = 'https://secure.projectorpsa.com'
|
20
|
+
|
21
|
+
# URL Path
|
22
|
+
URL_PATH = 'OpsProjectorWcfSvc/PwsProjectorServices.svc'
|
23
|
+
|
24
|
+
# Namespaces
|
25
|
+
NAMESPACE = 'http://projectorpsa.com/PwsProjectorServices/'
|
26
|
+
EXTRA_NAMESPACES = {
|
27
|
+
'xmlns:req' => 'http://projectorpsa.com/DataContracts/Requests/',
|
28
|
+
'xmlns:tim' => 'http://projectorpsa.com/DataContracts/Shared/TimeAndCost/',
|
29
|
+
'xmlns:com' => 'http://projectorpsa.com/DataContracts/Shared/Common/'
|
30
|
+
}
|
31
|
+
|
32
|
+
# Work Week
|
33
|
+
WEEK_START = :monday
|
34
|
+
WEEK_END = :friday
|
35
|
+
|
36
|
+
# Days Per Week
|
37
|
+
DAYS_PER_WEEK = 5
|
38
|
+
|
39
|
+
# Hours Per Day
|
40
|
+
HOURS_PER_DAY = 8
|
41
|
+
|
42
|
+
# Real Hours Per Day
|
43
|
+
HOURS_PER_DAY_REAL = 24
|
44
|
+
|
45
|
+
# Minutes Per Hour
|
46
|
+
MINUTES_PER_HOUR = 60
|
47
|
+
|
48
|
+
# Minutes Per Day
|
49
|
+
MINUTES_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR
|
50
|
+
|
51
|
+
# Minutes Per Week
|
52
|
+
MINUTES_PER_WEEK = MINUTES_PER_DAY * DAYS_PER_WEEK
|
53
|
+
|
54
|
+
# Open Client
|
55
|
+
def self.open url = BASE_URL
|
56
|
+
c = Savon.client wsdl: WSDL, endpoint: service_url(url), namespace: NAMESPACE, env_namespace: :soapenv, namespace_identifier: :pws, namespaces: EXTRA_NAMESPACES
|
57
|
+
return yield c if block_given?
|
58
|
+
c
|
59
|
+
end
|
60
|
+
|
61
|
+
# Authenticate
|
62
|
+
def self.authenticate username, password, account_code = nil
|
63
|
+
|
64
|
+
# Prepare URLs
|
65
|
+
next_url = BASE_URL
|
66
|
+
last_url = ''
|
67
|
+
|
68
|
+
# Prepare Credentials
|
69
|
+
creds = {}
|
70
|
+
creds['req:AccountCode'] = account_code if account_code
|
71
|
+
creds['req:Password'] = password
|
72
|
+
creds['req:UserName'] = username
|
73
|
+
|
74
|
+
# Loop to obtain Ticket
|
75
|
+
c = nil
|
76
|
+
ticket = nil
|
77
|
+
until ticket
|
78
|
+
|
79
|
+
# Authenticate
|
80
|
+
begin
|
81
|
+
c = open next_url
|
82
|
+
r = c.call(:pws_authenticate, message: { 'pws:serviceRequest' => creds }).body[:pws_authenticate_response][:pws_authenticate_result]
|
83
|
+
last_url = next_url
|
84
|
+
next_url = r[:redirect_url]
|
85
|
+
rescue Savon::SOAPFault => e
|
86
|
+
raise "Authentication failed - #{e.to_hash[:fault][:detail][:pws_fault][:messages][:pws_message][:error_text]}"
|
87
|
+
end
|
88
|
+
|
89
|
+
raise 'API Error (redirect loop)' if next_url == last_url
|
90
|
+
ticket = r[:session_ticket]
|
91
|
+
end
|
92
|
+
|
93
|
+
# Yield if block given
|
94
|
+
if block_given?
|
95
|
+
r = yield c, ticket
|
96
|
+
unauthenticate c, ticket
|
97
|
+
return r
|
98
|
+
end
|
99
|
+
|
100
|
+
[c, ticket]
|
101
|
+
end
|
102
|
+
|
103
|
+
# Unauthenticate
|
104
|
+
def self.unauthenticate c, ticket
|
105
|
+
|
106
|
+
# Unauthenticate
|
107
|
+
c.call(:pws_unauthenticate, message: { 'pws:serviceRequest' => { 'req:SessionTicket' => ticket } }).body
|
108
|
+
end
|
109
|
+
|
110
|
+
# Get Resources
|
111
|
+
def self.get_resources c, ticket
|
112
|
+
|
113
|
+
# Fetch Resources
|
114
|
+
c.call(:pws_get_resource_list, message: { 'pws:serviceRequest' => { 'req:SessionTicket' => ticket } }).body[:pws_get_resource_list_response][:pws_get_resource_list_result][:resources][:pws_resource_summary]
|
115
|
+
end
|
116
|
+
|
117
|
+
# Get Resource Schedule
|
118
|
+
def self.get_resource_schedule c, ticket, resource_uid, start_date = current_week_start, end_date = current_week_end
|
119
|
+
|
120
|
+
# Prepare Params
|
121
|
+
params = { 'req:SessionTicket' => ticket }
|
122
|
+
params['tim:EndDate'] = end_date.strftime '%Y-%m-%dz'
|
123
|
+
params['tim:IncludeScheduledTimeFlag'] = :true
|
124
|
+
params['tim:IncludeTimeOffFlag'] = :true
|
125
|
+
params['tim:ResourceIdentity'] = { 'com:ResourceUid' => resource_uid }
|
126
|
+
params['tim:StartDate'] = start_date.strftime '%Y-%m-%dz'
|
127
|
+
|
128
|
+
# Fetch Resource Schedule
|
129
|
+
c.call(:pws_get_resource_schedule, message: { 'pws:serviceRequest' => params }).body[:pws_get_resource_schedule_response][:pws_get_resource_schedule_result][:resource_schedule]
|
130
|
+
end
|
131
|
+
|
132
|
+
# Get Resource Working Schedule
|
133
|
+
def self.get_resource_working_schedule c, ticket, resource_uid, start_date = current_week_start, end_date = current_week_end
|
134
|
+
|
135
|
+
# Prepare Params
|
136
|
+
params = { 'req:SessionTicket' => ticket }
|
137
|
+
params['tim:EndDate'] = end_date.strftime '%Y-%m-%dz'
|
138
|
+
params['tim:ResourceIdentity'] = { 'com:ResourceUid' => resource_uid }
|
139
|
+
params['tim:StartDate'] = start_date.strftime '%Y-%m-%dz'
|
140
|
+
|
141
|
+
# Fetch Resource Working Schedule
|
142
|
+
c.call(:pws_get_resource_working_schedule, message: { 'pws:serviceRequest' => params }).body[:pws_get_resource_working_schedule_response][:pws_get_resource_working_schedule_result][:working_schedule]
|
143
|
+
end
|
144
|
+
|
145
|
+
# Get Free Resources (non-busy) with associated minutes
|
146
|
+
def self.get_free_resources c, ticket, start_date = current_week_start, end_date = current_week_end
|
147
|
+
|
148
|
+
# Get all resources
|
149
|
+
res = ProjectorPWS.get_resources c, ticket
|
150
|
+
|
151
|
+
# Collect free time
|
152
|
+
res.collect { |r| {
|
153
|
+
resource: r,
|
154
|
+
free_hours: get_resource_free_hours(c, ticket, r, start_date, end_date).to_f
|
155
|
+
} }.select { |x| x[:free_hours] > 0 }
|
156
|
+
end
|
157
|
+
|
158
|
+
# Get Resource Free Hours
|
159
|
+
def self.get_resource_free_hours c, ticket, resource, start_date = current_week_start, end_date = current_week_end
|
160
|
+
|
161
|
+
# Get scheduled hours
|
162
|
+
scheduled_hours = get_resource_scheduled_hours c, ticket, resource, start_date, end_date
|
163
|
+
|
164
|
+
# Get active hours
|
165
|
+
active_hours = get_resource_active_hours c, ticket, resource, start_date, end_date
|
166
|
+
|
167
|
+
# Compute time off into active hours
|
168
|
+
active_hours = active_hours - scheduled_hours[:toff]
|
169
|
+
|
170
|
+
# Compute free time (active unscheduled time)
|
171
|
+
active_hours - scheduled_hours[:work]
|
172
|
+
end
|
173
|
+
|
174
|
+
# Get Resource Active Hours
|
175
|
+
def self.get_resource_active_hours c, ticket, resource, start_date = current_week_start, end_date = current_week_end
|
176
|
+
|
177
|
+
# Acquire working schedule for resource
|
178
|
+
wsched = ProjectorPWS.get_resource_working_schedule(c, ticket, resource[:resource_uid], start_date, end_date)[:pws_working_schedule_day] rescue(return(0))
|
179
|
+
wsched = [wsched] unless wsched.is_a? Array
|
180
|
+
wsched.compact!
|
181
|
+
|
182
|
+
# Extract Working Minutes
|
183
|
+
work = wsched.inject(0) { |a, e| a + e[:working_minutes].to_i }
|
184
|
+
|
185
|
+
# Determine Active Hours
|
186
|
+
work.to_f / MINUTES_PER_HOUR.to_f
|
187
|
+
end
|
188
|
+
|
189
|
+
# Get Resource Scheduled Hours
|
190
|
+
def self.get_resource_scheduled_hours c, ticket, resource, start_date = current_week_start, end_date = current_week_end
|
191
|
+
|
192
|
+
# Get schedule
|
193
|
+
sched = get_resource_schedule(c, ticket, resource[:resource_uid], start_date, end_date) rescue(return({ work: 0, toff: 0 }))
|
194
|
+
|
195
|
+
# Extract scheduled work
|
196
|
+
sched_work = sched[:roles][:pws_schedule_role] rescue []
|
197
|
+
sched_work = [sched_work] unless sched_work.is_a? Array
|
198
|
+
sched_work.compact!
|
199
|
+
|
200
|
+
# Extract scheduled time off
|
201
|
+
time_off = sched[:time_off][:pws_schedule_time_off] rescue []
|
202
|
+
time_off = [time_off] unless time_off.is_a? Array
|
203
|
+
time_off.compact!
|
204
|
+
|
205
|
+
# Run through time off { collect total minutes }
|
206
|
+
time_off_minutes = time_off.inject(0) do |a, e|
|
207
|
+
|
208
|
+
# Extract Dates
|
209
|
+
dates = e[:time_off_dates][:pws_schedule_time_off_date] rescue []
|
210
|
+
dates = [dates] unless dates.is_a? Array
|
211
|
+
dates.compact!
|
212
|
+
|
213
|
+
# Collect time off dates
|
214
|
+
a + dates.inject(0) { |da, de| da + time_off_minutes(de[:time_off_minutes].to_i) }
|
215
|
+
end
|
216
|
+
|
217
|
+
# Run through schedule { collect total minutes }
|
218
|
+
scheduled_minutes = sched_work.inject(0) do |a, e|
|
219
|
+
|
220
|
+
# Extract bookings
|
221
|
+
bookings = e[:bookings][:pws_schedule_booking] rescue []
|
222
|
+
bookings = [bookings] unless bookings.is_a? Array
|
223
|
+
bookings.compact!
|
224
|
+
|
225
|
+
# Collect 'busy' bookings
|
226
|
+
e[:project_descriptor][:engagement_descriptor][:engagement_type_descriptor][:busy_flag] ? a + (bookings.inject(0) { |ba, be| ba + be[:scheduled_minutes].to_i }) : a
|
227
|
+
end
|
228
|
+
|
229
|
+
{
|
230
|
+
work: scheduled_minutes.to_f / MINUTES_PER_HOUR.to_f,
|
231
|
+
toff: time_off_minutes.to_f / MINUTES_PER_HOUR.to_f
|
232
|
+
}
|
233
|
+
end
|
234
|
+
|
235
|
+
# Generate Service URL
|
236
|
+
def self.service_url base_url
|
237
|
+
"#{base_url}/#{URL_PATH}"
|
238
|
+
end
|
239
|
+
|
240
|
+
# Current Week Start
|
241
|
+
def self.current_week_start
|
242
|
+
is_weekend? ? Date.today.wnext(WEEK_START) : Date.today.wlast(WEEK_START)
|
243
|
+
end
|
244
|
+
|
245
|
+
# Current Week End
|
246
|
+
def self.current_week_end
|
247
|
+
current_week_start.wnext(WEEK_END)
|
248
|
+
end
|
249
|
+
|
250
|
+
# Is Weekend
|
251
|
+
def self.is_weekend?
|
252
|
+
Date.today.friday? || Date.today.saturday? || Date.today.sunday?
|
253
|
+
end
|
254
|
+
|
255
|
+
# Time Off Minutes (adjust for full-day holidays)
|
256
|
+
def self.time_off_minutes x
|
257
|
+
(x == HOURS_PER_DAY_REAL * MINUTES_PER_HOUR) ? (HOURS_PER_DAY * MINUTES_PER_HOUR) : x
|
258
|
+
end
|
259
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'projector_pws/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'projector_pws'
|
8
|
+
spec.version = ProjectorPWS::VERSION
|
9
|
+
spec.authors = ['Eresse']
|
10
|
+
spec.email = ['eresse@eresse.net']
|
11
|
+
|
12
|
+
spec.summary = 'Projector PWS Client Library'
|
13
|
+
spec.description = 'Provides access to Projector PSA\'s Web Services (PWS)'
|
14
|
+
spec.homepage = 'http://redmine.eresse.net/projects/projector_pws'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
|
20
|
+
spec.add_development_dependency 'bundler'
|
21
|
+
spec.add_development_dependency 'rake'
|
22
|
+
spec.add_runtime_dependency 'minitest'
|
23
|
+
spec.add_runtime_dependency 'aromat'
|
24
|
+
spec.add_runtime_dependency 'savon'
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: projector_pws
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eresse
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-11-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: aromat
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: savon
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Provides access to Projector PSA's Web Services (PWS)
|
84
|
+
email:
|
85
|
+
- eresse@eresse.net
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- Gemfile
|
92
|
+
- LICENSE.txt
|
93
|
+
- README.md
|
94
|
+
- Rakefile
|
95
|
+
- lib/projector_pws.rb
|
96
|
+
- lib/projector_pws/version.rb
|
97
|
+
- projector_pws.gemspec
|
98
|
+
homepage: http://redmine.eresse.net/projects/projector_pws
|
99
|
+
licenses:
|
100
|
+
- MIT
|
101
|
+
metadata: {}
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
requirements: []
|
117
|
+
rubyforge_project:
|
118
|
+
rubygems_version: 2.5.1
|
119
|
+
signing_key:
|
120
|
+
specification_version: 4
|
121
|
+
summary: Projector PWS Client Library
|
122
|
+
test_files: []
|