-
Notifications
You must be signed in to change notification settings - Fork 257
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mark Kendall #602
base: main
Are you sure you want to change the base?
Mark Kendall #602
Changes from all commits
e2f393c
c345f73
baf7b46
4811339
64fb6dd
bc8c780
2509a24
e3157b1
5046684
6e2b5a1
e8814b1
5e55a74
0e1afd1
934d3e1
b75c60a
5d032fe
9195b04
b3f1eb9
4c5e6f0
a75eaa6
f295161
281f8a4
0d9a1eb
4ee6653
8b5ab56
f54a8ce
102bbea
ae9ccd5
cdf6e0d
f8ab7e3
d29adec
88db5d0
b4aa6c1
b085fb6
01e7e07
4dcc4b2
4ba21e0
ce3ae04
cb77fe8
24513bb
04d5d4b
5a65d4e
d9881b8
9bcac62
8a60efc
1fd2f51
50d714d
5e7cdf5
517d94d
7d10ec4
aff3014
4cdc621
bb4740d
730f52d
2f19172
8d28bfa
12b94cf
ebb57fa
550bfbf
7b3229a
9b8b363
d310de2
b6dafac
8ae2a5c
f40eb13
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,7 @@ GEM | |
PLATFORMS | ||
arm64-darwin-20 | ||
arm64-darwin-21 | ||
arm64-darwin-23 | ||
|
||
DEPENDENCIES | ||
faraday | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,8 @@ | ||
#Class appears to track DMV facilities and services offered to customers | ||
require 'pry' | ||
|
||
class Dmv | ||
attr_reader :facilities | ||
|
||
def initialize | ||
@facilities = [] | ||
|
@@ -9,8 +13,166 @@ def add_facility(facility) | |
end | ||
|
||
def facilities_offering_service(service) | ||
@facilities.find do |facility| | ||
@facilities.find_all do |facility| | ||
facility.services.include?(service) | ||
end | ||
end | ||
|
||
#Larger methods below. For future: could consider classes for DMVs in each state, to better handle | ||
#idiosyncracies to procedures and different dataset formats for different states. | ||
|
||
def create_state_facilities(state, facilities_incoming_data) | ||
facilities_incoming_data.each do |facility_data| | ||
#Construct facility based on state, since dataset must be converted differently due to idiosyncracies in each case | ||
if state == "Colorado" | ||
new_facility = Facility.new(facility_parameters_co(facility_data)) | ||
elsif state == "New York" | ||
new_facility = Facility.new(facility_parameters_ny(facility_data)) | ||
elsif state == "Missouri" | ||
new_facility = Facility.new(facility_parameters_mo(facility_data)) | ||
end | ||
|
||
add_facility(new_facility) | ||
|
||
#Add services. CO specifies some of them, so those must be checked (in furture rewrite, could remake initialize() to deal with this more cleanly too) | ||
new_facility.add_service("Written Test") | ||
new_facility.add_service("Road Test") | ||
if state == "Colorado" | ||
new_facility.add_service("New Drivers License") if facility_data[:services_p].include?("registration") | ||
new_facility.add_service("Renew Drivers License") if facility_data[:services_p].include?("renew") | ||
else | ||
new_facility.add_service("New Drivers License") | ||
new_facility.add_service("Renew Drivers License") | ||
end | ||
end | ||
end | ||
|
||
def get_ev_registration_analytics(state, specified_year) | ||
#Determine most popular make/model registered, the # registered for specified year, and the county with most registered vehicles | ||
#NOTE: I kept method naming based on EV vehicles. However, now that I've extended functionality to allow NY vehicles (later in iteration 4), this method is more general, | ||
#and can analyze general NY vehicles. | ||
|
||
vehicle_tally = {} | ||
@facilities.each do |facility| | ||
if facility.state == state | ||
facility.registered_vehicles.each do |vehicle| | ||
if vehicle_tally.include?(vehicle.model) #Assume just 'model' is enough (i.e. its unique and not make + model is needed) | ||
vehicle_tally[vehicle.model] += 1 | ||
else | ||
vehicle_tally[vehicle.model] = 1 | ||
end | ||
end | ||
end | ||
end | ||
#Now find the largest count in the hash: | ||
most_popular_model = vehicle_tally.key(vehicle_tally.values.max) | ||
|
||
#Count number of occurences of given registration year in vehicle list | ||
vehicle_year_total = 0 | ||
@facilities.each do |facility| | ||
if facility.state == state | ||
vehicle_year_total += facility.registered_vehicles.count do |vehicle| | ||
vehicle.registration_date.year == specified_year | ||
end | ||
end | ||
end | ||
|
||
#Find county which has the most vehicle registrations. Note high machinery overlap with most popular model...a way to combine? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I love seeing your note around combining! Extracting a helper method that just uses a different keyword value to look for, and updates the correct key in the hash, could do the trick! |
||
county_tally = {} | ||
@facilities.each do |facility| | ||
if facility.state == state | ||
facility.registered_vehicles.each do |vehicle| | ||
if county_tally.include?(vehicle.registration_county) | ||
county_tally[vehicle.registration_county] += 1 | ||
else | ||
county_tally[vehicle.registration_county] = 1 | ||
end | ||
end | ||
end | ||
end | ||
most_popular_county = county_tally.key(county_tally.values.max) | ||
|
||
{most_popular_model: most_popular_model, number_registered_for_year: vehicle_year_total, county_most_registered_vehicles: most_popular_county} | ||
end | ||
|
||
def facility_parameters_co(facility_co_data) | ||
#Try to centralize idiosyncratic aspects of each state's dataset to specific spots in codebase | ||
#Alternate: could hosue this in Facility class. Might try that another time - would require a different format since Facility wouldn't yet be initialized | ||
{ | ||
name: facility_co_data[:dmv_office], | ||
address: "#{facility_co_data[:address_li]} #{facility_co_data[:address__1]} #{facility_co_data[:location]} #{facility_co_data[:city]} #{facility_co_data[:state]} #{facility_co_data[:zip]}", | ||
phone: facility_co_data[:phone], | ||
hours: facility_co_data[:hours] #Iteration 4: add hours (direct copy for CO) | ||
} | ||
end | ||
|
||
def facility_parameters_ny(facility_ny_data) | ||
#To format name: have to concatenate and then 'fix' capitlization the hard way (since each word is capitalized for a title usually) | ||
name_formatted = facility_ny_data[:office_name].split.map { |word| word.capitalize } | ||
name_formatted << (facility_ny_data[:office_type].split.map { |word| word.capitalize }).join(" ") | ||
name_formatted = name_formatted.join(" ") | ||
|
||
#To format address: lots of overlap here with previous entry. If I knew how to make a method that accepted varying arguments, I might be able to; don't know this yet... | ||
address_formatted = facility_ny_data[:street_address_line_1].split.map { |word| word.capitalize } | ||
address_formatted << facility_ny_data[:city].split.map { |word| word.capitalize } | ||
address_formatted << facility_ny_data[:state] | ||
address_formatted << facility_ny_data[:zip_code] | ||
address_formatted = address_formatted.join(" ") | ||
|
||
#Hours are listed for each day by opening and closing. For now, I'm going to brute-force this, | ||
#since no exact format is expected. This is a mess though: | ||
#This is gross...some facilities don't have keys for all M-F. So I need to run ifs now (otherwise problems with nil) | ||
hours_formatted = "" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This data cleaning does get a bit icky. That's why extracting into a helper method that can be called such as |
||
if facility_ny_data.include?(:monday_beginning_hours) | ||
hours_formatted = "Monday: " + facility_ny_data[:monday_beginning_hours] + " - " + facility_ny_data[:monday_ending_hours] + "; " | ||
end | ||
if facility_ny_data.include?(:tuesday_beginning_hours) | ||
hours_formatted << "Tuesday: " + facility_ny_data[:tuesday_beginning_hours] + " - " + facility_ny_data[:tuesday_ending_hours] + "; " | ||
end | ||
if facility_ny_data.include?(:wednesday_beginning_hours) | ||
hours_formatted << "Wednesday: " + facility_ny_data[:wednesday_beginning_hours] + " - " + facility_ny_data[:wednesday_ending_hours] + "; " | ||
end | ||
if facility_ny_data.include?(:thursday_beginning_hours) | ||
hours_formatted << "Thursday: " + facility_ny_data[:thursday_beginning_hours] + " - " + facility_ny_data[:thursday_ending_hours] + "; " | ||
end | ||
if facility_ny_data.include?(:friday_beginning_hours) | ||
hours_formatted << "Friday: " + facility_ny_data[:friday_beginning_hours] + " - " + facility_ny_data[:friday_ending_hours] | ||
end | ||
|
||
#Some of the office don't seem to have phone numbers...make sure to process 'nil' correctly here | ||
phone = facility_ny_data[:public_phone_number] | ||
if phone != nil | ||
phone_formatted = "(" + phone.slice(0, 3) + ") " + phone.slice(3, 3) + "-" + phone.slice(6, 4) | ||
else | ||
phone_formatted = "not applicable" | ||
end | ||
|
||
{ | ||
name: name_formatted, | ||
address: address_formatted, | ||
phone: phone_formatted, | ||
hours: hours_formatted | ||
} | ||
end | ||
|
||
def facility_parameters_mo(facility_mo_data) | ||
address_formatted = facility_mo_data[:address1].delete_suffix(",") #Certain (random?) entries end with it for some reason | ||
address_formatted = "#{address_formatted} #{facility_mo_data[:city]} #{facility_mo_data[:state]} #{facility_mo_data[:zipcode]}" | ||
hours_formatted = facility_mo_data[:daysopen] | ||
if facility_mo_data.include?(:daysclosed) | ||
hours_formatted << " except for " + facility_mo_data[:daysclosed] | ||
end | ||
|
||
facility_info = { | ||
name: facility_mo_data[:name] + " Office", #To be as consistent with other states as possible | ||
address: address_formatted, | ||
phone: facility_mo_data[:phone], | ||
#Add hours (may need to format a little to be consistent across states) | ||
#Add holidays here (since MO provides them) | ||
#Need :daysopen, :daysclosed; :holidaysclosed | ||
hours: hours_formatted, | ||
holidays: facility_mo_data[:holidaysclosed] | ||
} | ||
end | ||
|
||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,66 @@ | ||
#This tracks a specific DMV facility's core information and serviced rendered (or perhaps I should say """"""services""""""", given that it's the DMV) | ||
|
||
class Facility | ||
attr_reader :name, :address, :phone, :services | ||
attr_reader :name, :address, :phone, :services, :collected_fees, :registered_vehicles, :state, :hours, :holidays | ||
@@fee_chart = {ev: 200, antique: 25, regular: 100} #Fun new thing. Can use again and again (class var, not instance var) | ||
|
||
def initialize(name, address, phone) | ||
@name = name | ||
@address = address | ||
@phone = phone | ||
def initialize(facility_info) | ||
@name = facility_info[:name] | ||
@address = facility_info[:address] | ||
@phone = facility_info[:phone] | ||
@services = [] | ||
@collected_fees = 0 | ||
@registered_vehicles = [] | ||
#Added in iteration 4 (for by-state searching). Default is nil, if not specified (backwards compatible methinks) | ||
@state = facility_info[:state] | ||
@hours = facility_info[:hours] | ||
@holidays = facility_info[:holidays] | ||
end | ||
|
||
def add_services(service) | ||
def add_service(service) | ||
@services << service | ||
end | ||
|
||
def include?(service_specified) | ||
#NOTE: find must return 'false' if nothing found to play nice with methods accessing it | ||
@services.find(proc {false}) { |service| service == service_specified } | ||
end | ||
|
||
#Facility services | ||
|
||
def register_vehicle(vehicle) | ||
#Only register the vehicle if this facility offers the service! Otherwise don't waste time with processing. | ||
return nil if !include?("Vehicle Registration") | ||
|
||
vehicle.registration_date = Date.today | ||
|
||
#Determine plate type and collect fees (this of course assumes the registrant has/will pay) | ||
if vehicle.electric_vehicle?() | ||
vehicle.plate_type = :ev | ||
@collected_fees += @@fee_chart[:ev] | ||
elsif vehicle.antique?() | ||
vehicle.plate_type = :antique | ||
@collected_fees += @@fee_chart[:antique] | ||
else | ||
vehicle.plate_type = :regular | ||
@collected_fees += @@fee_chart[:regular] | ||
end | ||
|
||
@registered_vehicles << vehicle | ||
end | ||
|
||
def administer_written_test(registrant) | ||
#ALL conditions must be met. Pretty compact! | ||
registrant.license_data[:written] = include?("New Drivers License") && registrant.age >= 16 && registrant.permit?() | ||
end | ||
|
||
def administer_road_test(registrant) | ||
#Of course, this assumes the registrant actually passes the test (happy path) | ||
registrant.license_data[:license] = include?("Road Test") && registrant.license_data[:written] == true | ||
end | ||
|
||
def renew_drivers_license(registrant) | ||
registrant.license_data[:renewed] = include?("Renew License") && registrant.license_data[:license] == true | ||
end | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice logic here! |
||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
class Registrant | ||
attr_reader :name, :age, :license_data | ||
|
||
def initialize(name, age, permit = false) | ||
@name = name | ||
@age = age | ||
@permit = permit | ||
@license_data = {:written => false, :license => false, :renewed => false} | ||
end | ||
|
||
def permit?() | ||
@permit | ||
end | ||
|
||
def earn_permit() | ||
@permit = true | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,14 +5,21 @@ class Vehicle | |
:year, | ||
:make, | ||
:model, | ||
:engine | ||
:engine, | ||
:registration_county | ||
attr_accessor :registration_date, :plate_type | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the purposes of this project, it is totally okay to use an attr_accessor. However, we often want to stay away from these and instead create methods for changing the value of our attributes. We want to be able to have control over these data changes, and maintain data integrity. Right now, someone could change the registration_date to |
||
|
||
def initialize(vehicle_details) | ||
@vin = vehicle_details[:vin] | ||
@year = vehicle_details[:year] | ||
@year = vehicle_details[:year].to_i #Need to_i to permit proper function of antique?() method | ||
@make = vehicle_details[:make] | ||
@model = vehicle_details[:model] | ||
@engine = vehicle_details[:engine] | ||
@registration_date = nil | ||
|
||
#Could make a registration_data hash for compactness, but will keep vars separate to mimic interaction pattern | ||
@plate_type = nil | ||
@registration_county = vehicle_details[:county] #This could either be set based on where car is built, OR where it's registered | ||
end | ||
|
||
def antique? | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
#This 'manufactures' cars. Kinda a misnomer, since it just takes existing registration data from API | ||
#(doesn't really build them), but I see why we could do this to 'help' the Vehicle class along. | ||
|
||
class VehicleFactory | ||
attr_reader :vehicles_manufactured | ||
|
||
def initialize() | ||
@vehicles_manufactured = [] | ||
end | ||
|
||
def create_vehicles(vehicle_registration_list, state) | ||
#Different states produce different datasets, so need to switch based on this. | ||
|
||
vehicle_registration_list.each do |vehicle| | ||
if state == "Washington" | ||
vehicle_data = { | ||
vin: vehicle[:vin_1_10], | ||
make: vehicle[:make], | ||
model: vehicle[:model], | ||
year: vehicle[:model_year], | ||
#Assume the engine is always ev here in WA (though not generally true - happy path for now) | ||
engine: :ev, | ||
county: vehicle[:county] | ||
} | ||
elsif state == "New York" | ||
#Engine type isn't specified in dataset. Let's not get too fancy here, so just provide three options: :ev, :ice, or :other | ||
case vehicle[:fuel_type] | ||
when "ELECTRIC" | ||
engine_type = :ev | ||
when "GAS" || "DIESEL" || "PROPANE" || "FLEX" #There are several fossil-fuel types...these are the only ones I'm pretty confident about | ||
engine_type = :ice | ||
else | ||
engine_type = :other | ||
end | ||
|
||
vehicle_data = { | ||
vin: vehicle[:vin], | ||
make: vehicle[:make], | ||
model: "data not available", #Not provided in NY dataset, hence this value given | ||
year: vehicle[:model_year], | ||
engine: engine_type, | ||
county: vehicle[:county].capitalize #Be consistent between states | ||
} | ||
else | ||
puts "Error: state invalid / not recognized. Cannot build vehicles from registry." | ||
return [] #To be consistent with typical return type | ||
end | ||
|
||
@vehicles_manufactured << Vehicle.new(vehicle_data) | ||
end | ||
|
||
return @vehicles_manufactured | ||
end | ||
|
||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider breaking up some of this logic into well-named helper methods in order to shorten this method. We typically want methods to stay between 8-10 lines, so using a helper method like tally_vehicle could be a great way to extract some of this into a separate method.