Small improvements to interface and help text
[munter.git] / munter / munter.py
1 # -*- coding: utf-8 -*-
2
3
4 """
5 Munter Time Calculation
6 Alexander Vasarab
7 Wylark Mountaineering LLC
8
9 A rudimentary program which implements the Munter time calculation.
10 """
11
12 import sys
13 import argparse
14
15 class InvalidUnitsException(Exception):
16 pass
17
18 rates = {
19 'uphill': { 'rate': 4, 'direction': '↑' },
20 'flat': { 'rate': 6, 'direction': '→' }, # or downhill on foot
21 'downhill': { 'rate': 10, 'direction': '↓' },
22 'bushwhacking': { 'rate': 2, 'direction': '↹' },
23 }
24
25 unit_choices = ['metric', 'imperial']
26 travel_mode_choices = rates.keys()
27
28 def time_calc(distance, elevation, rate='uphill', units='imperial'):
29 retval = {}
30
31 if units not in unit_choices:
32 raise InvalidUnitsException
33
34 unit_count = 0
35
36 if 'imperial' == units:
37 # convert to metric
38 distance = (distance * 1.609) # mi to km
39 elevation = (elevation * .305) # ft to m
40
41 unit_count = distance + (elevation / 100.0)
42
43 retval['time'] = (distance + (elevation / 100.0)) / rates[rate]['rate']
44 retval['unit_count'] = unit_count
45 retval['direction'] = rates[rate]['direction']
46 retval['pace'] = rates[rate]['rate']
47
48 return retval
49
50 def print_ugly_estimate(est):
51 hours = int(est['time'])
52 minutes = int((est['time'] - hours) * 60)
53 print("{human_time}".format(
54 human_time="{hours} hours {minutes} minutes".format(
55 hours=hours,
56 minutes=minutes)))
57
58 def print_pretty_estimate(est):
59 hours = int(est['time'])
60 minutes = int((est['time'] - hours) * 60)
61
62 # NOTE: Below, the line with the unicode up arrow uses an alignment
63 # value of 31. In the future, consider using e.g. wcwidth
64 # library so that this is more elegant.
65 print("\n\t╒═══════════════════════════════╕")
66 print("\t╎▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒╎╮")
67 print("\t╎▒{:^29}▒╎│".format(''))
68 print("\t╎▒{pace_readable:^31}▒╎│".format(
69 pace_readable="{units} {direction} @ {pace}".format(
70 units=round(est['unit_count']),
71 direction=est['direction'],
72 pace=est['pace'])))
73 print("\t╎▒{human_time:^29}▒╎│".format(
74 human_time="{hours} hours {minutes} minutes".format(
75 hours=hours,
76 minutes=minutes)))
77 print("\t╎▒{:^29}▒╎│".format(''))
78 print("\t╎▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒╎│")
79 print("\t╘═══════════════════════════════╛│")
80 print("\t └───────────────────────────────┘\n")
81
82 def get_parser():
83 parser = argparse.ArgumentParser(description='Implementation of '
84 'the Munter time calculation')
85
86 parser.add_argument('--distance',
87 '-d',
88 type=float,
89 required=True,
90 help='Distance (in km, by default)')
91
92 parser.add_argument('--elevation',
93 '-e',
94 type=float,
95 required=True,
96 help='Elevation change (in m, by default)')
97
98 parser.add_argument('--travel-mode',
99 '-t',
100 type=str,
101 default='uphill',
102 choices=travel_mode_choices, required=False,
103 help='Travel mode (uphill, by default)')
104
105 parser.add_argument('--units',
106 '-u',
107 type=str,
108 default='imperial',
109 required=False,
110 choices=unit_choices,
111 help='Units of input values')
112
113 parser.add_argument('--pretty',
114 '-p',
115 action='store_true',
116 default=False,
117 required=False,
118 help="Make output pretty");
119
120 return parser
121
122 def main():
123 parser = get_parser()
124 opts = parser.parse_args()
125
126 distance = opts.distance
127 elevation = opts.elevation
128 units = opts.units
129 travel_mode = opts.travel_mode
130
131 time_estimate = time_calc(distance=distance, elevation=elevation,
132 rate=travel_mode, units=units)
133
134 if opts.pretty:
135 print_pretty_estimate(time_estimate)
136 else:
137 print_ugly_estimate(time_estimate)
138
139 return 0
140
141 if __name__ == "__main__":
142 sys.exit(main())