Esempio n. 1
0
    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)
Esempio n. 2
0
    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)
Esempio n. 3
0
    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))
Esempio n. 4
0
    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))
Esempio n. 5
0
    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))
Esempio n. 6
0
    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))
Esempio n. 7
0
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()
Esempio n. 8
0
    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))
Esempio n. 9
0
    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))
Esempio n. 10
0
    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))
Esempio n. 11
0
    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))
Esempio n. 12
0
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()
Esempio n. 13
0
    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))
Esempio n. 14
0
    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))