def from_instructions(cls, instructions: str, data: FirstData, time_index: int, rp: FirstPace): """ Create a step from an instruction string :param instructions: instruction string :type instructions: str :param data: First database :type data: FirstData :param time_index: the index in the paces table :type time_index: int :param rp: race pace :type rp: FirstPace :return: the step :rtype: FirstStep """ segment_name = instructions.split('@')[0] increment = None pace = None duration = None try: segment = data.segment_by_name(segment_name) distance = segment.distance except KeyError: distance = FirstDistance.from_string(segment_name) segment_name = instructions.split('@')[1] pace_list = segment_name.split('+') segment_name = pace_list[0] if len(pace_list) > 1: increment = int(pace_list[1]) else: increment = None if segment_name == 'RP': # special case for race-pace segment = None pace = FirstPace.copy(rp) else: segment = data.segment_by_name(segment_name) if segment is not None: if segment.ref_pace_name is not None: segment_name = segment.ref_pace_name segment_index = data.segment_index_by_name( segment_name) + 1 # +1 since the first column is the ref time pace = data.segments_paces[time_index][segment_index] duration = segment.duration if increment is not None: pace.increment(increment) return cls(name=instructions, pace=pace, time=duration, distance=distance)
def generate_workouts(self, data: FirstData) -> None: """ Generate the training plan :param data: the database :type data: FirstData """ self.can_generate_workouts() if self.workouts is not None and len(self.workouts) > 0: del self.workouts[:] FirstStepBase.reset_global_id( ) # ids are auto incremented. Make sure you start from 0 index = data.race_type_index_by_name(name=self.race.race_type.name) plan_instructions = data.plan_instructions[index] time_index = data.pace_index_by_race_time( race_time=self.race.target_time, race_name=self.race.race_type.name) # TODO for now all plans have 3 weekly key-runs. Add a parameter num_weekly_runs to generalize num_weekly_runs = 3 num_weeks = len(plan_instructions.instructions) / num_weekly_runs start_date = self.race.race_date - timedelta(weeks=(num_weeks - 1)) dow = start_date.weekday() delta = dow - self.weekly_schedule[0] start_date = start_date - timedelta(days=delta) delta = self.weekly_schedule[1] - self.weekly_schedule[0] second_date = start_date + timedelta(days=delta) delta = self.weekly_schedule[2] - self.weekly_schedule[0] third_date = start_date + timedelta(days=delta) week_dates = [start_date, second_date, third_date] weekday_index = 0 race_pace = self.race.race_pace() for wi in plan_instructions.instructions: self.workouts.append( FirstWorkout.from_instructions( instructions=wi, wo_date=week_dates[weekday_index], data=data, time_index=time_index, race_pace=race_pace)) week_dates[weekday_index] += timedelta(days=7) weekday_index = (weekday_index + 1) % num_weekly_runs if self.workouts[-1].workout_date != self.race.race_date: self.workouts[-1].workout_date = self.race.race_date if self.workouts[-2].workout_date >= self.race.race_date: self.workouts[-2].workout_date -= timedelta(days=1)
def test_segments(self): data_file_path = expanduser( '~') + '/PycharmProjects/first/database/FIRSTregularPlans.xml' try: # good path data = FirstData(xml_path=data_file_path) self.assertEqual(14, len(data.segments)) self.assertEqual('400m distance 400.0 m', str(data.segments[0])) self.assertEqual('long pace', str(data.segments[-5])) self.assertEqual('cooldown time 0:10:00 easy', str(data.segments[-2])) self.assertEqual('5K', data.reference_race) self.assertEqual(91, len(data.segments_paces)) self.assertEqual('0:15:00', str(data.segments_paces[0][0])) self.assertEqual('0:04:22 min per mile', str(data.segments_paces[0][4])) self.assertEqual('0:20:00', str(data.segments_paces[30][0])) self.assertEqual('0:05:56 min per mile', str(data.segments_paces[30][3])) self.assertEqual('0:30:00', str(data.segments_paces[-1][0])) self.assertEqual('0:11:31 min per mile', str(data.segments_paces[-1][11])) except ValueError as vex: self.fail(str(vex)) except IOError as ioex: self.fail(str(ioex))
def test_plan_instructions(self): data_file_path = expanduser( '~') + '/PycharmProjects/first/database/FIRSTregularPlans.xml' try: # good path data = FirstData(xml_path=data_file_path) self.assertEqual(4, len(data.plan_instructions)) plan1 = data.plan_instructions[0] self.assertEqual('5K plan instructions', plan1.name) self.assertEqual('5K', plan1.race_name) self.assertEqual(36, len(plan1.instructions)) self.assertEqual('1 1 warmup#8x(400m#400 m@RI)cooldown', plan1.instructions[0]) self.assertEqual('5 1 warmup#4x(1000m#400 m@RI)cooldown', plan1.instructions[12]) plan2 = data.plan_instructions[1] self.assertEqual('10K plan instructions', plan2.name) self.assertEqual('10K', plan2.race_name) self.assertEqual(36, len(plan2.instructions)) self.assertEqual('1 1 warmup#8x(400m#400 m@RI)cooldown', plan2.instructions[0]) self.assertEqual('2 3 7 mile@long', plan2.instructions[5]) plan3 = data.plan_instructions[2] self.assertEqual('Half Marathon plan instructions', plan3.name) self.assertEqual('HalfMarathon', plan3.race_name) self.assertEqual(54, len(plan3.instructions)) self.assertEqual('1 1 warmup#12x(400m#300 m@RI)cooldown', plan3.instructions[0]) self.assertEqual('13 3 10 mile@RP+20', plan3.instructions[38]) plan4 = data.plan_instructions[3] self.assertEqual('Marathon plan instructions', plan4.name) self.assertEqual('Marathon', plan4.race_name) self.assertEqual(48, len(plan4.instructions)) self.assertEqual('1 1 warmup#3x(1600m#200 m@RI)cooldown', plan4.instructions[0]) self.assertEqual('16 3 26.22 mile@RP', plan4.instructions[47]) except ValueError as vex: self.fail(str(vex)) except IOError as ioex: self.fail(str(ioex))
def test_plan_instructions(self): try: # good path data = FirstData(json_path=Config.DATABASE_JSON) self.assertEqual(4, len(data.plan_instructions)) plan1 = data.plan_instructions[0] self.assertEqual('5K plan instructions', plan1.name) self.assertEqual('5K', plan1.race_name) self.assertEqual(36, len(plan1.instructions)) self.assertEqual('1 1 warmup#8x(400m#400 m@RI)cooldown', plan1.instructions[0]) self.assertEqual('5 1 warmup#4x(1000m#400 m@RI)cooldown', plan1.instructions[12]) plan2 = data.plan_instructions[1] self.assertEqual('10K plan instructions', plan2.name) self.assertEqual('10K', plan2.race_name) self.assertEqual(36, len(plan2.instructions)) self.assertEqual('1 1 warmup#8x(400m#400 m@RI)cooldown', plan2.instructions[0]) self.assertEqual('2 3 7 mile@long', plan2.instructions[5]) plan3 = data.plan_instructions[2] self.assertEqual('Half Marathon plan instructions', plan3.name) self.assertEqual('HalfMarathon', plan3.race_name) self.assertEqual(54, len(plan3.instructions)) self.assertEqual('1 1 warmup#12x(400m#300 m@RI)cooldown', plan3.instructions[0]) self.assertEqual('13 3 10 mile@RP+20', plan3.instructions[38]) plan4 = data.plan_instructions[3] self.assertEqual('Marathon plan instructions', plan4.name) self.assertEqual('Marathon', plan4.race_name) self.assertEqual(48, len(plan4.instructions)) self.assertEqual('1 1 warmup#3x(1600m#200 m@RI)cooldown', plan4.instructions[0]) self.assertEqual('16 3 26.22 mile@RP', plan4.instructions[47]) except ValueError as vex: self.fail(str(vex)) except IOError as ioex: self.fail(str(ioex))
def test_segments(self): try: # good path data = FirstData(json_path=Config.DATABASE_JSON) self.assertEqual(14, len(data.segments)) self.assertEqual('400m distance 400.0 m', str(data.segments[0])) self.assertEqual('long pace', str(data.segments[-5])) self.assertEqual('cooldown time 0:10:00 easy', str(data.segments[-2])) self.assertEqual('5K', data.reference_race) self.assertEqual(91, len(data.segments_paces)) self.assertEqual('0:15:00', str(data.segments_paces[0][0])) self.assertEqual('0:04:22 min per mile', str(data.segments_paces[0][4])) self.assertEqual('0:20:00', str(data.segments_paces[30][0])) self.assertEqual('0:05:56 min per mile', str(data.segments_paces[30][3])) self.assertEqual('0:30:00', str(data.segments_paces[-1][0])) self.assertEqual('0:11:31 min per mile', str(data.segments_paces[-1][11])) except ValueError as vex: self.fail(str(vex)) except IOError as ioex: self.fail(str(ioex))
def main(): args = process_args() if args.output not in ['text', 'tcx', 'both']: raise ValueError('output should be one of "text", "tcx", or "both"') data_file_path = expanduser('~') + '/PycharmProjects/first/database/FIRSTregularPlans.xml' data = FirstData(xml_path=data_file_path) runner = FirstRunner(name=args.runner_name) target_time = FirstTime.from_string(args.target_time) if args.ref_race_type is not None: target_time = data.equivalent_time(time_from=target_time, race_index_from=data.race_type_index_by_name(args.ref_race_type), race_index_to=data.race_type_index_by_name(args.race_type)) if args.race_name is not None: race_name = args.race_name else: race_name = args.race_type race_date = datetime.datetime.strptime(args.race_date, '%m/%d/%Y').date() race = FirstRace(race_type=data.get_race_type_by_name(args.race_type), name=race_name, race_date=race_date, target_time=target_time) ws = get_keyrun_days(args.keyrun_days) plan = FirstPlan(name=args.race_name, weekly_schedule=ws, race=race, runner=runner) plan.generate_workouts(data=data) base_file_name = str(race_date) + race_name if args.output == 'text' or args.output == 'both': file_name = expanduser('~/Downloads/') + base_file_name + '.txt' target = open(file_name, 'w') target.write(plan.details(level=3)) target.close() if args.output == 'tcx' or args.output == 'both': file_name = expanduser('~/Downloads/') + base_file_name + '.tcx' target = open(file_name, 'w') target.write(plan.tcx()) target.close()
def test_equivalent_time(self): try: # good path data = FirstData(json_path=Config.DATABASE_JSON) self.assertEqual(4, len(data.race_types)) self.assertEqual(1, data.race_type_index_by_name(name='10K')) self.assertEqual(2, data.race_type_index_by_name(name='HalfMarathon')) self.assertEqual(91, len(data.race_times)) self.assertEqual(4, len(data.race_times[0])) from_time = FirstTime.from_string(string='0:20:13') self.assertEqual( '1:34:15', str( data.equivalent_time(time_from=from_time, race_index_from=0, race_index_to=2))) from_time = FirstTime.from_string('1:54:32') self.assertEqual( '4:01:39', str( data.equivalent_time(time_from=from_time, race_index_from=2, race_index_to=3))) except ValueError as vex: self.fail(str(vex)) except IOError as ioex: self.fail(str(ioex)) try: # bad race name _ = data.race_type_index_by_name('lulu') self.fail('Should not get here with a bad race type name') except ValueError as ex: self.assertEqual('Race type lulu not found', str(ex)) try: # time not found high from_time = FirstTime.from_string('4:49:59') _ = data.equivalent_time(time_from=from_time, race_index_from=2, race_index_to=0) self.fail('Should not get here with time not found') except ValueError as ex: self.assertEqual('Time is longer than the highest database time', str(ex)) try: # time not found low from_time = FirstTime.from_string('0:49:59') _ = data.equivalent_time(time_from=from_time, race_index_from=2, race_index_to=0) self.fail('Should not get here with time not found') except ValueError as ex: self.assertEqual('Time is shorter than the lowest database time', str(ex)) try: # index out of range _ = data.equivalent_time(time_from=from_time, race_index_from=-1, race_index_to=2) self.fail('Should not get here with bad index') except ValueError as ex: self.assertEqual('Race index must be between 0 and 3', str(ex)) try: # index out of range _ = data.equivalent_time(time_from=from_time, race_index_from=4, race_index_to=2) self.fail('Should not get here with bad index') except ValueError as ex: self.assertEqual('Race index must be between 0 and 3', str(ex)) try: # index out of range _ = data.equivalent_time(time_from=from_time, race_index_from=1, race_index_to=-2) self.fail('Should not get here with bad index') except ValueError as ex: self.assertEqual('Race index must be between 0 and 3', str(ex)) try: # index out of range _ = data.equivalent_time(time_from=from_time, race_index_from=1, race_index_to=55) self.fail('Should not get here with bad index') except ValueError as ex: self.assertEqual('Race index must be between 0 and 3', str(ex)) try: # bad database file _ = FirstData(json_path='lulu') self.fail('Should not get here with bad file name') except IOError as ioex: self.assertEqual("[Errno 2] No such file or directory: 'lulu'", str(ioex))
def test_generate_workouts(self): data_file_path = expanduser( '~') + '/PycharmProjects/first/database/FIRSTregularPlans.xml' data = FirstData(xml_path=data_file_path) ws1 = [0, 2, 5] target_time = data.equivalent_time( time_from=FirstTime(minutes=30), race_index_from=data.race_type_index_by_name('5K'), race_index_to=data.race_type_index_by_name('Marathon')) sf_marathon = FirstRace( race_type=data.get_race_type_by_name('Marathon'), name='San Francisco Marathon', race_date=date(year=2017, month=7, day=23), target_time=target_time) me = FirstRunner(name='Daniel BenDavid', age=56, gender='m', email='*****@*****.**') p1 = FirstPlan(name='My first marathon training plan', weekly_schedule=ws1, race=sf_marathon, runner=me) try: # positive p1.generate_workouts(data=data) # print p1.details(1) self.assertEqual(48, len(p1.workouts)) wo = p1.workouts[0] self.assertEqual('Week 1 Keyrun 1', wo.name) self.assertEqual(3, len(wo.steps)) step = wo.steps[0] self.assertEqual('warmup', step.name) self.assertEqual(0, step.step_id) self.assertEqual('time', step.get_duration_type()) self.assertEqual('0:15:00', str(step.time)) self.assertEqual('0:11:31 min per mile', str(step.pace)) step = wo.steps[1] self.assertEqual('repeat X 3', step.name) self.assertEqual(1, step.step_id) self.assertEqual(3, step.repeat) # repeat self.assertEqual(2, len(step.steps)) substep = step.steps[0] self.assertEqual('1600m', substep.name) self.assertEqual(2, substep.step_id) self.assertEqual('distance', substep.get_duration_type()) self.assertEqual('1600.0 m', str(substep.distance)) self.assertEqual('0:09:26 min per mile', str(substep.pace)) substep = step.steps[1] self.assertEqual('200 m@RI', substep.name) self.assertEqual(3, substep.step_id) self.assertEqual('distance', substep.get_duration_type()) self.assertEqual('200.0 m', str(substep.distance)) self.assertEqual('0:11:31 min per mile', str(substep.pace)) step = wo.steps[2] self.assertEqual('cooldown', step.name) self.assertEqual(4, step.step_id) self.assertEqual('time', step.get_duration_type()) self.assertEqual('0:10:00', str(step.time)) self.assertEqual('0:11:31 min per mile', str(step.pace)) file_name = expanduser( '~/PycharmProjects/first/database/cmp_plan_marathon.tcx') # to_file = open(file_name, 'w') # to_file.write(p1.tcx()) # to_file.close() from_file = open(file_name) cmp_string = from_file.read() from_file.close() self.assertEqual(cmp_string, p1.tcx()) except ValueError as vex: self.fail(str(vex)) except TypeError as tex: self.fail(str(tex)) ws1 = [0, 3, 6] target_time = data.equivalent_time( time_from=FirstTime(minutes=22, seconds=36), race_index_from=data.race_type_index_by_name('5K'), race_index_to=data.race_type_index_by_name('HalfMarathon')) sf_half_marathon = FirstRace( race_type=data.get_race_type_by_name('HalfMarathon'), name='San Francisco Marathon', race_date=date(year=2017, month=7, day=23), target_time=target_time) me = FirstRunner(name='Daniel BenDavid', age=56, gender='m', email='*****@*****.**') p2 = FirstPlan(name='San Francisco half-marathon training plan', weekly_schedule=ws1, race=sf_half_marathon, runner=me) try: # positive p2.generate_workouts(data=data) # print p2.details(1) # print p2.details(2) file_name = expanduser( '~/PycharmProjects/first/database/cmp_plan_half_marathon.tcx') # to_file = open(file_name, 'w') # $ to_file.write(p2.tcx()) # to_file.close() from_file = open(file_name) cmp_string = from_file.read() from_file.close() self.assertEqual(cmp_string, p2.tcx()) except ValueError as vex: self.fail(str(vex)) except TypeError as tex: self.fail(str(tex))
def test_from_instructions(self): rp = FirstPace.from_string(str_input='0:09:35 min per mile') ti = 50 wo_date = date(2017, 8, 21) data = FirstData(json_path=Config.DATABASE_JSON) instructions = '1 1 warmup#3x(1600m#200 m@RI)cooldown' FirstStepBase.reset_global_id() try: wo1 = FirstWorkout.from_instructions(instructions=instructions, wo_date=wo_date, data=data, time_index=ti, race_pace=rp) self.assertEqual('Week 1 Keyrun 1', wo1.name) self.assertEqual('2017-08-21', str(wo1.workout_date)) self.assertEqual('scheduled', wo1.status) self.assertEqual('warmup#3x(1600m#200 m@RI)cooldown', wo1.note) self.assertEqual(3, len(wo1.steps)) # Time steps step = wo1.steps[0] self.assertEqual(0, step.step_id) self.assertEqual('warmup', step.name) self.assertEqual('0:09:23 min per mile', str(step.pace)) self.assertEqual(None, step.distance) self.assertEqual('time', step.get_duration_type()) self.assertEqual('0:15:00', str(step.time)) self.assertEqual(15.0, step.total(what='time', unit='minute')) self.assertAlmostEqual(1.59858, step.total(what='distance', unit='mile'), 5) step = wo1.steps[2] self.assertEqual(4, step.step_id) self.assertEqual('cooldown', step.name) self.assertEqual('0:09:23 min per mile', str(step.pace)) self.assertEqual(None, step.distance) self.assertEqual('time', step.get_duration_type()) self.assertEqual('0:10:00', str(step.time)) self.assertEqual(600.0, step.total(what='time', unit='second')) self.assertAlmostEqual(1.06572, step.total(what='distance', unit='mile'), 5) # Repeat step step = wo1.steps[1] self.assertEqual(1, step.step_id) self.assertEqual('repeat X 3', step.name) self.assertEqual(3, step.repeat) self.assertEqual(2, len(step.steps)) substep = step.steps[0] self.assertEqual(2, substep.step_id) self.assertEqual('1600m', substep.name) self.assertEqual('0:07:18 min per mile', str(substep.pace)) self.assertEqual(None, substep.time) self.assertEqual('distance', substep.get_duration_type()) self.assertEqual('1600.0 m', str(substep.distance)) self.assertAlmostEqual(7.25, substep.total(what='time', unit='minute'), 5) self.assertAlmostEqual(0.99419, substep.total(what='distance', unit='mile'), 5) substep = step.steps[1] self.assertEqual(3, substep.step_id) self.assertEqual('200 m@RI', substep.name) self.assertEqual('0:09:23 min per mile', str(substep.pace)) self.assertEqual(None, substep.time) self.assertEqual('distance', substep.get_duration_type()) self.assertEqual('200.0 m', str(substep.distance)) self.assertAlmostEqual(1.16667, substep.total(what='time', unit='minute'), 5) self.assertAlmostEqual(0.12427, substep.total(what='distance', unit='mile'), 5) # Repeat steps totals self.assertAlmostEqual(25.25, step.total(what='time', unit='minute'), 5) self.assertAlmostEqual(3.35540, step.total(what='distance', unit='mile'), 5) # Workout totals self.assertAlmostEqual(50.25, wo1.total(what='time', unit='minute'), 5) self.assertAlmostEqual(6.01970, wo1.total(what='distance', unit='mile'), 5) # tcx file_name = 'cmp_workout1.tcx' with open('{}/{}'.format(Config.TEST_RESOURCE_DIR, file_name), 'r') as from_file: cmp_string = from_file.read() self.assertEqual(cmp_string, wo1.tcx().indented_str()) # json file_name = 'cmp_workout1.json' with open('{}/{}'.format(Config.TEST_RESOURCE_DIR, file_name), 'r') as from_file: cmp_json = json.load(from_file) FirstUtils.assert_deep_almost_equal(self, cmp_json, wo1.to_json(), 5) file_name = 'cmp_workout1_km.json' with open('{}/{}'.format(Config.TEST_RESOURCE_DIR, file_name), 'r') as from_file: cmp_json = json.load(from_file) FirstUtils.assert_deep_almost_equal( self, cmp_json, wo1.to_json(output_unit='km'), 5) # html file_name = 'cmp_workout1.html' with open('{}/{}'.format(Config.TEST_RESOURCE_DIR, file_name), 'r') as from_file: cmp_html = from_file.read() self.assertEqual(cmp_html, wo1.to_html().indented_str()) file_name = 'cmp_workout1_km.html' with open('{}/{}'.format(Config.TEST_RESOURCE_DIR, file_name), 'r') as from_file: cmp_html = from_file.read() self.assertEqual(cmp_html, wo1.to_html(output_unit='km').indented_str()) except ValueError as ex: self.fail(str(ex)) instructions = '1 2 warmup#3x(1600m#4x(200 m@RI)800m)cooldown' try: wo2 = FirstWorkout.from_instructions(instructions=instructions, wo_date=wo_date, data=data, time_index=ti, race_pace=rp) self.assertEqual('Week 1 Keyrun 2', wo2.name) self.assertEqual('2017-08-21', str(wo2.workout_date)) self.assertEqual('scheduled', wo2.status) self.assertEqual('warmup#3x(1600m#4x(200 m@RI)800m)cooldown', wo2.note) self.assertEqual(3, len(wo2.steps)) # recursive repeat step step = wo2.steps[1] self.assertEqual(6, step.step_id) self.assertEqual('repeat X 3', step.name) self.assertEqual(3, step.repeat) self.assertEqual(3, len(step.steps)) substep = step.steps[1] self.assertEqual(8, substep.step_id) self.assertEqual('repeat X 4', substep.name) self.assertEqual(4, substep.repeat) self.assertEqual(1, len(substep.steps)) subsubstep = substep.steps[0] self.assertEqual(9, subsubstep.step_id) self.assertEqual('200 m@RI', subsubstep.name) self.assertEqual('0:09:23 min per mile', str(subsubstep.pace)) self.assertEqual(None, subsubstep.time) self.assertEqual('distance', subsubstep.get_duration_type()) self.assertEqual('200.0 m', str(subsubstep.distance)) self.assertAlmostEqual( 1.16667, subsubstep.total(what='time', unit='minute'), 5) self.assertAlmostEqual( 0.12427, subsubstep.total(what='distance', unit='mile'), 5) self.assertAlmostEqual(4.66667, substep.total(what='time', unit='minute'), 5) self.assertAlmostEqual(0.4971, substep.total(what='distance', unit='mile'), 5) self.assertAlmostEqual(46.2, step.total(what='time', unit='minute'), 5) self.assertAlmostEqual(5.96516, step.total(what='distance', unit='mile'), 5) self.assertAlmostEqual(71.2, wo2.total(what='time', unit='minute'), 5) self.assertAlmostEqual(8.62946, wo2.total(what='distance', unit='mile'), 5) file_name = 'cmp_workout2.tcx' with open('{}/{}'.format(Config.TEST_RESOURCE_DIR, file_name), 'r') as from_file: cmp_string = from_file.read() self.assertEqual(cmp_string, wo2.tcx().indented_str()) except ValueError as ex: self.fail(str(ex)) instructions = '1 1 warmup#3x(1600m#200 m@RI#cooldown' try: # unbalanced parentheses _ = FirstWorkout.from_instructions(instructions=instructions, wo_date=wo_date, data=data, time_index=ti, race_pace=rp) except ValueError as ex: self.assertEqual('Unbalanced parentheses', str(ex))
def test_generate_workouts(self): data = FirstData(json_path=Config.DATABASE_JSON) ws1 = [0, 2, 5] target_time = data.equivalent_time(time_from=FirstTime(minutes=30), race_index_from=data.race_type_index_by_name('5K'), race_index_to=data.race_type_index_by_name('Marathon')) sf_marathon = FirstRace(race_type=data.get_race_type_by_name('Marathon'), name='San Francisco Marathon', race_date=date(year=2017, month=7, day=23), target_time=target_time) me = FirstRunner(name='Daniel BenDavid', age=56, gender='m', email='*****@*****.**') p1 = FirstPlan(name='My first marathon training plan', weekly_schedule=ws1, race=sf_marathon, runner=me) try: # positive p1.generate_workouts(data=data) self.assertEqual(48, len(p1.workouts)) wo = p1.workouts[0] self.assertEqual('Week 1 Keyrun 1', wo.name) self.assertEqual(3, len(wo.steps)) step = wo.steps[0] self.assertEqual('warmup', step.name) self.assertEqual(0, step.step_id) self.assertEqual('time', step.get_duration_type()) self.assertEqual('0:15:00', str(step.time)) self.assertEqual('0:11:31 min per mile', str(step.pace)) step = wo.steps[1] self.assertEqual('repeat X 3', step.name) self.assertEqual(1, step.step_id) self.assertEqual(3, step.repeat) # repeat self.assertEqual(2, len(step.steps)) substep = step.steps[0] self.assertEqual('1600m', substep.name) self.assertEqual(2, substep.step_id) self.assertEqual('distance', substep.get_duration_type()) self.assertEqual('1600.0 m', str(substep.distance)) self.assertEqual('0:09:26 min per mile', str(substep.pace)) substep = step.steps[1] self.assertEqual('200 m@RI', substep.name) self.assertEqual(3, substep.step_id) self.assertEqual('distance', substep.get_duration_type()) self.assertEqual('200.0 m', str(substep.distance)) self.assertEqual('0:11:31 min per mile', str(substep.pace)) step = wo.steps[2] self.assertEqual('cooldown', step.name) self.assertEqual(4, step.step_id) self.assertEqual('time', step.get_duration_type()) self.assertEqual('0:10:00', str(step.time)) self.assertEqual('0:11:31 min per mile', str(step.pace)) file_name = 'cmp_plan_marathon.tcx' with open('{}/{}'.format(Config.TEST_RESOURCE_DIR, file_name), 'r') as from_file: cmp_string = from_file.read() self.assertEqual(cmp_string, p1.tcx()) file_name = 'cmp_plan_marathon.json' with open('{}/{}'.format(Config.TEST_RESOURCE_DIR, file_name), 'r') as from_file: cmp_json = json.load(from_file) self.assertEqual(cmp_json, p1.to_json()) file_name = 'cmp_plan_marathon.html' with open('{}/{}'.format(Config.TEST_RESOURCE_DIR, file_name), 'r') as from_file: cmp_html = from_file.read() self.assertEqual(cmp_html, p1.to_html()) except ValueError as vex: self.fail(str(vex)) except TypeError as tex: self.fail(str(tex)) ws1 = [0, 3, 6] target_time = data.equivalent_time(time_from=FirstTime(minutes=22, seconds=36), race_index_from=data.race_type_index_by_name('5K'), race_index_to=data.race_type_index_by_name('HalfMarathon')) sf_half_marathon = FirstRace(race_type=data.get_race_type_by_name('HalfMarathon'), name='San Francisco Marathon', race_date=date(year=2017, month=7, day=23), target_time=target_time) me = FirstRunner(name='Daniel BenDavid', age=56, gender='m', email='*****@*****.**') p2 = FirstPlan(name='San Francisco half-marathon training plan', weekly_schedule=ws1, race=sf_half_marathon, runner=me) try: # positive p2.generate_workouts(data=data) file_name = 'cmp_plan_half_marathon.tcx' from_file = open('{}/{}'.format(Config.TEST_RESOURCE_DIR, file_name)) cmp_string = from_file.read() from_file.close() self.assertEqual(cmp_string, p2.tcx()) except ValueError as vex: self.fail(str(vex)) except TypeError as tex: self.fail(str(tex))
def main(): args = process_args() if 'text' not in args.output and 'tcx' not in args.output and \ 'json' not in args.output and 'html' not in args.output: raise ValueError( 'Unknown output formats (). Available text, tcx, html, and json'. format(args.output)) data = FirstData(json_path=Config.DATABASE_JSON) runner = FirstRunner(name=args.runner_name) target_time = FirstTime.from_string(string=args.target_time) if args.ref_race_type is not None: target_time = data.equivalent_time( time_from=target_time, race_index_from=data.race_type_index_by_name( name=args.ref_race_type), race_index_to=data.race_type_index_by_name(name=args.race_type)) if args.race_name is not None: race_name = args.race_name else: race_name = args.race_type race_date = datetime.datetime.strptime(args.race_date, '%m/%d/%Y').date() race = FirstRace(race_type=data.get_race_type_by_name(name=args.race_type), name=race_name, race_date=race_date, target_time=target_time) ws = get_keyrun_days(user_string=args.keyrun_days) plan = FirstPlan(name=args.race_name, weekly_schedule=ws, race=race, runner=runner) plan.generate_workouts(data=data) base_file_name = str(race_date) + race_name if 'text' in args.output: file_name = '{}/{}.{}'.format(Config.DOWNLOADS_DIR, base_file_name, 'txt') target = open(file_name, 'w') target.write(plan.details(level=3)) target.close() if 'tcx' in args.output: file_name = '{}/{}.{}'.format(Config.DOWNLOADS_DIR, base_file_name, 'tcx') target = open(file_name, 'w') target.write(plan.tcx()) target.close() if 'json' in args.output: file_name = '{}/{}.{}'.format(Config.DOWNLOADS_DIR, base_file_name, 'json') target = open(file_name, 'w') target.write(json.dumps(plan.to_json(args.length_unit))) target.close() if 'html' in args.output: file_name = '{}/{}.{}'.format(Config.DOWNLOADS_DIR, base_file_name, 'html') target = open(file_name, 'w') target.write(plan.to_html(args.length_unit)) target.close()
def test_from_instructions_new(self): rp = FirstPace.from_string('0:09:35 min per mile') ti = 50 wo_date = date(2017, 8, 21) data_file_path = expanduser( '~') + '/PycharmProjects/first/database/FIRSTregularPlans.xml' data = FirstData(data_file_path) instructions = '1 1 warmup#3x(1600m#200 m@RI)cooldown' FirstStepBase.reset_global_id() try: wo1 = FirstWorkout.from_instructions(instructions=instructions, wo_date=wo_date, data=data, time_index=ti, race_pace=rp) self.assertEqual('Week 1 Keyrun 1', wo1.name) self.assertEqual('2017-08-21', str(wo1.workout_date)) self.assertEqual('scheduled', wo1.status) self.assertEqual('warmup#3x(1600m#200 m@RI)cooldown', wo1.note) self.assertEqual(3, len(wo1.steps)) # Time steps step = wo1.steps[0] self.assertEqual(0, step.step_id) self.assertEqual('warmup', step.name) self.assertEqual('0:09:23 min per mile', str(step.pace)) self.assertEqual(None, step.distance) self.assertEqual('time', step.get_duration_type()) self.assertEqual('0:15:00', str(step.time)) self.assertEqual(15.0, step.total(what='time', unit='minute')) self.assertAlmostEquals(1.59858, step.total(what='distance', unit='mile'), 5) step = wo1.steps[2] self.assertEqual(4, step.step_id) self.assertEqual('cooldown', step.name) self.assertEqual('0:09:23 min per mile', str(step.pace)) self.assertEqual(None, step.distance) self.assertEqual('time', step.get_duration_type()) self.assertEqual('0:10:00', str(step.time)) self.assertEqual(600.0, step.total(what='time', unit='second')) self.assertAlmostEquals(1.06572, step.total(what='distance', unit='mile'), 5) # Repeat step step = wo1.steps[1] self.assertEqual(1, step.step_id) self.assertEqual('repeat X 3', step.name) self.assertEqual(3, step.repeat) self.assertEqual(2, len(step.steps)) substep = step.steps[0] self.assertEqual(2, substep.step_id) self.assertEqual('1600m', substep.name) self.assertEqual('0:07:18 min per mile', str(substep.pace)) self.assertEqual(None, substep.time) self.assertEqual('distance', substep.get_duration_type()) self.assertEqual('1600.0 m', str(substep.distance)) self.assertAlmostEquals(7.25762, substep.total(what='time', unit='minute'), 5) self.assertAlmostEquals( 0.99419, substep.total(what='distance', unit='mile'), 5) substep = step.steps[1] self.assertEqual(3, substep.step_id) self.assertEqual('200 m@RI', substep.name) self.assertEqual('0:09:23 min per mile', str(substep.pace)) self.assertEqual(None, substep.time) self.assertEqual('distance', substep.get_duration_type()) self.assertEqual('200.0 m', str(substep.distance)) self.assertAlmostEquals(1.16611, substep.total(what='time', unit='minute'), 5) self.assertAlmostEquals( 0.12427, substep.total(what='distance', unit='mile'), 5) # Repeat steps totals self.assertAlmostEquals(25.27117, step.total(what='time', unit='minute'), 5) self.assertAlmostEquals(3.35540, step.total(what='distance', unit='mile'), 5) # Workout totals self.assertAlmostEquals(50.27117, wo1.total(what='time', unit='minute'), 5) self.assertAlmostEquals(6.01970, wo1.total(what='distance', unit='mile'), 5) file_name = expanduser( '~/PycharmProjects/first/database/cmp_workout1.tcx') # to_file = open(file_name, 'w') # to_file.write(wo1.tcx()) # to_file.close() from_file = open(file_name) cmp_string = from_file.read() from_file.close() self.assertEqual(cmp_string, wo1.tcx()) except ValueError as ex: self.fail(str(ex)) instructions = '1 2 warmup#3x(1600m#4x(200 m@RI)800m)cooldown' try: wo2 = FirstWorkout.from_instructions(instructions=instructions, wo_date=wo_date, data=data, time_index=ti, race_pace=rp) self.assertEqual('Week 1 Keyrun 2', wo2.name) self.assertEqual('2017-08-21', str(wo2.workout_date)) self.assertEqual('scheduled', wo2.status) self.assertEqual('warmup#3x(1600m#4x(200 m@RI)800m)cooldown', wo2.note) self.assertEqual(3, len(wo2.steps)) # recursive repeat step step = wo2.steps[1] self.assertEqual(6, step.step_id) self.assertEqual('repeat X 3', step.name) self.assertEqual(3, step.repeat) self.assertEqual(3, len(step.steps)) substep = step.steps[1] self.assertEqual(8, substep.step_id) self.assertEqual('repeat X 4', substep.name) self.assertEqual(4, substep.repeat) self.assertEqual(1, len(substep.steps)) subsubstep = substep.steps[0] self.assertEqual(9, subsubstep.step_id) self.assertEqual('200 m@RI', subsubstep.name) self.assertEqual('0:09:23 min per mile', str(subsubstep.pace)) self.assertEqual(None, subsubstep.time) self.assertEqual('distance', subsubstep.get_duration_type()) self.assertEqual('200.0 m', str(subsubstep.distance)) self.assertAlmostEquals( 1.16611, subsubstep.total(what='time', unit='minute'), 5) self.assertAlmostEquals( 0.12427, subsubstep.total(what='distance', unit='mile'), 5) self.assertAlmostEquals(4.66443, substep.total(what='time', unit='minute'), 5) self.assertAlmostEquals( 0.4971, substep.total(what='distance', unit='mile'), 5) self.assertAlmostEquals(46.20516, step.total(what='time', unit='minute'), 5) self.assertAlmostEquals(5.96516, step.total(what='distance', unit='mile'), 5) self.assertAlmostEquals(71.20516, wo2.total(what='time', unit='minute'), 5) self.assertAlmostEquals(8.62946, wo2.total(what='distance', unit='mile'), 5) file_name = expanduser( '~/PycharmProjects/first/database/cmp_workout2.tcx') # to_file = open(file_name, 'w') # to_file.write(wo2.tcx()) # to_file.close() from_file = open(file_name) cmp_string = from_file.read() from_file.close() self.assertEqual(cmp_string, wo2.tcx()) except ValueError as ex: self.fail(str(ex)) instructions = '1 1 warmup#3x(1600m#200 m@RI#cooldown' try: # unbalanced parentheses dummy = FirstWorkout.from_instructions(instructions=instructions, wo_date=wo_date, data=data, time_index=ti, race_pace=rp) except ValueError as ex: self.assertEqual( 'FirstWorkout.__parse_steps - Unbalanced parentheses', str(ex))
def test_equivalent_time(self): data_file_path = expanduser( '~') + '/PycharmProjects/first/database/FIRSTregularPlans.xml' try: # good path data = FirstData(xml_path=data_file_path) self.assertEqual(4, len(data.race_types)) self.assertEqual(1, data.race_type_index_by_name('10K')) self.assertEqual(2, data.race_type_index_by_name('HalfMarathon')) self.assertEqual(91, len(data.race_times)) self.assertEqual(4, len(data.race_times[0])) from_time = FirstTime.from_string('0:20:13') self.assertEqual( '1:34:15', str( data.equivalent_time(time_from=from_time, race_index_from=0, race_index_to=2))) from_time = FirstTime.from_string('1:54:32') self.assertEqual( '4:01:39', str( data.equivalent_time(time_from=from_time, race_index_from=2, race_index_to=3))) except ValueError as vex: self.fail(str(vex)) except IOError as ioex: self.fail(str(ioex)) try: # bad race name index = data.race_type_index_by_name('lulu') self.fail('Should not get here with a bad race type name') except ValueError as ex: self.assertEqual( 'FirstSegment.race_type_index_by_name - Race type lulu not found', str(ex)) try: # time not found high from_time = FirstTime.from_string('4:49:59') equivalent_time = data.equivalent_time(time_from=from_time, race_index_from=2, race_index_to=0) self.fail('Should not get here with time not found') except ValueError as ex: self.assertEqual( 'FirstData.equivalent_time - time is longer than the highest database time', str(ex)) try: # time not found low from_time = FirstTime.from_string('0:49:59') equivalent_time = data.equivalent_time(time_from=from_time, race_index_from=2, race_index_to=0) self.fail('Should not get here with time not found') except ValueError as ex: self.assertEqual( 'FirstData.equivalent_time - time is shorter than the lowest database time', str(ex)) try: # bad time type equivalent_time = data.equivalent_time(time_from='abc', race_index_from=0, race_index_to=1) self.fail('Should not get here with bad time type') except TypeError as ex: self.assertEqual( 'FirstData.equivalent_time - time_from must be an instance of FirstTime', str(ex)) try: # bad index type equivalent_time = data.equivalent_time(time_from=from_time, race_index_from='abc', race_index_to=2) self.fail('Should not get here with bad index') except TypeError as ex: self.assertEqual( 'FirstData.equivalent_time - race_index_from must be an int', str(ex)) try: # bad index type equivalent_time = data.equivalent_time(time_from=from_time, race_index_from=0, race_index_to='abc') self.fail('Should not get here with bad index') except TypeError as ex: self.assertEqual( 'FirstData.equivalent_time - race_index_to must be an int', str(ex)) try: # index out of range equivalent_time = data.equivalent_time(time_from=from_time, race_index_from=-1, race_index_to=2) self.fail('Should not get here with bad index') except ValueError as ex: self.assertEqual( 'FirstData.equivalent_time - race index must be between 0 and 3', str(ex)) try: # index out of range equivalent_time = data.equivalent_time(time_from=from_time, race_index_from=4, race_index_to=2) self.fail('Should not get here with bad index') except ValueError as ex: self.assertEqual( 'FirstData.equivalent_time - race index must be between 0 and 3', str(ex)) try: # index out of range equivalent_time = data.equivalent_time(time_from=from_time, race_index_from=1, race_index_to=-2) self.fail('Should not get here with bad index') except ValueError as ex: self.assertEqual( 'FirstData.equivalent_time - race index must be between 0 and 3', str(ex)) try: # index out of range equivalent_time = data.equivalent_time(time_from=from_time, race_index_from=1, race_index_to=55) self.fail('Should not get here with bad index') except ValueError as ex: self.assertEqual( 'FirstData.equivalent_time - race index must be between 0 and 3', str(ex)) try: # bad database file bad_data = FirstData(xml_path='lulu') self.fail('Should not get here with bad file name') except IOError as ioex: self.assertEqual("[Errno 2] No such file or directory: 'lulu'", str(ioex))