Skip to content

Commit

Permalink
HOPE-141 HOPE-147 OCC-1174 HOPE-144 HOPE-142 OCC-1177 OCC-1176 HOPE-129
Browse files Browse the repository at this point in the history
  • Loading branch information
drewteter committed Apr 10, 2023
1 parent e86a19a commit 116cc27
Show file tree
Hide file tree
Showing 18 changed files with 160 additions and 74 deletions.
3 changes: 3 additions & 0 deletions app/controllers/admin/configs_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ class Admin::ConfigsController < Admin::AdminController
helper_method :build_dashboard_mode_collection

PERMITTED_CONFIGS = [
:application_title,
:open_trip_planner,
:open_trip_planner_version,
:otp_itinerary_quantity,
:otp_car_park_quantity,
:otp_transit_quantity,
:otp_paratransit_quantity,
# :otp_max_itineraries_shown,
:tff_api_key,
Expand Down
49 changes: 36 additions & 13 deletions app/mailers/user_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,16 @@ def user_trip_email(addresses, trip, itinerary=nil)
@trip = trip
@traveler = trip.user
@locale = @traveler.locale.try(:name)
subject = "FindMyRidePA Trip Details sent to you by traveler's request"
subject = [application_title, "Trip Details sent to you by traveler's request"].compact.join(" ")
@itinerary = itinerary || @trip.selected_itinerary
unless @itinerary
return
end
if @itinerary.service and @itinerary.service.logo.url
attachments.inline['service_logo.png'] = open(ActionController::Base.helpers.asset_path(@itinerary.service.logo.thumb.url.to_s), 'rb').read
end
map_image = MapService.new(@itinerary).create_static_map
attachments.inline[@itinerary.id.to_s + ".png"] = open(map_image, 'rb').read

attach_service_logo
attach_map_image
attach_standard_icons #TODO: Don't attach all icons by default. Attach them as needed.

mail(to: addresses, subject: subject)
end

Expand Down Expand Up @@ -112,7 +111,7 @@ def api_v2_reset_password_instructions(user, new_password)

def ecolane_trip_email(addresses, bookings)
@decorated_bookings = bookings # form [[booking, trip_hash],...]
subject = "FindMyRidePA Trip Details sent to you by traveler's request"
subject = [application_title, "Trip Details sent to you by traveler's request"].compact.join(" ")
mail(to: addresses, subject: subject)
end

Expand All @@ -121,17 +120,16 @@ def user_trip_reminder(addresses,trip,days_away)
@trip = trip
@traveler = @trip.user
@locale = @traveler.locale.try(:name)
subject = "FindMyRidePA Trip Reminder!"
subject = [application_title, "Trip Reminder!"].compact.join(" ")
@itinerary = @trip.selected_itinerary
unless @itinerary
return
end
if @itinerary.service and @itinerary.service.logo.url
attachments.inline['service_logo.png'] = open(ActionController::Base.helpers.asset_path(@itinerary.service.logo.thumb.url.to_s), 'rb').read
end
map_image = MapService.new(@itinerary).create_static_map
attachments.inline[@itinerary.id.to_s + ".png"] = open(map_image, 'rb').read

attach_service_logo
attach_map_image
attach_standard_icons #TODO: Don't attach all icons by default. Attach them as needed.

mail(to: addresses, subject: subject)
end

Expand All @@ -145,6 +143,31 @@ def attach_standard_icons
end
end

def attach_service_logo
if @itinerary.service and @itinerary.service.logo.url.present?
begin
attachments.inline['service_logo.png'] = open(ActionController::Base.helpers.asset_path(@itinerary.service.logo.thumb.url.to_s), 'rb').read
rescue Errno::ENOENT
Rails.logger.error "Failure to attach logo to email: '#{@itinerary.service.logo.thumb.url.to_s}' is not a valid path for a logo."
rescue StandardError => e
Rails.logger.error e
end
end
end

def attach_map_image
if ENV['GOOGLE_API_KEY'].present?
begin
map_image = MapService.new(@itinerary).create_static_map
attachments.inline[@itinerary.id.to_s + ".png"] = open(map_image, 'rb').read
rescue StandardError => e
Rails.logger.error e
end
end
end

def application_title
Config.application_title.present? ? Config.application_title : nil
end

end
11 changes: 8 additions & 3 deletions app/models/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ def initialize(user)
can :manage, GeographyRecord # Can create Geography records
can :manage, Role, # Can manage roles for current agency
resource_id: user.staff_agency.id
# Admins cannot manage superusers
cannot :manage, User, # Cannot manage superusers
id: User.all.superuser.pluck(:id)
cannot :manage, Role, # Cannot manage superuser Roles
id: Role.find_by(name: :superuser)

# Transportation Admin Permissions
if user.transportation_admin?
Expand Down Expand Up @@ -160,16 +165,16 @@ def initialize(user)
can :manage, BookingWindow,
agency_id: user.staff_agency.agency_oversight_agency.pluck(:transportation_agency_id).concat([user.staff_agency.id])
can :manage, Service,
id: user.get_services_for_oversight.pluck(:id).concat(Service.no_agencies_assigned.pluck(:id)) # Can access services associated with an oversight agency, and those with no oversight agency
id: user.get_services_for_oversight.pluck(:id).concat(Service.no_agencies_assigned.pluck(:id)) # Can access services associated with an oversight agency, and those with no oversight agency
can :manage, Role # Can manage Roles
# Mapping related permissions
can :manage, GeographyRecord # Can manage geography records

# Oversight Admins cannot manage superusers
cannot :manage, User, # Cannot manage superusers
id: User.all.superuser.pluck(:id)
id: User.all.superuser.pluck(:id)
cannot :manage, Role, # Cannot manage superuser Roles
id: Role.find_by(name: :superuser)
id: Role.find_by(name: :superuser)
end
end # end admin

Expand Down
4 changes: 2 additions & 2 deletions app/models/agency.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Agency < ApplicationRecord

### SCOPES, CONSTANTS, & VALIDATIONS ###

validates :name, presence: true
validates :name, presence: true, uniqueness: true
validates :agency_type_id, presence: true
contact_fields email: :email, phone: :phone, url: :url

Expand All @@ -29,7 +29,7 @@ class Agency < ApplicationRecord
has_many :funding_sources, dependent: :destroy
has_many :travel_patterns
# this is to help access the Agency index page, although it's a bit redundant
has_one :agency_oversight_agency,foreign_key:"transportation_agency_id", dependent: :destroy
has_one :agency_oversight_agency, foreign_key: "transportation_agency_id", dependent: :destroy
belongs_to :agency_type

AGENCY_TYPE_MAP = {
Expand Down
16 changes: 8 additions & 8 deletions app/models/concerns/role_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -353,25 +353,25 @@ def get_travelers_for_staff_user

def get_trips_for_staff_user
# Conditional statement flow:
# If current user is a traveler => return nil
# If current user is a superuser => return all Trips
# If current user is a transportation agency staff => return Trips associated with the agency
# If current user is viewing as oversight staff => return Trips associated with all agencies under the oversight agency
# If current user is viewing as transportation agency staff => return Trips associated with the current transportation agency
# If the current user is viewing all unaffiliated trips and is oversight staff => return Trips associated with no tranpsortation agency

if self.superuser?
# If current user is a superuser => return all Trips
Trip.all
elsif self.transportation_admin? || self.transportation_staff?
# If current user is a transportation agency staff or admin => return Trips associated with the agency
Trip.with_transportation_agency(self.staff_agency.id)
elsif self.currently_oversight?
# If current user is viewing as oversight staff or admin => return Trips associated with all agencies under the oversight agency
tas = AgencyOversightAgency.where(oversight_agency_id: self.staff_agency.id).pluck(:transportation_agency_id)
Trip.with_transportation_agency(tas)
elsif self.currently_transportation?
Trip.with_transportation_agency(self.current_agency.id)
# If current user is viewing as transportation agency staff or admin => return Trips associated with the current transportation agency
Trip.with_transportation_agency(self.current_agency.id)
elsif self.staff_agency&.oversight? && self.current_agency.nil?
# If the current user is viewing all unaffiliated trips and is oversight staff => return Trips associated with no tranpsortation agency
Trip.with_no_transportation_agency
# Fallback just in case an edge case is missed
else
# If current user is a traveler => return nil
nil
end
end
Expand Down
7 changes: 5 additions & 2 deletions app/models/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ class Config < ApplicationRecord
DEFAULT_NOTIFICATION_PREFS = [7,3,1].freeze

DEFAULT_CONFIGS = {
application_title: "", # (String) The title of the application. Mostly used in emails.
bike_reluctance: 5, # (Integer) ???
booking_api: 'all', # (String) Declares which booking apis services may use. Other values include "none" and the name of an api.
booking_api: "all", # (String) Declares which booking apis services may use. Other values include "none" and the name of an api.
daily_scheduled_tasks: [],
dashboard_mode: "default", # (String) Set to "travel_patterns" to enable the travel pattern workflow.
dashboard_reports: [], # (Array<???>) ???
Expand All @@ -34,8 +35,10 @@ class Config < ApplicationRecord
maximum_booking_notice: 30, # (Integer) The maximum number of days into the future a user is allowed to book.
max_walk_minutes: 45, # (Integer) The maximum number of minutes a traveler is expected to walk when planing a trip.
open_trip_planner: "", # (String) OTP's base url.
open_trip_planner_version: 'v1', # (String) Which version of OTP to use. The other option is "v2".
open_trip_planner_version: "v1", # (String) Which version of OTP to use. The other option is "v2".
otp_itinerary_quantity: 3,
otp_car_park_quantity: 3,
otp_transit_quantity: 3,
otp_paratransit_quantity: 3,
# otp_max_itineraries_shown: 3,
require_user_confirmation: false, # (Boolean) Requires user to confirm their email address within a certain timeframe.
Expand Down
1 change: 1 addition & 0 deletions app/models/trip.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class Trip < ApplicationRecord
TRIP_TYPES = [
:transit,
:paratransit,
# :car_park, # This trip type is not selectable. Instead, it is automatically included when both :car and :transit are selected
:taxi,
:walk,
:car,
Expand Down
29 changes: 15 additions & 14 deletions app/services/external_api_ambassadors/otp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -193,20 +193,21 @@ def get_stoptimes trip_id, agency_id=1
return JSON.parse(resp.body)
end

def get_otp_mode trip_type
hash = {'transit': 'TRANSIT,WALK',
'bicycle_transit': 'TRANSIT,BICYCLE',
'park_transit':'CAR_PARK,WALK,TRANSIT',
'car_transit':'CAR,WALK,TRANSIT',
'bike_park_transit':'BICYCLE_PARK,WALK,TRANSIT',
'paratransit':'TRANSIT,WALK,FLEX_ACCESS,FLEX_EGRESS,FLEX_DIRECT',
'rail':'TRAM,SUBWAY,RAIL,WALK',
'bus':'BUS,WALK',
'walk':'WALK',
'car':'CAR',
'bicycle':'BICYCLE'}
hash[trip_type.to_sym]
end
# Dead code? Drew Teter - 4/7/2023
# def get_otp_mode trip_type
# hash = {'transit': 'TRANSIT,WALK',
# 'bicycle_transit': 'TRANSIT,BICYCLE',
# 'park_transit': 'CAR_PARK,WALK,TRANSIT',
# 'car_transit': 'CAR,WALK,TRANSIT',
# 'bike_park_transit': 'BICYCLE_PARK,WALK,TRANSIT',
# 'paratransit': 'TRANSIT,WALK,FLEX_ACCESS,FLEX_EGRESS,FLEX_DIRECT',
# 'rail': 'TRAM,SUBWAY,RAIL,WALK',
# 'bus': 'BUS,WALK',
# 'walk': 'WALK',
# 'car': 'CAR',
# 'bicycle': 'BICYCLE'}
# hash[trip_type.to_sym]
# end

# Wraps a response body in an OTPResponse object for easy inspection and manipulation
def unpack(response)
Expand Down
58 changes: 40 additions & 18 deletions app/services/external_api_ambassadors/otp_ambassador.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,27 @@ class OTPAmbassador

# Translates 1-click trip_types into OTP mode requests
TRIP_TYPE_DICTIONARY = {
transit: { label: :otp_transit, modes: "TRANSIT,WALK" },
paratransit: { label: :otp_paratransit, modes: "CAR" },
taxi: { label: :otp_car, modes: "CAR" },
walk: { label: :otp_walk, modes: "WALK"},
car: { label: :otp_car, modes: "CAR"},
bicycle: { label: :otp_bicycle, modes: "BICYCLE"},
uber: { label: :otp_car, modes: "CAR"},
lyft: { label: :otp_car, modes: "CAR"}
transit: { label: :otp_transit, modes: "TRANSIT,WALK" },
paratransit: { label: :otp_paratransit, modes: "CAR" },
car_park: { label: :otp_car_park, modes: "" },
taxi: { label: :otp_car, modes: "CAR" },
walk: { label: :otp_walk, modes: "WALK" },
car: { label: :otp_car, modes: "CAR" },
bicycle: { label: :otp_bicycle, modes: "BICYCLE" },
uber: { label: :otp_car, modes: "CAR" },
lyft: { label: :otp_car, modes: "CAR" }
}

TRIP_TYPE_DICTIONARY_V2 = {
transit: { label: :otp_transit, modes: "CAR_PARK,TRANSIT,WALK" },
paratransit: { label: :otp_paratransit, modes: "CAR_PARK,TRANSIT,WALK,FLEX_ACCESS,FLEX_EGRESS,FLEX_DIRECT" },
taxi: { label: :otp_car, modes: "CAR" },
walk: { label: :otp_walk, modes: "WALK"},
car: { label: :otp_car, modes: "CAR"},
bicycle: { label: :otp_bicycle, modes: "BICYCLE"},
uber: { label: :otp_car, modes: "CAR"},
lyft: { label: :otp_car, modes: "CAR"}
transit: { label: :otp_transit, modes: "TRANSIT,WALK" },
paratransit: { label: :otp_paratransit, modes: "TRANSIT,WALK,FLEX_ACCESS,FLEX_EGRESS,FLEX_DIRECT" },
car_park: { label: :otp_car_park, modes: "CAR_PARK,TRANSIT,WALK" },
taxi: { label: :otp_car, modes: "CAR" },
walk: { label: :otp_walk, modes: "WALK" },
car: { label: :otp_car, modes: "CAR" },
bicycle: { label: :otp_bicycle, modes: "BICYCLE" },
uber: { label: :otp_car, modes: "CAR" },
lyft: { label: :otp_car, modes: "CAR" }
}

# Initialize with a trip, an array of trip trips, an HTTP Request Bundler object,
Expand Down Expand Up @@ -82,13 +84,28 @@ def get_duration(trip_type)
return 0 if errors(trip_type)
itineraries = ensure_response(trip_type).itineraries
return itineraries[0]["duration"] if itineraries[0]
0
end

# Extracts a trip distance from the OTP response.
def get_distance(trip_type)
return 0 if errors(trip_type)
itineraries = ensure_response(trip_type).itineraries
return extract_distance(itineraries[0]) if itineraries[0]
0
end

def max_itineraries(trip_type_label)
quantity_config = {
otp_car: Config.otp_itinerary_quantity,
otp_walk: Config.otp_itinerary_quantity,
otp_bicycle: Config.otp_itinerary_quantity,
otp_car_park: Config.otp_car_park_quantity,
otp_transit: Config.otp_transit_quantity,
otp_paratransit: Config.otp_paratransit_quantity
}

quantity_config[trip_type_label]
end

# Dead Code? - Drew 02/16/2023
Expand All @@ -111,7 +128,7 @@ def prepare_http_requests

# Formats the trip as an OTP request based on trip_type
def format_trip_as_otp_request(trip_type)
num_itineraries = trip_type[:label] == :otp_paratransit ? Config.otp_paratransit_quantity : Config.otp_itinerary_quantity
num_itineraries = max_itineraries(trip_type[:label])
{
from: [@trip.origin.lat, @trip.origin.lng],
to: [@trip.destination.lat, @trip.destination.lng],
Expand Down Expand Up @@ -142,8 +159,13 @@ def ensure_response(trip_type)
# Converts an OTP itinerary hash into a set of 1-Click itinerary attributes
def convert_itinerary(otp_itin, trip_type)
associate_legs_with_services(otp_itin)
itin_has_invalid_leg = otp_itin.legs.detect{ |leg|
leg['serviceName'] && leg['serviceId'].nil?
}
return nil if itin_has_invalid_leg

service_id = otp_itin.legs
.detect{|leg| leg['serviceId'].present? }
.detect{ |leg| leg['serviceId'].present? }
&.fetch('serviceId', nil)

return {
Expand Down
8 changes: 7 additions & 1 deletion app/services/trip_planner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ def initialize(trip, options={})
@trip = trip
@options = options
@trip_types = (options[:trip_types] || TRIP_TYPES) & TRIP_TYPES # Set to only valid trip_types, all by default
@trip_types.push(:car_park) if (@trip_types.include?(:car) && @trip_types.include?(:transit))

@errors = []
@paratransit_drive_time_multiplier = 2.5
@master_service_scope = options[:available_services] || Service.all # Allow pre-filtering of available services
Expand Down Expand Up @@ -183,6 +185,10 @@ def build_transit_itineraries
build_fixed_itineraries :transit
end

def build_car_park_itineraries
build_fixed_itineraries :car_park
end

# Builds walk itineraries, using OTP by default
def build_walk_itineraries
build_fixed_itineraries :walk
Expand Down Expand Up @@ -256,7 +262,7 @@ def build_paratransit_itineraries
.find_or_initialize_by(
service_id: svc.id,
trip_type: :paratransit,
trip_id: @trip.id,
trip_id: @trip.id
)

# Whether an itinerary was found, or initialized, we need to update it
Expand Down
2 changes: 1 addition & 1 deletion app/views/admin/agencies/_edit_agency.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
- if !current_user.superuser?
= foa.input :oversight_agency_id,
disabled: !current_user.superuser?,
collection: [@agency.agency_oversight_agency.oversight_agency],
collection: [@agency.agency_oversight_agency&.oversight_agency].compact,
label_method: :name,
value_method: :id,
include_blank: false,
Expand Down
5 changes: 5 additions & 0 deletions app/views/admin/configs/_miscellaneous.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
authenticity_token: true do |f|
=remote_form_input # This sends the partial_path to the controller, so it can serve back the correct partial

= f.input :application_title, required: true,
label: "Application Title",
input_html: {value: Config.application_title},
as: :string

= f.input :dashboard_mode, require: true,
label: "Dashboard Mode",
include_blank: false,
Expand Down
Loading

0 comments on commit 116cc27

Please sign in to comment.