Skip to content

Commit e43544b

Browse files
committed
Add epa data to all tests where it works
1 parent 5252e4c commit e43544b

File tree

3 files changed

+316
-90
lines changed

3 files changed

+316
-90
lines changed

libvin/epa.py

Lines changed: 77 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ def __init__(self, vin):
2525
self.__nhtsa = nhtsa_decode(vin)
2626
self.__attribs = self.__get_attributes()
2727
self.__model = self.__get_model()
28-
(self.__ids, self.__trims) = self.__get_ids()
29-
self.__eco = self.__get_vehicle_economy()
28+
if (self.__model != None):
29+
self.__ids, self.__trims = self.__get_ids()
30+
self.__eco = self.__get_vehicle_economy()
3031

3132
@property
3233
def nhtsa(self):
@@ -58,6 +59,15 @@ def id(self):
5859
# first one. We're guessing anyway, what with the fuzzy matching and all...
5960
return self.__ids[0]
6061

62+
@property
63+
def trim(self):
64+
'''
65+
EPA trim for this vehicle.
66+
'''
67+
# FIXME: If we don't know which trim exactly, just pick the
68+
# first one. We're guessing anyway, what with the fuzzy matching and all...
69+
return self.__trims[0]
70+
6171
@property
6272
def eco(self):
6373
'''
@@ -80,7 +90,7 @@ def __get_attributes(self):
8090
driveType = self.nhtsa['DriveType']
8191
if 'AWD' in driveType:
8292
attributes.append("AWD")
83-
if '4WD' in driveType or '4x4' in driveType:
93+
elif '4WD' in driveType or '4x4' in driveType:
8494
attributes.append("4WD")
8595
elif '4x2' in driveType:
8696
attributes.append("2WD")
@@ -94,9 +104,16 @@ def __get_attributes(self):
94104
attributes.append(self.nhtsa['BodyClass'])
95105
if 'Series' in self.nhtsa and self.nhtsa['Series'] != "":
96106
attributes.append(self.nhtsa['Series'])
107+
if 'Series2' in self.nhtsa and self.nhtsa['Series2'] != "":
108+
attributes.append(self.nhtsa['Series2'])
97109

98110
if 'DisplacementL' in self.nhtsa and self.nhtsa['DisplacementL'] != '':
99111
attributes.append('%s L' % self.nhtsa['DisplacementL'])
112+
# EPA sometimes likes to go all precise
113+
if '.' not in self.nhtsa['DisplacementL']:
114+
attributes.append('%s.0 L' % self.nhtsa['DisplacementL'])
115+
if 'EngineCylinders' in self.nhtsa and self.nhtsa['EngineCylinders'] != '':
116+
attributes.append('%s cyl' % self.nhtsa['EngineCylinders'])
100117

101118
if 'Manual' in self.nhtsa['TransmissionStyle']:
102119
attributes.append('MAN')
@@ -106,6 +123,10 @@ def __get_attributes(self):
106123
attributes.append('CVT')
107124
attributes.append('Variable')
108125

126+
# Twin turbo is "Yes, Yes"!
127+
if 'Turbo' in self.nhtsa and 'Yes' in self.nhtsa['Turbo']:
128+
attributes.append('Turbo')
129+
109130
return attributes
110131

111132
def __get_possible_models(self):
@@ -171,40 +192,45 @@ def __get_possible_ids(self):
171192
return None
172193
return id2trim
173194

174-
def __fuzzy_match(self, attributes, choices):
195+
def __fuzzy_match(self, mustmatch, attributes, choices):
175196
'''
176197
Given a base name and a bunch of attributes, find the choice that matches them the best.
177-
name : string
198+
mustmatch : string
178199
attributes : string[]
179200
choices : dict mapping id to string
180201
Returns: array of ids of best matching choices
181202
'''
182203

183-
best_matches = 0
184-
best_ids = []
185-
best_fraction = 0
204+
best_ids = [] # id of best matching trims
205+
best_len = 0 # len of best matching trims
206+
best_matched = 0
186207
for (key, val) in choices.iteritems():
187-
n_matches = 0
188-
chars_total = len(val)
208+
# optional mandatory attribute
209+
# to prevent [Q60 AWD] from matching Q85 AWD instead of Q60 AWD Coupe
210+
if mustmatch != None and mustmatch.upper() not in val.upper():
211+
continue
212+
# Find choice that matches most chars from attributes.
213+
# In case of a tie, prefer shortest choice.
189214
chars_matched = 0
190215
for attrib in attributes:
191216
if attrib != "" and attrib.upper() in val.upper():
192-
chars_matched += len(attrib)
193-
n_matches += 1
194-
fraction = float(chars_matched) / chars_total
195-
#print "n_matches %d, chars_matched %d, chars_total %d, fraction %f for %s" % (n_matches, chars_matched,chars_total, fraction, val)
196-
if (n_matches > best_matches):
217+
if chars_matched == 0:
218+
chars_matched = len(attrib)
219+
else:
220+
chars_matched += len(attrib) + 1 # for space
221+
#print "chars_matched %d, for %s" % (chars_matched, val)
222+
if (chars_matched > best_matched):
197223
best_ids = [key]
198-
best_matches = n_matches
199-
best_fraction = fraction
200-
elif ((n_matches > 0) and (n_matches == best_matches)):
201-
# Heuristic: favor most complete match (so missing an attribute like Hybrid hurts)
202-
if fraction > best_fraction:
224+
best_len = len(val)
225+
best_matched = chars_matched
226+
elif (chars_matched > 0 and chars_matched == best_matched):
227+
if len(val) < best_len:
228+
#print "chars %d == %d, len %d < %d, breaking tie in favor of shorter trim" % (chars_matched, best_matched, len(val), best_len)
203229
best_ids = [key]
204-
best_matches = n_matches
205-
best_fraction = fraction
206-
elif fraction == best_fraction:
207-
#print "%d == %d, marking tie" % (n_matches, best_matches)
230+
best_len = len(val)
231+
best_matched = chars_matched
232+
elif len(val) == best_len:
233+
#print "chars %d == %d, len %d == %d, marking tie" % (chars_matched, best_matched, len(val), best_len)
208234
best_ids.append(key)
209235
if len(best_ids) == 0:
210236
print "epa:__fuzzy_match: no match found for vin %s" % self.vin
@@ -220,7 +246,31 @@ def __get_model(self):
220246
id2models = self.__get_possible_models()
221247
if id2models == None:
222248
return None
223-
ids = self.__fuzzy_match(self.__attribs, id2models)
249+
#print "Finding model for vin %s" % self.vin
250+
# Special case for Mercedes-Benz, which puts the real model in Series
251+
oldmodel = self.nhtsa['Model']
252+
model = oldmodel.replace('-Class', '')
253+
ids = self.__fuzzy_match(model, self.__attribs, id2models)
254+
if len(ids) != 1:
255+
# Second chance for alternate spellings
256+
if '4WD' in self.__attribs:
257+
tribs = self.__attribs
258+
tribs.append('AWD')
259+
print "Searching again with AWD"
260+
ids = self.__fuzzy_match(self.nhtsa['Model'], tribs, id2models)
261+
elif '2WD' in self.__attribs and 'FWD' not in self.__attribs:
262+
tribs = self.__attribs
263+
tribs.append('RWD')
264+
print "Searching again with RWD"
265+
ids = self.__fuzzy_match(self.nhtsa['Model'], tribs, id2models)
266+
elif 'Mazda' in self.nhtsa['Model']:
267+
oldmodel = self.nhtsa['Model']
268+
model = oldmodel.replace('Mazda', '')
269+
tribs = self.__attribs
270+
tribs.append(model)
271+
print "Searching again with %s instead of %s" % (model, oldmodel)
272+
ids = self.__fuzzy_match(model, tribs, id2models)
273+
224274
if len(ids) != 1:
225275
print "epa:__get_model: Failed to find model for vin %s" % self.vin
226276
return None
@@ -238,7 +288,8 @@ def __get_ids(self):
238288
id2trim = self.__get_possible_ids()
239289
if id2trim == None:
240290
return None
241-
ids = self.__fuzzy_match(self.__attribs, id2trim)
291+
#print "Finding trims for vin %s" % self.vin
292+
ids = self.__fuzzy_match(None, self.__attribs, id2trim)
242293
if len(ids) == 0:
243294
print "epa:__get_id: No trims found for vin %s" % self.vin
244295
return None

0 commit comments

Comments
 (0)