Skip to content

Instantly share code, notes, and snippets.

@bskari
Last active June 28, 2017 05:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bskari/1bf6628543900a3f36b3744f8f28ea66 to your computer and use it in GitHub Desktop.
Save bskari/1bf6628543900a3f36b3744f8f28ea66 to your computer and use it in GitHub Desktop.
Formats aCar's CSV backup file format into its importable full backup file format
*inf
*xml
*abp
records.csv
sample
"""Formats aCar's CSV backup output into its full backup format.
When moving to a new phone, I accidentally used the CSV backup option instead
of the full backup format, and aCar can't import its CSV format. It's more used
for loading into a spreadsheet.
Run this on the output CSV file to create a full backup suitable for importing
back into aCar.
"""
from __future__ import print_function
import csv
import datetime
import re
import sys
import xml.dom.minidom
import zipfile
def main():
"""Main."""
if len(sys.argv) != 2:
print('Usage: {} <aCar-records.csv>'.format(sys.argv[0]))
return
# The full backup file is a zip with 6 files:
# vehicles.xml - computed from the CSV backup
# expenses.xml - not currently supported
# metadata.inf
# trip-types.xml - only includes defaults, no custom
# fuel-types.xml - only includes defaults, no custom
# preferences.xml - only includes defaults, no custom
# services.xml - only includes defaults, no custom
# vehicles.xml
with open(sys.argv[1]) as csv_file:
vehicles = skip_to_heading_and_parse('Vehicles', csv_file)
print('Got {} vehicles'.format(len(vehicles)))
fill_ups = skip_to_heading_and_parse('Fill-Up Records', csv_file)
print('Got {} fill ups'.format(len(fill_ups)))
service_records = skip_to_heading_and_parse('Service Records', csv_file)
print('Got {} service records'.format(len(service_records)))
expense_records = skip_to_heading_and_parse('Expense Records', csv_file)
print('Got {} expense records'.format(len(expense_records)))
trip_records = skip_to_heading_and_parse('Trip Records', csv_file)
print('Got {} trip records'.format(len(trip_records)))
if len(expense_records) != 0:
print('Formatting of expense records is not supported!')
if len(trip_records) != 0:
print('Formatting of trip records is not supported!')
xml_string = format_vehicles(vehicles, fill_ups, service_records)
dom = xml.dom.minidom.parseString(xml_string)
with open('vehicles.xml', 'wb') as vehicles_file:
vehicles_file.write(dom.toprettyxml(encoding='UTF-8'))
# metadata.inf
metadata = DEFAULT_METADATA.copy()
metadata['acar.backup.data-count.vehicles'] = str(len(vehicles))
metadata['acar.backup.data-count.fillup-records'] = str(len(fill_ups))
metadata['acar.backup.data-count.service-records'] = str(len(service_records))
metadata['acar.backup.data-count.trip-records'] = str(len(trip_records))
metadata['acar.backup.data-count.expense-records'] = str(len(expense_records))
# I don't see this in the CSV at all, so just hard code to 0
metadata['acar.backup.data-count.vehicle-parts'] = '0'
write_metadata(metadata)
# Default files
write_expense_types()
write_service_types()
write_trip_types()
write_fuel_types()
write_preferences()
file_names = (
'vehicles.xml',
'expenses.xml',
'metadata.inf',
'trip-types.xml',
'fuel-types.xml',
'preferences.xml',
'services.xml',
)
# Zip them all
now = datetime.datetime.now()
zip_file_name = datetime.datetime.strftime(now, 'aCar-%m%d%y-%H%M.abp')
with zipfile.ZipFile(zip_file_name, 'w') as zip_file:
for file_name in file_names:
zip_file.write(file_name)
print('Wrote to ' + zip_file_name)
def format_keys(dictionary):
"""Format keys for XML."""
formatted_dict = {}
for key, value in dictionary.items():
formatted_key = re.sub(r'\s+', '-', key).replace('?', '').lower()
formatted_dict[formatted_key] = value
return formatted_dict
def format_vehicles(old_vehicle_records, old_fill_up_records, old_service_records):
"""Prints the vehicles as XML."""
return_string = '<vehicles>'
# Fix up keys to match the XML format
vehicles = []
for record in old_vehicle_records:
vehicles.append(format_keys(record))
format_vehicle_records(vehicles)
services = []
for record in old_service_records:
services.append(format_keys(record))
format_service_records(services)
fill_ups = []
for record in old_fill_up_records:
fill_ups.append(format_keys(record))
format_fillup_records(vehicles, fill_ups)
# Just double check that I have the right keys
check_vehicle_keys(vehicles[0])
check_service_record_keys(services[0])
check_fill_up_keys(fill_ups[0])
service_to_id = dict(((s['name'], s['id']) for s in DEFAULT_SERVICES))
fur_count = 1
serv_count = 1
for index, veh in enumerate(vehicles):
id_ = len(vehicles) - index
return_string += '<vehicle id="{}">'.format(id_)
return_string += dict_to_xml(veh)
return_string += get_default_service_reminders_xml()
return_string += '<fillup-records>'
for fur in fill_ups:
if 'vehicle' in fur and fur['vehicle'] == veh['name']:
return_string += '<fillup-record id="{}">'.format(fur_count)
fur_count += 1
del fur['vehicle']
return_string += dict_to_xml(fur)
return_string += '</fillup-record>'
return_string += '</fillup-records>'
return_string += '<service-records>'
for serv in services:
if 'vehicle' in serv and serv['vehicle'] == veh['name']:
return_string += '<service-record id="{}">'.format(serv_count)
serv_count += 1
del serv['vehicle']
specific_services = serv['services'].split(', ')
return_string += '<services>'
for specific in specific_services:
if specific not in service_to_id:
raise ValueError('Unknown service: "{}"'.format(specific))
return_string += '<service id="{}" />'.format(service_to_id[specific])
return_string += '</services>'
del serv['services']
return_string += dict_to_xml(serv)
return_string += '</service-record>'
return_string += '</service-records>'
return_string += '</vehicle>'
return_string += '</vehicles>'
return return_string
def format_vehicle_records(vehicles):
"""Adds, formats, and removes fields."""
for veh in vehicles:
# Add empty fields
for empty_field in ('trip-records', 'expense-records', 'vehicle-parts'):
veh[empty_field] = ''
veh['active'] = 'true' if veh['active'] == 'Yes' else 'false'
def format_service_records(services):
"""Adds, formats and removes fields."""
for serv in services:
# Format date
month, day, year = (int(i) for i in serv['date'].split('/'))
hour, minute = (int(i) for i in serv['time'].split(':'))
time_stamp = datetime.datetime(year, month, day, hour, minute)
serv['date'] = datetime.datetime.strftime(time_stamp, '%m/%d/%Y - %H:%M')
del serv['time']
# Rename a field
serv['payment-type'] = serv['payment']
del serv['payment']
# Remove unused field
del serv['distance-unit']
# Fix formatting
serv['odometer-reading'] = serv['odometer-reading'].replace(',', '')
serv['total-cost'] = serv['total-cost'].replace('$', '')
def format_fillup_records(vehicles, fill_ups):
"""Adds, formats, and removes fields."""
# Reformat the dates in the fill up records
for fur in fill_ups:
month, day, year = (int(i) for i in fur['date'].split('/'))
hour, minute = (int(i) for i in fur['time'].split(':'))
fur['datetime'] = datetime.datetime(year, month, day, hour, minute)
fur['date'] = datetime.datetime.strftime(fur['datetime'], '%m/%d/%Y - %H:%M')
fill_ups = sorted(fill_ups, key=lambda record: record['datetime'], reverse=True)
fuel_type_to_id = dict(
(
'{} - {} ({})'.format(f['category'], f['grade'], f['octane']).lower(),
f['id']
) for f in DEFAULT_FUEL_TYPES
)
# If no type is given, just default to an arbitrary ID
fuel_type_to_id[''] = '1'
for fur in fill_ups:
# Just convert this now for ease later
fur['odometer-reading'] = float(fur['odometer-reading'].replace(',', ''))
# Remove unused fields
for unused_field in (
# We'll remove 'datetime' later
'distance-unit', 'fuel-efficiency-unit', 'time', 'volume-unit'
):
del fur[unused_field]
# Rename and reformat other fields
fur['partial'] = fur['partial-fill-up'].lower() == 'yes'
del fur['partial-fill-up']
fur['previous-missed-fillups'] = fur['previously-missed-fill-ups'].lower() == 'yes'
del fur['previously-missed-fill-ups']
fur['has-fuel-additive'] = 'true' if fur['has-fuel-additive'].lower() == 'yes' else 'false'
fur['driving-mode'] = fur['driving-mode'].lower()
fur['fuel-type-id'] = fuel_type_to_id[fur['fuel-type'].lower()]
del fur['fuel-type']
for new_key, old_key in (
('price-per-volume-unit', 'price-per-unit'),
('payment-type', 'payment'),
):
fur[new_key] = fur[old_key]
del fur[old_key]
for key in ('price-per-volume-unit', 'total-cost'):
fur[key] = fur[key].replace('$', '')
# Add new fields
for veh in vehicles:
previous_odometer_reading = None
previous_fillup_time = None
previous_volume = 0.0
for fur in (i for i in fill_ups if i['vehicle'] == veh['name']):
if previous_odometer_reading is None:
fur['distance-till-next-fillup'] = 0
fur['distance-for-fuel-efficiency'] = 0
else:
fur['distance-till-next-fillup'] = previous_odometer_reading - fur['odometer-reading']
fur['distance-for-fuel-efficiency'] = fur['distance-till-next-fillup']
previous_odometer_reading = fur['odometer-reading']
if fur['partial'] or fur['previous-missed-fillups']:
fur['distance-for-fuel-efficiency'] = 0
else:
fur['distance-for-fuel-efficiency'] = fur['distance-till-next-fillup']
if previous_fillup_time is None:
fur['time-till-next-fillup'] = 0
else:
diff = previous_fillup_time - fur['datetime']
fur['time-till-next-fillup'] = diff.total_seconds() * 1000
previous_fillup_time = fur['datetime']
fur['volume-for-fuel-efficiency'] = previous_volume
previous_volume = fur['volume']
# Distance for fuel efficiency is just the driven distance of the last
# entry, as long as we didn't skip a fill up. Normally I'd zip two
# iterations that are one off, or use an index here, but that won't work
# because different vehicle fill ups may be interleaved.
fill_ups.reverse()
previous_odometer_reading = None
previous_fillup_time = None
previous_volume = 0.0
for fur in (i for i in fill_ups if i['vehicle'] == veh['name']):
if previous_odometer_reading is None:
fur['driven-distance'] = 0
else:
fur['driven-distance'] = previous_odometer_reading - fur['odometer-reading']
previous_odometer_reading = fur['odometer-reading']
if previous_fillup_time is None:
fur['time-since-previous-fillup'] = 0
else:
diff = previous_fillup_time - fur['datetime']
fur['time-since-previous-fillup'] = diff.total_seconds() * 1000
previous_fillup_time = fur['datetime']
fill_ups.reverse()
for fur in fill_ups:
del fur['datetime']
def get_default_service_reminders_xml():
"""Returns an XML string with the default service reminders."""
service_reminders = (
('12', '12000', '', '', 'false', 'false', '', ''),
('12', '12000', '', '', 'false', 'false', '', ''),
('36', '36000', '', '', 'false', 'false', '', ''),
('12', '12000', '', '', 'false', 'false', '', ''),
('12', '12000', '', '', 'false', 'false', '', ''),
('3', '3000', '', '', 'false', 'false', '', ''),
('15', '15000', '', '', 'false', 'false', '', ''),
('15', '15000', '', '', 'false', 'false', '', ''),
('12', '12000', '', '', 'false', 'false', '', ''),
('24', '24000', '', '', 'false', 'false', '', ''),
('6', '6000', '', '', 'false', 'false', '', ''),
('12', '12000', '', '', 'false', 'false', '', ''),
('30', '30000', '', '', 'false', 'false', '', ''),
('12', '12000', '', '', 'false', 'false', '', ''),
('24', '24000', '', '', 'false', 'false', '', ''),
('3', '3000', '', '', 'false', 'false', '', ''),
('6', '6000', '', '', 'false', 'false', '', ''),
('24', '24000', '', '', 'false', 'false', '', ''),
('24', '24000', '', '', 'false', 'false', '', ''),
('24', '24000', '', '', 'false', 'false', '', ''),
('12', '12000', '', '', 'false', 'false', '', ''),
('12', '12000', '', '', 'false', 'false', '', ''),
('12', '12000', '', '', 'false', 'false', '', ''),
('6', '6000', '', '', 'false', 'false', '', ''),
('12', '12000', '', '', 'false', 'false', '', ''),
('6', '6000', '', '', 'false', 'false', '', ''),
('24', '24000', '', '', 'false', 'false', '', ''),
('3', '3000', '', '', 'false', 'false', '', ''),
('3', '3000', '', '', 'false', 'false', '', ''),
('10', '10000', '', '', 'false', 'false', '', ''),
('6', '6000', '', '', 'false', 'false', '', ''),
('6', '6000', '', '', 'false', 'false', '', ''),
('12', '12000', '', '', 'false', 'false', '', ''),
('6', '6000', '', '', 'false', 'false', '', ''),
('12', '12000', '', '', 'false', 'false', '', ''),
('3', '3000', '', '', 'false', 'false', '', ''),
('3', '3000', '', '', 'false', 'false', '', ''),
('12', '12000', '', '', 'false', 'false', '', ''),
('3', '3000', '', '', 'false', 'false', '', ''),
('6', '6000', '', '', 'false', 'false', '', ''),
)
return_string = '<service-reminders>'
for index, rem in enumerate(service_reminders):
return_string += '<service-reminder id="{}" service-id="{}">'.format(index + 1, index + 1)
rem_dict = {}
for index2, key in enumerate((
'time', 'distance', 'due-time', 'due-distance',
'time-alert-silent', 'distance-alert-silent', 'last-time-alert',
'last-distance-alert'
)):
rem_dict[key] = rem[index2]
return_string += dict_to_xml(rem_dict)
return_string += '</service-reminder>'
return_string += '</service-reminders>'
return return_string
def dict_to_xml(dictionary):
"""Concerts a dictionary to XML."""
return_string = ''
sorted_keys = sorted(dictionary.keys())
for key in sorted_keys:
return_string += '<{}>'.format(key)
if isinstance(dictionary[key], bool):
return_string += str(dictionary[key]).lower()
else:
return_string += str(dictionary[key])
return_string += '</{}>'.format(key)
return return_string
def skip_to_heading_and_parse(heading, csv_file):
"""Reads until a certain heading is found then parses the data."""
line = csv_file.readline()
heading_with_newline = '{}\n'.format(heading)
while line != '' and line != heading_with_newline:
line = csv_file.readline()
line = csv_file.readline()
keys = parse_line(line)
entries = []
try:
while True:
row = parse_line(csv_file.readline())
entry = {}
for index, key in enumerate(keys):
entry[key] = row[index]
entries.append(entry)
except IndexError:
return entries
def parse_line(line):
"""Parses a single CSV line."""
reader = csv.reader((line,))
return next(reader)
def check_vehicle_keys(vehicle_record):
"""Checks that I have all the proper vehicle keys."""
my_keys = set((vehicle_record.keys()))
their_keys = set((
'name',
'notes',
'model',
'make',
'year',
'license-plate',
'vin',
'insurance-policy',
'body-style',
'color',
'engine-displacement',
'fuel-tank-capacity',
'purchase-price',
'purchase-odometer-reading',
'purchase-date',
'selling-price',
'selling-odometer-reading',
'selling-date',
'active',
'vehicle-parts',
'service-reminders',
'fillup-records',
'service-records',
'expense-records',
'trip-records',
))
# We print these records manually after we format, so ignore
their_keys = their_keys - set((
'fillup-records',
'service-records',
'service-reminders',
))
if my_keys != their_keys:
raise ValueError(
'Missing {} and have extra {} keys'.format(
their_keys - my_keys,
my_keys - their_keys
)
)
def check_fill_up_keys(fill_up_record):
"""Checks that I have all the proper fill up keys."""
my_keys = set((fill_up_record.keys()))
their_keys = set((
'average-speed',
'city-driving-percentage',
'date',
'distance-for-fuel-efficiency',
'distance-till-next-fillup',
'driven-distance',
'driving-mode',
'fuel-additive-name',
'fuel-brand',
'fuel-efficiency',
'fuel-type-id',
'fueling-station-address',
'has-fuel-additive',
'highway-driving-percentage',
'latitude',
'longitude',
'notes',
'odometer-reading',
'partial',
'payment-type',
'previous-missed-fillups',
'price-per-volume-unit',
'tags',
'time-since-previous-fillup',
'time-till-next-fillup',
'total-cost',
'volume',
'volume-for-fuel-efficiency',
))
# 'vehicle' is needed when linking up the fill up record to the vehicle, but
# will be removed before printing, so ignore it
my_keys = my_keys - set(('vehicle',))
if my_keys != their_keys:
raise ValueError(
'Missing {} and have extra {} keys'.format(
their_keys - my_keys,
my_keys - their_keys
)
)
def check_service_record_keys(service_record):
"""Checks that I have all the proper service record keys."""
my_keys = set((service_record.keys()))
their_keys = set((
'date',
'latitude',
'longitude',
'notes',
'odometer-reading',
'payment-type',
'service-center-address',
'service-center-name',
'services',
'tags',
'total-cost',
))
# 'vehicle' is needed when linking up the fill up record to the vehicle, but
# will be removed before printing, so ignore it
my_keys = my_keys - set(('vehicle',))
if my_keys != their_keys:
raise ValueError(
'Missing {} and have extra {} keys'.format(
their_keys - my_keys,
my_keys - their_keys
)
)
def write_metadata(metadata):
"""Writes the metadata file part of the backup."""
assert(set(DEFAULT_METADATA.keys()) | set(TO_FILL_METADATA_KEYS) == set(metadata.keys()))
with open('metadata.inf', 'w') as file_:
for key, value in metadata.items():
file_.write('{}={}\n'.format(key, value.replace(':', '\\:')))
def write_expense_types():
"""Writes the expenses types file part of the backup."""
xml_string = '<expenses>'
for exp in DEFAULT_EXPENSES:
xml_string += '<expense id="{}">'.format(exp['id'])
for key in ('name', 'notes'):
xml_string += '<{}>{}</{}>'.format(key, exp[key], key)
xml_string += '</expense>'
xml_string += '</expenses>'
dom = xml.dom.minidom.parseString(xml_string)
with open('expenses.xml', 'wb') as file_:
file_.write(dom.toprettyxml(encoding='UTF-8'))
def write_service_types():
"""Writes the service types file part of the backup."""
xml_string = '<services>'
for serv in DEFAULT_SERVICES:
xml_string += '<service id="{}">'.format(serv['id'])
for key in ('name', 'notes', 'time-reminder', 'distance-reminder'):
xml_string += '<{}>{}</{}>'.format(key, serv[key].replace('&', '&amp;'), key)
xml_string += '</service>'
xml_string += '</services>'
dom = xml.dom.minidom.parseString(xml_string)
with open('services.xml', 'wb') as file_:
file_.write(dom.toprettyxml(encoding='UTF-8'))
def write_trip_types():
"""Writes the trip types file part of the backup."""
xml_string = '<trip-types>'
for trip in DEFAULT_TRIP_TYPES: # pylint: disable=W0621
xml_string += '<trip-type id="{}">'.format(trip['id'])
for key in ('name', 'default-tax-deduction-rate', 'notes'):
xml_string += '<{}>{}</{}>'.format(key, trip[key], key)
xml_string += '</trip-type>'
xml_string += '</trip-types>'
dom = xml.dom.minidom.parseString(xml_string)
with open('trip-types.xml', 'wb') as file_:
file_.write(dom.toprettyxml(encoding='UTF-8'))
def write_fuel_types():
"""Writes the fuel types file part of the backup."""
xml_string = '<fuel-types>'
for fuel in DEFAULT_FUEL_TYPES:
xml_string += '<fuel-type id="{}">'.format(fuel['id'])
for key in ('category', 'grade', 'octane', 'cetane', 'notes'):
xml_string += '<{}>{}</{}>'.format(key, fuel[key], key)
xml_string += '</fuel-type>'
xml_string += '</fuel-types>'
dom = xml.dom.minidom.parseString(xml_string)
with open('fuel-types.xml', 'wb') as file_:
file_.write(dom.toprettyxml(encoding='UTF-8'))
def write_preferences():
"""Writes the preferences types file part of the backup."""
xml_string = '<preferences>'
for pref in DEFAULT_PREFERENCES: # pylint: disable=W0621
xml_string += '<preference name="{}" type="{}">{}</preference>'.format(
pref['name'], pref['type'], pref['value']
)
xml_string += '</preferences>'
dom = xml.dom.minidom.parseString(xml_string)
with open('preferences.xml', 'wb') as file_:
file_.write(dom.toprettyxml(encoding='UTF-8'))
# Default services as parsed from the output XML file
DEFAULT_SERVICES = [
dict(
(key, service[index])
for index, key in enumerate(
('id', 'name', 'notes', 'time-reminder', 'distance-reminder')
)
) for service in (
# I know these are out of order, but that's how they were in the XML
# test file backup that I made
(2, 'A/C System', 'Including Water Pump, Fans and Refrigerant', '12', '12000'),
(1, 'Air Filter', '', '12', '12000'),
(3, 'Battery', '', '36', '36000'),
(4, 'Belts', 'Fan, Ribbed and Timing Belts', '12', '12000'),
(5, 'Body/Chassis', '', '12', '12000'),
(6, 'Brake Fluid', 'Fluid needed for braking system operation', '3', '3000'),
(7, 'Brakes Front', '', '15', '15000'),
(8, 'Brakes Rear', '', '15', '15000'),
(9, 'Cabin Air Filter', '', '12', '12000'),
(11, 'Clutch Hydraulic Fluid', '', '24', '24000'),
(10, 'Clutch Hydraulic System', '', '6', '6000'),
(12, 'Cooling System', '', '12', '12000'),
(13, 'Differential Fluid', '', '30', '30000'),
(14, 'Doors', '', '12', '12000'),
(15, 'Engine Antifreeze', 'Radiator Coolant Fluid', '24', '24000'),
(16, 'Engine Oil', '', '3', '3000'),
(17, 'Exhaust System', '', '6', '6000'),
(18, 'Fuel Filter', '', '24', '24000'),
(19, 'Fuel Lines & Pipes', '', '24', '24000'),
(20, 'Fuel Pump', '', '24', '24000'),
(21, 'Fuel System', '', '12', '12000'),
(22, 'Glass/Mirrors', '', '12', '12000'),
(23, 'Heating System', '', '12', '12000'),
(24, 'Horns', '', '6', '6000'),
(25, 'Inspection', '', '12', '12000'),
(26, 'Lights', '', '6', '6000'),
(27, 'New Tires', '', '24', '24000'),
(28, 'Oil Filter', '', '3', '3000'),
(29, 'Power Steering Fluid', '', '3', '3000'),
(30, 'Radiator', '', '10', '10000'),
(31, 'Rotate Tires', '', '6', '6000'),
(32, 'Safety Devices', '', '6', '6000'),
(33, 'Spark Plugs', '', '12', '12000'),
(34, 'Steering System', '', '6', '6000'),
(35, 'Suspension System', '', '12', '12000'),
(36, 'Tire Pressure', 'Controlling and balancing the tire pressure', '3', '3000'),
(37, 'Transmission Fluid', 'Automatic Transmission Fluid', '3', '3000'),
(38, 'Wheel Alignment', '', '12', '12000'),
(39, 'Windshield Washer Fluid', '', '3', '3000'),
(40, 'Windshield Wipers', '', '6', '6000'),
)
]
# Default fuel types as parsed from the output XML file
DEFAULT_FUEL_TYPES = [
dict(
(key, service[index])
for index, key in enumerate(
('id', 'category', 'grade', 'octane', 'cetane', 'notes')
)
) for service in (
# I know these are out of order, but that's how they were in the XML
# test file backup that I made
('22', 'bioalcohol', 'E10', '0', '0', ''),
('27', 'bioalcohol', 'E100', '0', '0', ''),
('23', 'bioalcohol', 'E22 - Gasohol', '0', '0', ''),
('24', 'bioalcohol', 'E50', '0', '0', ''),
('25', 'bioalcohol', 'E85', '0', '0', ''),
('26', 'bioalcohol', 'E93', '0', '0', ''),
('20', 'biodiesel', 'B99', '0', '0', ''),
('17', 'biodiesel', 'Blend B2', '0', '0', ''),
('18', 'biodiesel', 'Blend B5', '0', '0', ''),
('19', 'biodiesel', 'Blend B20', '0', '50', ''),
('21', 'biodiesel', 'B100', '0', '55', ''),
('14', 'diesel', '4D', '0', '0', ''),
('15', 'diesel', 'Synthetic', '0', '0', ''),
('13', 'diesel', '2D', '0', '40', ''),
('12', 'diesel', '1D', '0', '44', ''),
('16', 'diesel', 'ULSD', '0', '45', ''),
('28', 'gas', 'Autogas/LPG', '0', '0', ''),
('29', 'gas', 'CNG - Methane', '0', '0', ''),
('1', 'gasoline', 'Low', '85', '0', ''),
('2', 'gasoline', 'Regular', '87', '0', ''),
('3', 'gasoline', 'Mid', '88', '0', ''),
('4', 'gasoline', 'Mid', '89', '0', ''),
('5', 'gasoline', 'High', '90', '0', ''),
('6', 'gasoline', 'Premium', '91', '0', ''),
('7', 'gasoline', 'Premium', '92', '0', ''),
('8', 'gasoline', 'Premium', '93', '0', ''),
('9', 'gasoline', 'Super Premium', '94', '0', ''),
('10', 'gasoline', 'Super Premium', '95', '0', ''),
('11', 'gasoline', 'Super Premium', '98', '0', ''),
)
]
DEFAULT_PREFERENCES = [
dict(
(key, pref[index])
for index, key in enumerate(
('name', 'type', 'value')
)
) for pref in (
('acar.chart.mark-average.distance-between-fillups', 'java.lang.Boolean', 'true'),
('acar.visible.fuel-additive', 'java.lang.Boolean', 'false'),
('acar.visible.vehicle-selling-info', 'java.lang.Boolean', 'true'),
('acar.status-bar.notification-led', 'java.lang.Boolean', 'true'),
('acar.visible.vehicle-engine-displacement', 'java.lang.Boolean', 'false'),
('acar.visible.vehicle-insurance-policy', 'java.lang.Boolean', 'true'),
('acar.chart.mark-average.fuel-volume-per-fillup', 'java.lang.Boolean', 'true'),
('acar.last-execution.version', 'java.lang.String', '4.8.6'),
('acar.visible.fueling-station', 'java.lang.Boolean', 'true'),
('acar.chart.mark-average.fuel-efficiency', 'java.lang.Boolean', 'true'),
('acar.visible.trip-client', 'java.lang.Boolean', 'true'),
('acar.visible.vehicle-purchase-info', 'java.lang.Boolean', 'true'),
('acar.main-toolbox.visibility', 'java.lang.String', 'full'),
('acar.automatic-backup.frequency', 'java.lang.String', '720'),
('acar.fuel-efficiency-unit', 'java.lang.String', 'MPG (US)'),
('acar.currency-fraction-digits', 'java.lang.String', '-1'),
('acar.browse-records.sort-order', 'java.lang.String', 'desc'),
('acar.due-distance-alert.beginning-percentage', 'java.lang.Integer', '10'),
('acar.main-status-trends-block.visible', 'java.lang.Boolean', 'true'),
('acar.use-geographical-location', 'java.lang.Boolean', 'true'),
('acar.status-bar.notifications', 'java.lang.Boolean', 'true'),
('acar.visible.vehicle-body-style', 'java.lang.Boolean', 'false'),
('acar.visible.average-speed', 'java.lang.Boolean', 'false'),
('acar.visible.driving-mode', 'java.lang.Boolean', 'false'),
('acar.locale', 'java.lang.String', 'system'),
('acar.visible.trip-tax-deduction', 'java.lang.Boolean', 'true'),
('acar.visible.trip-reimbursement', 'java.lang.Boolean', 'true'),
('acar.chart.mark-average.time-between-fillups', 'java.lang.Boolean', 'true'),
('acar.date-format.compact', 'java.lang.String', 'MM/dd/yy'),
('acar.visible.service-center', 'java.lang.Boolean', 'true'),
('acar.chart.mark-average.fuel-expenses', 'java.lang.Boolean', 'true'),
('acar.chart.mark-average.service-expenses', 'java.lang.Boolean', 'true'),
('acar.last-selected.vehicle', 'java.lang.Long', '1'),
('acar.chart.mark-average.expense-costs', 'java.lang.Boolean', 'true'),
('acar.visible.trip-location', 'java.lang.Boolean', 'true'),
('acar.visible.tags', 'java.lang.Boolean', 'true'),
('acar.distance-unit', 'java.lang.String', 'mi'),
('acar.visible.payment-type', 'java.lang.Boolean', 'true'),
('acar.visible.trip-purpose', 'java.lang.Boolean', 'true'),
('acar.visible.driving-condition', 'java.lang.Boolean', 'false'),
('acar.date-format.full', 'java.lang.String', 'MMM dd, yyyy'),
('acar.volume-unit', 'java.lang.String', 'gal (US)'),
('acar.fuel-price-auto-add-nine-tenth-decimal-digit', 'java.lang.Boolean', 'true'),
('acar.visible.expense-center', 'java.lang.Boolean', 'true'),
('acar.visible.notes', 'java.lang.Boolean', 'true'),
('acar.visible.vehicle-fuel-tank-capacity', 'java.lang.Boolean', 'true'),
('acar.visible.vehicle-vin', 'java.lang.Boolean', 'true'),
('acar.currency', 'java.lang.String', '$'),
('acar.fuel-efficiency-calculation-method', 'java.lang.String', 'previous-record'),
('acar.due-time-alert.beginning-percentage', 'java.lang.Integer', '10'),
('acar.visible.vehicle-license-plate', 'java.lang.Boolean', 'true'),
('acar.visible.color', 'java.lang.Boolean', 'false'),
('acar.visible.fuel-type', 'java.lang.Boolean', 'true'),
('acar.automatic-backup.history-count', 'java.lang.String', '25'),
('acar.chart.mark-average.fuel-price-per-volume-unit', 'java.lang.Boolean', 'true'),
)
]
DEFAULT_EXPENSES = [
dict(
(key, expense[index])
for index, key in enumerate(
('id', 'name', 'notes')
)
) for expense in (
('1', 'Accident', ''),
('2', 'Car Wash', ''),
('3', 'Fine', ''),
('4', 'Insurance', ''),
('5', 'MOT', ''),
('6', 'Parking', ''),
('7', 'Payment', ''),
('8', 'Registration', 'License Plate Fee'),
('9', 'Tax', ''),
('10', 'Tolls', ''),
('11', 'Tow', 'Getting Towed'),
)
]
DEFAULT_TRIP_TYPES = [
dict(
(key, trip[index])
for index, key in enumerate(
('id', 'name', 'default-tax-deduction-rate', 'notes')
)
) for trip in (
('1', 'Business', '0.575', ''),
('2', 'Charity', '0.14', ''),
('3', 'Medical', '0.23', ''),
('4', 'Moving', '0.23', ''),
('6', 'Other', '0.0', ''),
('5', 'Personal', '0.0', ''),
)
]
DEFAULT_METADATA = {
'acar.version': '4.8.6',
'device.model': 'SGH-M919',
'acar.build.date': '10/21/2015',
'device.device-name': 'jfltetmo',
'acar.backup.data-count.expenses': '11',
'device.product': 'jfltetmo',
'acar.version.minimum-supported': '3.0.0',
'acar.pro.status': 'true',
'acar.database.version': '14',
'acar.backup.version': '9',
'device.brand': 'samsung',
'acar.backup.type': 'Full-Backup',
'android.version': '4.4.4',
'device.manufacturer': 'samsung',
'acar.backup.datetime': '06/24/2017 - 12:58',
# These are computed
'acar.backup.data-count.services': str(len(DEFAULT_SERVICES)),
'acar.backup.data-count.fuel-types': str(len(DEFAULT_FUEL_TYPES)),
'acar.backup.data-count.trip-types': str(len(DEFAULT_TRIP_TYPES)),
}
# These keys' values need to be filled in in DEFAULT_METADATA
TO_FILL_METADATA_KEYS = (
'acar.backup.data-count.vehicles',
'acar.backup.data-count.fillup-records',
'acar.backup.data-count.service-records',
'acar.backup.data-count.trip-records',
'acar.backup.data-count.vehicle-parts',
'acar.backup.data-count.expense-records',
)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment