Module: ProjectorPWS

Defined in:
lib/projector_pws.rb,
lib/projector_pws/version.rb

Overview

ProjectorPWS Module

Constant Summary collapse

WSDL =

Service Definition

'https://secure.projectorpsa.com/OpsProjectorWcfSvc/PwsProjectorServices.svc?wsdl'
BASE_URL =

Base URL

'https://secure.projectorpsa.com'
URL_PATH =

URL Path

'OpsProjectorWcfSvc/PwsProjectorServices.svc'
NAMESPACE =

Namespaces

'http://projectorpsa.com/PwsProjectorServices/'
EXTRA_NAMESPACES =
{
	'xmlns:req' => 'http://projectorpsa.com/DataContracts/Requests/',
	'xmlns:tim' => 'http://projectorpsa.com/DataContracts/Shared/TimeAndCost/',
	'xmlns:com' => 'http://projectorpsa.com/DataContracts/Shared/Common/'
}
WEEK_START =

Work Week

:monday
WEEK_END =
:friday
DAYS_PER_WEEK =

Days Per Week

5
HOURS_PER_DAY =

Hours Per Day

8
HOURS_PER_DAY_REAL =

Real Hours Per Day

24
MINUTES_PER_HOUR =

Minutes Per Hour

60
MINUTES_PER_DAY =

Minutes Per Day

HOURS_PER_DAY * MINUTES_PER_HOUR
MINUTES_PER_WEEK =

Minutes Per Week

MINUTES_PER_DAY * DAYS_PER_WEEK
VERSION =

Version

'0.1.0'

Class Method Summary collapse

Class Method Details

.authenticate(username, password, account_code = nil) ⇒ Object

Authenticate



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/projector_pws.rb', line 62

def self.authenticate username, password,  = nil

	# Prepare URLs
	next_url = BASE_URL
	last_url = ''

	# Prepare Credentials
	creds = {}
	creds['req:AccountCode'] =  if 
	creds['req:Password'] = password
	creds['req:UserName'] = username

	# Loop to obtain Ticket
	c = nil
	ticket = nil
	until ticket

		# Authenticate
		begin
			c = open next_url
			r = c.call(:pws_authenticate, message: { 'pws:serviceRequest' => creds }).body[:pws_authenticate_response][:pws_authenticate_result]
			last_url = next_url
			next_url = r[:redirect_url]
		rescue Savon::SOAPFault => e
			raise "Authentication failed - #{e.to_hash[:fault][:detail][:pws_fault][:messages][:pws_message][:error_text]}"
		end

		raise 'API Error (redirect loop)' if next_url == last_url
		ticket = r[:session_ticket]
	end

	# Yield if block given
	if block_given?
		r = yield c, ticket
		unauthenticate c, ticket
		return r
	end

	[c, ticket]
end

.current_week_endObject

Current Week End



246
247
248
# File 'lib/projector_pws.rb', line 246

def self.current_week_end
	current_week_start.wnext(WEEK_END)
end

.current_week_startObject

Current Week Start



241
242
243
# File 'lib/projector_pws.rb', line 241

def self.current_week_start
	is_weekend? ? Date.today.wnext(WEEK_START) : Date.today.wlast(WEEK_START)
end

.get_free_resources(c, ticket, start_date = current_week_start, end_date = current_week_end) ⇒ Object

Get Free Resources (non-busy) with associated minutes



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/projector_pws.rb', line 146

def self.get_free_resources c, ticket, start_date = current_week_start, end_date = current_week_end

	# Get all resources
	res = ProjectorPWS.get_resources c, ticket

	# Collect free time
	res.collect { |r| {
		resource: r,
		free_hours: get_resource_free_hours(c, ticket, r, start_date, end_date).to_f
	} }.select { |x| x[:free_hours] > 0 }
end

.get_resource_active_hours(c, ticket, resource, start_date = current_week_start, end_date = current_week_end) ⇒ Object

Get Resource Active Hours



175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/projector_pws.rb', line 175

def self.get_resource_active_hours c, ticket, resource, start_date = current_week_start, end_date = current_week_end

	# Acquire working schedule for resource
	wsched = ProjectorPWS.get_resource_working_schedule(c, ticket, resource[:resource_uid], start_date, end_date)[:pws_working_schedule_day] rescue(return(0))
	wsched = [wsched] unless wsched.is_a? Array
	wsched.compact!

	# Extract Working Minutes
	work = wsched.inject(0) { |a, e| a + e[:working_minutes].to_i }

	# Determine Active Hours
	work.to_f / MINUTES_PER_HOUR.to_f
end

.get_resource_free_hours(c, ticket, resource, start_date = current_week_start, end_date = current_week_end) ⇒ Object

Get Resource Free Hours



159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/projector_pws.rb', line 159

def self.get_resource_free_hours c, ticket, resource, start_date = current_week_start, end_date = current_week_end

	# Get scheduled hours
	scheduled_hours = get_resource_scheduled_hours c, ticket, resource, start_date, end_date

	# Get active hours
	active_hours = get_resource_active_hours c, ticket, resource, start_date, end_date

	# Compute time off into active hours
	active_hours = active_hours - scheduled_hours[:toff]

	# Compute free time (active unscheduled time)
	active_hours - scheduled_hours[:work]
end

.get_resource_schedule(c, ticket, resource_uid, start_date = current_week_start, end_date = current_week_end) ⇒ Object

Get Resource Schedule



118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/projector_pws.rb', line 118

def self.get_resource_schedule c, ticket, resource_uid, start_date = current_week_start, end_date = current_week_end

	# Prepare Params
	params = { 'req:SessionTicket' => ticket }
	params['tim:EndDate'] = end_date.strftime '%Y-%m-%dz'
	params['tim:IncludeScheduledTimeFlag'] = :true
	params['tim:IncludeTimeOffFlag'] = :true
	params['tim:ResourceIdentity'] = { 'com:ResourceUid' => resource_uid }
	params['tim:StartDate'] = start_date.strftime '%Y-%m-%dz'

	# Fetch Resource Schedule
	c.call(:pws_get_resource_schedule, message: { 'pws:serviceRequest' => params }).body[:pws_get_resource_schedule_response][:pws_get_resource_schedule_result][:resource_schedule]
end

.get_resource_scheduled_hours(c, ticket, resource, start_date = current_week_start, end_date = current_week_end) ⇒ Object

Get Resource Scheduled Hours



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/projector_pws.rb', line 190

def self.get_resource_scheduled_hours c, ticket, resource, start_date = current_week_start, end_date = current_week_end

	# Get schedule
	sched = get_resource_schedule(c, ticket, resource[:resource_uid], start_date, end_date) rescue(return({ work: 0, toff: 0 }))

	# Extract scheduled work
	sched_work = sched[:roles][:pws_schedule_role] rescue []
	sched_work = [sched_work] unless sched_work.is_a? Array
	sched_work.compact!

	# Extract scheduled time off
	time_off = sched[:time_off][:pws_schedule_time_off] rescue []
	time_off = [time_off] unless time_off.is_a? Array
	time_off.compact!

	# Run through time off { collect total minutes }
	time_off_minutes = time_off.inject(0) do |a, e|

		# Extract Dates
		dates = e[:time_off_dates][:pws_schedule_time_off_date] rescue []
		dates = [dates] unless dates.is_a? Array
		dates.compact!

		# Collect time off dates
		a + dates.inject(0) { |da, de| da + time_off_minutes(de[:time_off_minutes].to_i) }
	end

	# Run through schedule { collect total minutes }
	scheduled_minutes = sched_work.inject(0) do |a, e|

		# Extract bookings
		bookings = e[:bookings][:pws_schedule_booking] rescue []
		bookings = [bookings] unless bookings.is_a? Array
		bookings.compact!

		# Collect 'busy' bookings
		e[:project_descriptor][:engagement_descriptor][:engagement_type_descriptor][:busy_flag] ? a + (bookings.inject(0) { |ba, be| ba + be[:scheduled_minutes].to_i }) : a
	end

	{
		work: scheduled_minutes.to_f / MINUTES_PER_HOUR.to_f,
	    toff: time_off_minutes.to_f / MINUTES_PER_HOUR.to_f
	}
end

.get_resource_working_schedule(c, ticket, resource_uid, start_date = current_week_start, end_date = current_week_end) ⇒ Object

Get Resource Working Schedule



133
134
135
136
137
138
139
140
141
142
143
# File 'lib/projector_pws.rb', line 133

def self.get_resource_working_schedule c, ticket, resource_uid, start_date = current_week_start, end_date = current_week_end

	# Prepare Params
	params = { 'req:SessionTicket' => ticket }
	params['tim:EndDate'] = end_date.strftime '%Y-%m-%dz'
	params['tim:ResourceIdentity'] = { 'com:ResourceUid' => resource_uid }
	params['tim:StartDate'] = start_date.strftime '%Y-%m-%dz'

	# Fetch Resource Working Schedule
	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]
end

.get_resources(c, ticket) ⇒ Object

Get Resources



111
112
113
114
115
# File 'lib/projector_pws.rb', line 111

def self.get_resources c, ticket

	# Fetch Resources
	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]
end

.is_weekend?Boolean

Is Weekend



251
252
253
# File 'lib/projector_pws.rb', line 251

def self.is_weekend?
	Date.today.friday? || Date.today.saturday? || Date.today.sunday?
end

.open(url = BASE_URL) ⇒ Object

Open Client



55
56
57
58
59
# File 'lib/projector_pws.rb', line 55

def self.open url = BASE_URL
	c = Savon.client wsdl: WSDL, endpoint: service_url(url), namespace: NAMESPACE, env_namespace: :soapenv, namespace_identifier: :pws, namespaces: EXTRA_NAMESPACES
	return yield c if block_given?
	c
end

.service_url(base_url) ⇒ Object

Generate Service URL



236
237
238
# File 'lib/projector_pws.rb', line 236

def self.service_url base_url
	"#{base_url}/#{URL_PATH}"
end

.time_off_minutes(x) ⇒ Object

Time Off Minutes (adjust for full-day holidays)



256
257
258
# File 'lib/projector_pws.rb', line 256

def self.time_off_minutes x
	(x == HOURS_PER_DAY_REAL * MINUTES_PER_HOUR) ? (HOURS_PER_DAY * MINUTES_PER_HOUR) : x
end

.unauthenticate(c, ticket) ⇒ Object

Unauthenticate



104
105
106
107
108
# File 'lib/projector_pws.rb', line 104

def self.unauthenticate c, ticket

	# Unauthenticate
	c.call(:pws_unauthenticate, message: { 'pws:serviceRequest' => { 'req:SessionTicket' => ticket } }).body
end