Beispiel #1
0
    def test_from_string(self):

        try:
            time = FirstTime.from_string(string='2:03:23')
            self.assertEqual('2:03:23', str(time))
        except ValueError:
            self.fail('test_from_string should not fail for valid string')

        try:
            _ = FirstTime.from_string(string='abc')
            self.fail('FirstTime is expected to fail with "abc"')
        except ValueError as ex:
            self.assertEqual('(\'Unknown string format:\', \'abc\')', str(ex))

        try:
            time = FirstTime.from_string(string='3:45')
            self.assertEqual('3:45:00', str(time))
        except ValueError:
            self.fail('test_from_string should not fail for valid string')

        try:
            _ = FirstTime.from_string(string='3')
            self.fail('test_from_string should not pass for "3"')
        except ValueError as ex:
            self.assertEqual('unknown string format for "3"', str(ex))

        try:
            time = FirstTime.from_string(string='4/15/2015')
            self.assertEqual(
                'test_from_string should not pass for "4/15/2015"', time)
        except ValueError as ex:
            self.assertEqual('unknown string format for "4/15/2015"', str(ex))
Beispiel #2
0
    def test_add_workout(self):

        ws1 = [0, 2, 5]
        rt1 = FirstRaceType(name='Marathon', distance=FirstDistance.from_string('42.195 km'))
        rd1 = date(year=2017, month=7, day=29)
        r1 = FirstRace(name='SFM', race_type=rt1, race_date=rd1)
        rn1 = FirstRunner(name='DBD')
        p1 = FirstPlan(name='My first marathon training plan', weekly_schedule=ws1, race=r1, runner=rn1)

        t_warmup = FirstTime.from_string('0:15:00')
        p_warmup = FirstPace.from_string('0:10:00 min per mile')
        s_warmup = FirstStepBody(name='Warm up', pace=p_warmup, time=t_warmup)

        s_repeat = FirstStepRepeat(name='repeat X 8', repeat=8)
        d_interval = FirstDistance.from_string('400 m')
        p_fast = FirstPace.from_string('0:08:00 min per mile')
        s_fast = FirstStepBody(name='Fast', pace=p_fast, distance=d_interval)
        s_repeat.add_step(s_fast)
        s_slow = FirstStepBody(name='Rest', pace=p_warmup, distance=d_interval)
        s_repeat.add_step(s_slow)

        t_cooldown = FirstTime.from_string('0:10:00')
        s_cooldown = FirstStepBody(name='Cool down', pace=p_warmup, time=t_cooldown)

        wo = FirstWorkout(name='Week 1 Key-run 1', workout_date=date(year=2017, month=6, day=24))
        wo.add_step(step=s_warmup)
        wo.add_step(step=s_repeat)
        wo.add_step(step=s_cooldown)

        try:  # first workout
            p1.add_workout(workout=wo)
            cmp_string = ('Training Plan:\nName - "My first marathon training plan"\n' +
                          'Workout days: Mon, Wed, Sat\nRace:\n' +
                          '  Name - "SFM" of type Marathon - 42.195 km\nRunner:\n  Name - "DBD"\nWorkouts:\n' +
                          '  "Week 1 Key-run 1"\n    Sat 2017-06-24\n    scheduled\n' +
                          'Total 1 workouts\n')
            self.assertEqual(cmp_string, str(p1))

            file_name = 'cmp_plan2.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_plan2.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_plan2_km.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(output_unit='km'))
            file_name = 'cmp_plan2.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())
            file_name = 'cmp_plan2_km.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(output_unit='km'))
        except TypeError as ex:
            self.fail(str(ex))
Beispiel #3
0
    def test_from_string(self):

        try:
            t1 = FirstTime.from_string('2:03:23')
            self.assertEqual('2:03:23', str(t1))
        except ValueError:
            self.fail('test_from_string hould not fail for valid string')

        try:
            t2 = FirstTime.from_string('abc')
            self.fail('FirstTime is expected to fail with "abc"')
        except ValueError as ex:
            self.assertEqual('FirstTime.from_string - unknown string format - "abc"', str(ex))

        try:
            t3 = FirstTime.from_string('3:45')
            self.assertEqual('3:45:00', str(t3))
        except ValueError:
            self.fail('test_from_string should not fail for valid string')

        try:
            t4 = FirstTime.from_string('3')
            self.fail('test_from_string should not pass for "3"')
        except ValueError as ex:
            self.assertEqual('FirstTime.from_string - unknown string format for "3"', str(ex))

        try:
            t5 = FirstTime.from_string('4/15/2015')
            self.assertEqual('test_from_string should not pass for "4/15/2015"')
        except ValueError as ex:
            self.assertEqual('FirstTime.from_string - unknown string format for "4/15/2015"', str(ex))
Beispiel #4
0
    def increment(self, seconds: int) -> None:
        """
        Increment the pace by number of seconds - for instructions like 'RP+15'

        :param seconds:
        :type seconds: int
        """
        self.time = FirstTime(seconds=self.time.seconds + seconds)
Beispiel #5
0
    def __get_segments_json(self, json_segments: Dict) -> None:

        self.reference_race = json_segments['reference_race']
        index = 0
        for segment_type in json_segments['segment_types']:
            name = segment_type['name']
            type_str = segment_type['type']
            ref_pace = segment_type['ref_pace_name']
            if type_str == 'DISTANCE':
                distance = FirstDistance.from_string(
                    string=segment_type['distance'])
                self.segments.append(FirstSegment(name=name,
                                                  distance=distance))
            elif type_str == 'TIME':
                duration = FirstTime.from_string(string=segment_type['time'])
                self.segments.append(
                    FirstSegment(name=name,
                                 duration=duration,
                                 ref_pace_name=ref_pace))
            else:  # PACE
                self.segments.append(
                    FirstSegment(name=name, ref_pace_name=ref_pace))

            self.segments_lookup[name] = index
            index += 1

        pace_unit = json_segments['pace_unit']
        distance_unit = pace_unit.split()[-1]
        for line in json_segments['paces']:
            items = line.split()
            paces_list = [FirstTime.from_string(items[0])]
            index = 0
            for pace_str in items[1:]:
                time_str = '0:{}'.format(pace_str)
                ref_segment = self.segments[index]
                if ref_segment.get_type() == 'distance':
                    cur_time = FirstTime.from_string(time_str)
                    cur_distance = ref_segment.distance
                    paces_list.append(
                        FirstPace.from_time_distance(time=cur_time,
                                                     distance=cur_distance,
                                                     unit=distance_unit))
                elif ref_segment.get_type() == 'pace':
                    paces_list.append(
                        FirstPace.from_string('{} {}'.format(
                            time_str, pace_unit)))
                else:
                    raise ValueError(
                        'Duration segments have already a reference pace')

                index += 1
            self.segments_paces.append(paces_list)
Beispiel #6
0
    def to_time(self, distance: FirstDistance, unit: str) -> float:
        """
        How much time will take to run a given distance with this pace

        :param distance: the distance
        :type distance: FirstDistance
        :param unit: the desired unit of the result
        :type unit: str
        :return: the time value for this unit
        :rtype: float
        """
        factor = distance.convert_to(unit=self.length_unit)
        seconds = self.time.total_seconds() * factor
        result_time = FirstTime(seconds=round(seconds))
        return result_time.convert_to(unit=unit)
Beispiel #7
0
    def from_time_distance(cls,
                           time: FirstTime,
                           distance: FirstDistance,
                           unit: str = None):
        """
        Constructor: Initiate FirstPace from time/distance

        :param time:
        :type time: FirstTime
        :param distance:
        :type distance: FirstDistance
        :param unit: length unit
        :type unit: str
        :return: instance to FirstPace
        :rtype: FirstPace
        """
        if unit is None:
            unit = distance.unit
        factor = distance.convert_to(
            unit=unit)  # 400m with unit = mile will become ~0.25
        seconds = time.total_seconds(
        ) / factor  # 2 minutes for 400m will give ~(2*60)/0.25

        return cls(minutes=int(seconds // 60),
                   seconds=round(seconds % 60),
                   length_unit=unit)
Beispiel #8
0
    def test_to_string(self):

        try:
            t1 = FirstTime(hours=2, minutes=15, seconds=12)
            self.assertEqual('2:15:12', str(t1))
        except ValueError as ex:
            self.fail('Should not fail')

        try:
            t2 = FirstTime()
            self.assertEqual('0:00:00', str(t2))
        except ValueError:
            self.fail('Should not fail')

        try:
            t3 = FirstTime(minutes=-2, seconds=120)
            self.fail('FirstTime should not accept negative values')
        except ValueError as ex:
            self.assertEqual('FirstTime.__init__ - does not allow negative values', str(ex))
Beispiel #9
0
    def test_to_string(self):

        try:
            time = FirstTime(hours=2, minutes=15, seconds=12)
            self.assertEqual('2:15:12', str(time))
        except ValueError as ex:
            self.fail(str(ex))

        try:
            time = FirstTime()
            self.assertEqual('0:00:00', str(time))
        except ValueError:
            self.fail('Should not fail')

        try:
            _ = FirstTime(minutes=-2, seconds=120)
            self.fail('FirstTime should not accept negative values')
        except ValueError as ex:
            self.assertEqual('negative values are invalid', str(ex))
Beispiel #10
0
    def __init__(self, minutes=0, seconds=0, length_unit='mile'):

        """
        Constructor

        :param minutes:
        :type minutes: int
        :param seconds:
        :type seconds: int
        :param length_unit:
        :type length_unit: str
        :return: instance of FirstPace
        :rtype: FirstPace
        """
        where_am_i = 'FirstPace.__init__'
        if not FirstDistance.is_valid_unit(length_unit):
            raise ValueError(where_am_i + ' - "%1s" is not a valid length unit' % length_unit)
        self.time = FirstTime(minutes=minutes, seconds=seconds)
        self.length_unit = length_unit
Beispiel #11
0
    def __init__(self,
                 minutes: int = 0,
                 seconds: int = 0,
                 length_unit: str = 'mile'):
        """
        Constructor

        :param minutes:
        :type minutes: int
        :param seconds:
        :type seconds: int
        :param length_unit:
        :type length_unit: str
        :return: instance of FirstPace
        :rtype: FirstPace
        """
        if not FirstDistance.is_valid_unit(unit=length_unit):
            raise ValueError(
                '"{}" is not a valid length unit'.format(length_unit))
        self.time = FirstTime(minutes=minutes, seconds=seconds)
        self.length_unit = length_unit
Beispiel #12
0
    def __parse_rows(self, rows):

        for row in rows:
            times = []
            for col in row[0]:
                seconds = int(col[0].text)
                minutes = int(col[1].text)
                hours = int(col[2].text)
                times.append(
                    FirstTime(seconds=seconds, minutes=minutes, hours=hours))

            self.race_times.append(times)
Beispiel #13
0
 def to_distance(self, time: FirstTime, unit: str) -> float:
     """
     How far you run given duration with this pace
     
     :param time: the duration
     :type time: FirstTime
     :param unit: the desired unit of the result
     :type unit: str
     :return: the distance value for this unit
     :rtype: float
     """
     factor = time.total_seconds() / self.time.total_seconds()
     result_distance = FirstDistance(distance=factor, unit=self.length_unit)
     return result_distance.convert_to(unit=unit)
Beispiel #14
0
    def __init__(self, json_path: str):
        """
        Constructor

        :return: instance of FirstData
        :rtype: FirstData
        """

        self.race_types = []
        self.race_times = []
        self.segments = []
        self.segments_lookup = {}
        self.reference_race = None
        self.segments_paces = []
        self.plan_instructions = []

        if json_path is not None:
            with open(json_path, 'r') as fd:
                data_dict = json.load(fd)

                self.name = data_dict['name']
                self.note = data_dict['note']

                self.race_types = [
                    FirstRaceType(name=race['name'],
                                  distance=FirstDistance.from_string(
                                      race['distance']))
                    for race in data_dict['races']
                ]
                self.race_times = [[
                    FirstTime.from_string(string=time) for time in times
                ] for times in data_dict['equivalent_times']]
                self.__get_segments_json(json_segments=data_dict['segments'])
                self.plan_instructions = [
                    PlanInstructions(
                        name=plan['name'],
                        race_name=plan['race_name'],
                        instructions=[line for line in plan['instructions']])
                    for plan in data_dict['workout_instructions']
                ]
        else:
            raise ValueError('json_path should point to an existing file')
Beispiel #15
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()
Beispiel #16
0
    def from_string(cls, str_input):

        """
        Constructor: Instantiate FirstPace from a string input
        
        :param str_input: format - '0:MM:SS per unit'
        :type str_input: str
        :return: instance of FirstPace
        :rtype: FirstPace
        """
        where_am_i = 'FirstPace.from_string'
        tokens = str_input.split()

        p_time = FirstTime.from_string(tokens[0])  # pass the exception on
        length_unit = tokens[-1]

        if not FirstDistance.is_valid_unit(unit=tokens[-1]):
            raise ValueError(where_am_i + ' - "%1s" is not a valid length unit' % length_unit)

        return cls(minutes=p_time.seconds//60, seconds=p_time.seconds % 60, length_unit=length_unit)
Beispiel #17
0
    def test_race(self):

        rt1 = FirstRaceType(name='5K',
                            distance=FirstDistance.from_string('5.0 km'))
        rd1 = date(year=2017, month=7, day=29)
        tt1 = FirstTime.from_string(string='0:25:30')
        tt2 = FirstTime.from_string(string='0:24:34')

        try:  # positive
            race = FirstRace(race_type=rt1,
                             race_date=rd1,
                             name='Martial Cottle Park 5K',
                             target_time=tt1)
            cmp_string = ('Martial Cottle Park 5K of type ' + str(rt1) + '\n' +
                          'On ' + str(rd1) + '\n' + 'Target time - ' +
                          str(tt1) + '\n' + 'Status - scheduled\n')
            self.assertEqual(cmp_string, str(race))
            cmp_json = {
                'Name': 'Martial Cottle Park 5K',
                'race_date': '2017-07-29',
                'race_type': {
                    'distance': {
                        'distance': 5.0,
                        'unit': 'km'
                    },
                    'name': '5K'
                },
                'status': 'scheduled',
                'target_time': {
                    'seconds': 1530,
                    'time': '0:25:30'
                }
            }
            self.assertEqual(cmp_json, race.to_json())
            cmp_json = {
                'Name': 'Martial Cottle Park 5K',
                'race_date': '2017-07-29',
                'race_type': {
                    'distance': {
                        'distance': 3.10686,
                        'unit': 'mile'
                    },
                    'name': '5K'
                },
                'status': 'scheduled',
                'target_time': {
                    'seconds': 1530,
                    'time': '0:25:30'
                }
            }
            FirstUtils.assert_deep_almost_equal(
                self, cmp_json, race.to_json(output_unit='mile'), 5)
            cmp_html = (
                '<div>\n' + '  <h2>Race:</h2>\n' +
                '  <table style="border-spacing: 15px 0">\n' +
                '    <tbody>\n' + '      <tr>\n' + '        <td>Name:</td>\n' +
                '        <td><b>Martial Cottle Park 5K</b></td>\n' +
                '      </tr>\n' + '      <tr>\n' + '        <td>Type:</td>\n' +
                '        <td><b>5K</b></td>\n' + '      </tr>\n' +
                '      <tr>\n' + '        <td>Distance:</td>\n' +
                '        <td><b>5.000 km</b></td>\n' + '      </tr>\n' +
                '      <tr>\n' + '        <td>Date:</td>\n' +
                '        <td><b>2017-07-29</b></td>\n' + '      </tr>\n' +
                '      <tr>\n' + '        <td>Target time:</td>\n' +
                '        <td><b>0:25:30</b></td>\n' + '      </tr>\n' +
                '      <tr>\n' + '        <td>Status:</td>\n' +
                '        <td><b>scheduled</b></td>\n' + '      </tr>\n' +
                '    </tbody>\n' + '  </table>\n' + '</div>')
            self.assertEqual(cmp_html, race.to_html().indented_str())
            cmp_html = (
                '<div>\n' + '  <h2>Race:</h2>\n' +
                '  <table style="border-spacing: 15px 0">\n' +
                '    <tbody>\n' + '      <tr>\n' + '        <td>Name:</td>\n' +
                '        <td><b>Martial Cottle Park 5K</b></td>\n' +
                '      </tr>\n' + '      <tr>\n' + '        <td>Type:</td>\n' +
                '        <td><b>5K</b></td>\n' + '      </tr>\n' +
                '      <tr>\n' + '        <td>Distance:</td>\n' +
                '        <td><b>3.107 mile</b></td>\n' + '      </tr>\n' +
                '      <tr>\n' + '        <td>Date:</td>\n' +
                '        <td><b>2017-07-29</b></td>\n' + '      </tr>\n' +
                '      <tr>\n' + '        <td>Target time:</td>\n' +
                '        <td><b>0:25:30</b></td>\n' + '      </tr>\n' +
                '      <tr>\n' + '        <td>Status:</td>\n' +
                '        <td><b>scheduled</b></td>\n' + '      </tr>\n' +
                '    </tbody>\n' + '  </table>\n' + '</div>')
            self.assertEqual(cmp_html,
                             race.to_html(output_unit='mile').indented_str())
        except TypeError as tex:
            self.fail(str(tex))
        except ValueError as vex:
            self.fail(str(vex))

        try:  # add actual time
            race.set_status(status='done')
            race.set_actual_time(a_time=tt2)
            cmp_string = ('Martial Cottle Park 5K of type ' + str(rt1) + '\n' +
                          'On ' + str(rd1) + '\n' + 'Target time - ' +
                          str(tt1) + '\n' + 'Status - done\n' +
                          'Actual time - ' + str(tt2) + '\n')
            self.assertEqual(cmp_string, str(race))
            cmp_json = {
                'Name': 'Martial Cottle Park 5K',
                'actual_time': '0:24:34',
                'race_date': '2017-07-29',
                'race_type': {
                    'distance': {
                        'distance': 5.0,
                        'unit': 'km'
                    },
                    'name': '5K'
                },
                'status': 'done',
                'target_time': {
                    'seconds': 1530,
                    'time': '0:25:30'
                }
            }
            self.assertEqual(cmp_json, race.to_json())
            cmp_json = {
                'Name': 'Martial Cottle Park 5K',
                'actual_time': '0:24:34',
                'race_date': '2017-07-29',
                'race_type': {
                    'distance': {
                        'distance': 3.10686,
                        'unit': 'mile'
                    },
                    'name': '5K'
                },
                'status': 'done',
                'target_time': {
                    'seconds': 1530,
                    'time': '0:25:30'
                }
            }
            FirstUtils.assert_deep_almost_equal(
                self, cmp_json, race.to_json(output_unit='mile'), 5)
        except TypeError as tex:
            self.fail(str(tex))
        except ValueError as vex:
            self.fail(str(vex))

        try:  # remove target time
            race.set_target_time()
            cmp_string = ('Martial Cottle Park 5K of type ' + str(rt1) + '\n' +
                          'On ' + str(rd1) + '\n' + 'Status - done\n' +
                          'Actual time - ' + str(tt2) + '\n')
            self.assertEqual(cmp_string, str(race))
        except TypeError as tex:
            self.fail(str(tex))
        except ValueError as vex:
            self.fail(str(vex))

        try:  # negative
            race.set_status('lulu')
            self.fail('Should not get here with a bad status')
        except ValueError as ex:
            self.assertEqual("Status not in ['scheduled', 'done', 'skipped']",
                             str(ex))
Beispiel #18
0
    def test_repeat(self):

        FirstStepBase.reset_global_id()

        p1 = FirstPace.from_string('0:10:00 min per mile')
        d1 = FirstDistance.from_string('3 mile')
        t1 = FirstTime.from_string('0:15:00')

        try:
            name = '3 X (3 mile @ 10 min per mile + 15 minutes @ 19 min per mile)'
            s1 = FirstStepRepeat(name=name, repeat=3)
            self.assertEqual(
                'Step: "' + name + '"  id = 0\ntype - repeat  repeat - 3\n',
                str(s1))
            self.assertAlmostEquals(0.0, s1.total(unit='mile'), 5)
            self.assertAlmostEquals(0.0, s1.total(what='time', unit='minute'))
            tcx_string = (
                '<Step xsi:type="Repeat_t">\n' + '  <StepId>0</StepId>\n' +
                '  <Name>3 X (3 mile @ 10 min per mile + 15 minutes @ 19 min per mile)</Name>\n'
                + '  <Repetitions>3</Repetitions>\n' + '</Step>\n')
            self.assertEqual(tcx_string, s1.tcx())
        except TypeError as tex:
            self.fail(str(tex))
        except ValueError as vex:
            self.fail(str(vex))

        try:
            s2 = FirstStepBody(name='3 mile @ 10 min per mile',
                               pace=p1,
                               distance=d1)
            s3 = FirstStepBody(name='15 minutes @ 19 min per mile',
                               pace=p1,
                               time=t1)
            s1.add_step(s2)
            s1.add_step(s3)
            short = 'Step: "' + name + '"  id = 0\ntype - repeat  repeat - 3\n'
            self.assertEqual(short, str(s1))
            detail = 'Step: "3 X (3 mile @ 10 min per mile + 15 minutes @ 19 min per mile)"\n' +\
                     '  Step: "3 mile @ 10 min per mile"\n' +\
                     '    3.0 mile  at  0:10:00 min per mile\n' +\
                     '  Step: "15 minutes @ 19 min per mile"\n' +\
                     '    0:15:00  at  0:10:00 min per mile\n'
            self.assertEqual(detail, s1.details())
            self.assertAlmostEquals(13.5, s1.total(unit='mile'), 5)
            self.assertAlmostEquals(135.0, s1.total(what='time',
                                                    unit='minute'))
            tcx_string = (
                '  <Step xsi:type="Repeat_t">\n' + '    <StepId>0</StepId>\n' +
                '    <Name>3 X (3 mile @ 10 min per mile + 15 minutes @ 19 min per mile)</Name>\n'
                + '    <Repetitions>3</Repetitions>\n' +
                '    <Child xsi:type="Step_t">\n' +
                '      <StepId>1</StepId>\n' +
                '      <Name>3 mile @ 10 min per mile</Name>\n' +
                '      <Duration xsi:type="Distance_t">\n' +
                '        <Meters>4828</Meters>\n' + '      </Duration>\n' +
                '      <Intensity>Active</Intensity>\n' +
                '      <Target xsi:type="Speed_t">\n' +
                '        <SpeedZone xsi:type="CustomSpeedZone_t">\n' +
                '        <LowInMetersPerSecond>2.6382689</LowInMetersPerSecond>\n'
                +
                '        <HighInMetersPerSecond>2.7277017</HighInMetersPerSecond>\n'
                + '      </SpeedZone>\n' + '      </Target>\n' +
                '    </Child>\n' + '    <Child xsi:type="Step_t">\n' +
                '      <StepId>2</StepId>\n' +
                '      <Name>15 minutes @ 19 min per mile</Name>\n' +
                '      <Duration xsi:type="Time_t">\n' +
                '        <Seconds>900</Seconds>\n' + '      </Duration>\n' +
                '      <Intensity>Active</Intensity>\n' +
                '      <Target xsi:type="Speed_t">\n' +
                '        <SpeedZone xsi:type="CustomSpeedZone_t">\n' +
                '        <LowInMetersPerSecond>2.6382689</LowInMetersPerSecond>\n'
                +
                '        <HighInMetersPerSecond>2.7277017</HighInMetersPerSecond>\n'
                + '      </SpeedZone>\n' + '      </Target>\n' +
                '    </Child>\n'
                '  </Step>\n')
            self.assertEqual(tcx_string, s1.tcx(indent='  ', delta_seconds=10))
        except TypeError as tex:
            self.fail(str(tex))
        except ValueError as vex:
            self.fail(str(vex))

        try:  # bad repeat type
            dummy = FirstStepRepeat(name='bla', repeat='3')
            self.fail('Should not get here with bad repeat type')
        except TypeError as ex:
            self.assertEqual(
                'FirstStepRepeat.__init__ - repeat must be an integer',
                str(ex))

        try:  # negative repeat
            dummy = FirstStepRepeat(name='bla', repeat=-3)
            self.fail('Should not get here with negative repeat value')
        except ValueError as ex:
            self.assertEqual(
                'FirstStepRepeat.__init__ - repeat must be greater than 0',
                str(ex))

        try:  # bad child step type
            s1.add_step('bad step')
            self.fail('Should not get here with bad step type')
        except TypeError as ex:
            self.assertEqual(
                'FirstStepRepeat.add_step - step must be an instance of FirstStepBase',
                str(ex))
Beispiel #19
0
    def test_body(self):

        FirstStepBase.reset_global_id()

        p1 = FirstPace.from_string('0:10:00 min per mile')
        d1 = FirstDistance.from_string('3 mile')
        t1 = FirstTime.from_string('0:15:00')

        try:  # distance step
            s1 = FirstStepBody(name='3 miles @ 10 minutes per mile',
                               pace=p1,
                               distance=d1)
            cmp_string = 'Step: "3 miles @ 10 minutes per mile"  id = 0\n' + \
                         'type - body  pace - 0:10:00 min per mile\nDistance - 3.0 mile\n'
            self.assertEqual(cmp_string, str(s1))
            self.assertAlmostEquals(3.0, s1.total(unit='mile'), 5)
            self.assertAlmostEquals(4.828032, s1.total(unit='km'), 5)
            self.assertAlmostEquals(30.0, s1.total(what='time', unit='minute'),
                                    5)
            self.assertAlmostEquals(0.5, s1.total(what='time', unit='hour'), 5)
            tcx_string = (
                '<Step xsi:type="Step_t">\n' + '  <StepId>0</StepId>\n' +
                '  <Name>3 miles @ 10 minutes per mile</Name>\n' +
                '  <Duration xsi:type="Distance_t">\n' +
                '    <Meters>4828</Meters>\n' + '  </Duration>\n' +
                '  <Intensity>Active</Intensity>\n' +
                '  <Target xsi:type="Speed_t">\n' +
                '    <SpeedZone xsi:type="CustomSpeedZone_t">\n' +
                '    <LowInMetersPerSecond>2.6600727</LowInMetersPerSecond>\n'
                +
                '    <HighInMetersPerSecond>2.7047798</HighInMetersPerSecond>\n'
                + '  </SpeedZone>\n' + '  </Target>\n' + '</Step>\n')
            self.assertEqual(tcx_string, s1.tcx())  # no indent
            tcx_string = (
                '    <Step xsi:type="Step_t">\n' +
                '      <StepId>0</StepId>\n' +
                '      <Name>3 miles @ 10 minutes per mile</Name>\n' +
                '      <Duration xsi:type="Distance_t">\n' +
                '        <Meters>4828</Meters>\n' + '      </Duration>\n' +
                '      <Intensity>Active</Intensity>\n' +
                '      <Target xsi:type="Speed_t">\n' +
                '        <SpeedZone xsi:type="CustomSpeedZone_t">\n' +
                '        <LowInMetersPerSecond>2.6688955</LowInMetersPerSecond>\n'
                +
                '        <HighInMetersPerSecond>2.6957186</HighInMetersPerSecond>\n'
                + '      </SpeedZone>\n' + '      </Target>\n' +
                '    </Step>\n')
            self.assertEqual(tcx_string,
                             s1.tcx(indent='    ',
                                    delta_seconds=3))  # with indent
        except TypeError as tex:
            self.fail(str(tex))
        except ValueError as vex:
            self.fail(str(vex))

        try:  # time step
            s1 = FirstStepBody(name='15 minutes @ 10 minutes per mile',
                               pace=p1,
                               time=t1)
            cmp_string = 'Step: "15 minutes @ 10 minutes per mile"  id = 1\n' + \
                         'type - body  pace - 0:10:00 min per mile\nTime - 0:15:00\n'
            self.assertEqual(cmp_string, str(s1))
            self.assertAlmostEquals(15.0, s1.total(what='time', unit='minute'),
                                    5)
            self.assertAlmostEquals(7920.0, s1.total(unit='ft'))
            tcx_string = (
                '<Step xsi:type="Step_t">\n' + '  <StepId>1</StepId>\n' +
                '  <Name>15 minutes @ 10 minutes per mile</Name>\n' +
                '  <Duration xsi:type="Time_t">\n' +
                '    <Seconds>900</Seconds>\n' + '  </Duration>\n' +
                '  <Intensity>Active</Intensity>\n' +
                '  <Target xsi:type="Speed_t">\n' +
                '    <SpeedZone xsi:type="CustomSpeedZone_t">\n' +
                '    <LowInMetersPerSecond>2.6600727</LowInMetersPerSecond>\n'
                +
                '    <HighInMetersPerSecond>2.7047798</HighInMetersPerSecond>\n'
                + '  </SpeedZone>\n' + '  </Target>\n' + '</Step>\n')
            self.assertEqual(tcx_string, s1.tcx())
        except TypeError as tex:
            self.fail(str(tex))
        except ValueError as vex:
            self.fail(str(vex))

        try:  # bad name type
            dummy = FirstStepBody(name=123, pace=p1, time=t1)
            self.fail('Should not get here with bad name')
        except TypeError as ex:
            self.assertEqual('FirstStepBase.__init__ - name must be a string',
                             str(ex))

        try:  # bad pace
            dummy = FirstStepBody(name='dummy', pace='bad pace type', time=t1)
            self.fail('Should not get here with bad pace')
        except TypeError as ex:
            self.assertEqual(
                'FirstStepBody.__init__ - pace must be an instance of FirstPace',
                str(ex))

        try:  # no distance and no time
            dummy = FirstStepBody(name='dummy', pace=p1)
            self.fail('Should not get here with neither distance nor duration')
        except ValueError as ex:
            self.assertEqual(
                'FirstStepBody.__init__ - Either distance or time must have a value',
                str(ex))

        try:  # bad distance
            dummy = FirstStepBody(name='dummy', pace=p1, distance=123.45)
            self.fail('Should not get here with bad distance')
        except TypeError as ex:
            self.assertEqual(
                'FirstStepBody.__init__ - distance must be an instance of FirstDistance',
                str(ex))

        try:  # bad time
            dummy = FirstStepBody(name='dummy', pace=p1, time=987.65)
            self.fail('Should not get here with bad time')
        except TypeError as ex:
            self.assertEqual(
                'FirstStepBody.__init__ - time must be an instance of FirstTime',
                str(ex))

        try:  # both distance and time
            dummy = FirstStepBody(name='dummy', pace=p1, distance=d1, time=t1)
            self.fail('Should not get here with both distance and time')
        except ValueError as ex:
            self.assertEqual(
                'FirstStepBody.__init__ - cannot set both distance and duration in the same step',
                str(ex))
Beispiel #20
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))
Beispiel #21
0
    def __parse_segments(self, segment_def, ref_race, pace_unit,
                         segment_paces):

        where_am_i = 'FirstData.__parse_segments'
        index = 0
        for segment in segment_def:
            name = segment[0].text
            segment_type = segment[1].text
            distance = None
            duration = None
            ref_pace_name = None
            if segment_type == 'DISTANCE':
                distance = FirstDistance(distance=float(segment[2][0].text),
                                         unit=segment[2][1].text)
            elif segment_type == 'TIME':
                duration = FirstTime(seconds=int(segment[2][0].text),
                                     minutes=int(segment[2][1].text),
                                     hours=int(segment[2][2].text))
                ref_pace_name = segment[3].text
            else:  # PACE
                ref_pace_name = segment[2].text

            self.segments.append(
                FirstSegment(name=name,
                             distance=distance,
                             duration=duration,
                             ref_pace_name=ref_pace_name))
            self.segments_lookup[name] = index
            index += 1

        self.reference_race = ref_race.text
        dist_unit = pace_unit.text.split()[-1]

        for line in segment_paces:
            paces_list = []
            first = True
            index = 0
            for value in line.text.split():
                if first:
                    paces_list.append(FirstTime.from_string(value))
                    first = False
                else:
                    # why incrementing index makes ref_segment.get_type undefined?
                    time_string = '0:' + value
                    ref_segment = self.segments[index]
                    if ref_segment.get_type() == 'distance':
                        cur_time = FirstTime.from_string(time_string)
                        cur_dist = ref_segment.distance
                        paces_list.append(
                            FirstPace.from_time_distance(time=cur_time,
                                                         distance=cur_dist,
                                                         unit=dist_unit))
                    elif ref_segment.get_type() == 'pace':
                        paces_list.append(
                            FirstPace.from_string(time_string + ' ' +
                                                  pace_unit.text))
                    else:
                        raise ValueError(
                            where_am_i +
                            ' - Duration segments have already a reference pace'
                        )

                    index = index + 1

            self.segments_paces.append(paces_list)
Beispiel #22
0
    def test_to_string(self):

        rt1 = FirstRaceType(name='5K', distance=5.0, unit='km')
        rd1 = date(year=2017, month=7, day=29)
        tt1 = FirstTime.from_string('0:25:30')
        tt2 = FirstTime.from_string('0:24:34')

        try:  # positive
            r1 = FirstRace(race_type=rt1, race_date=rd1, name='Martial Cottle Park 5K', target_time=tt1)
            cmp_string = ('Martial Cottle Park 5K of type ' + str(rt1) + '\n' +
                          'On ' + str(rd1) + '\n' + 'Target time - ' + str(tt1) + '\n' + 'Status - scheduled\n')
            self.assertEqual(cmp_string, str(r1))
        except TypeError as tex:
            self.fail(str(tex))
        except ValueError as vex:
            self.fail(str(vex))

        try:  # add actual time
            r1.set_status('done')
            r1.set_actual_time(tt2)
            cmp_string = ('Martial Cottle Park 5K of type ' + str(rt1) + '\n' +
                          'On ' + str(rd1) + '\n' + 'Target time - ' + str(tt1) + '\n' + 'Status - done\n' +
                          'Actual time - ' + str(tt2) + '\n')
            self.assertEqual(cmp_string, str(r1))
        except TypeError as tex:
            self.fail(str(tex))
        except ValueError as vex:
            self.fail(str(vex))

        try:  # remove target time
            r1.set_target_time()
            cmp_string = ('Martial Cottle Park 5K of type ' + str(rt1) + '\n' +
                          'On ' + str(rd1) + '\n' + 'Status - done\n' +
                          'Actual time - ' + str(tt2) + '\n')
            self.assertEqual(cmp_string, str(r1))
        except TypeError as tex:
            self.fail(str(tex))
        except ValueError as vex:
            self.fail(str(vex))

        try:  # negative
            r1.set_target_time(1243)
            self.fail('Should not get here with bad target time')
        except TypeError as ex:
            self.assertEqual('FirstRace.set_target_time - a_time must be an instance of FirstTime', str(ex))

        try:  # negative
            r1.set_actual_time(1243)
            self.fail('Should not get here with bad actual time')
        except TypeError as ex:
            self.assertEqual('FirstRace.set_actual_time - a_time must be an instance of FirstTime', str(ex))

        try:  # negative
            r1.set_status(1234)
            self.fail('Should not get here with bad status')
        except TypeError as ex:
            self.assertEqual('FirstRace.set_status - status must be a string', str(ex))

        try:  # negative
            r1.set_status('lulu')
            self.fail('Should not get here with a bad status')
        except ValueError as ex:
            self.assertEqual("FirstRace.set_status - Status not in ['scheduled', 'done', 'skipped']", str(ex))
Beispiel #23
0
    def test_repeat(self):

        FirstStepBase.reset_global_id()

        pace = FirstPace.from_string(str_input='0:10:00 min per mile')
        distance = FirstDistance.from_string(string='3 mile')
        time = FirstTime.from_string(string='0:15:00')

        try:
            name = '3 X (3 mile @ 10 min per mile + 15 minutes @ 19 min per mile)'
            step_r = FirstStepRepeat(name=name, repeat=3)
            self.assertEqual(
                'Step: "' + name + '"  id = 0\ntype - repeat  repeat - 3\n',
                str(step_r))
            self.assertAlmostEqual(0.0, step_r.total(unit='mile'), 5)
            self.assertAlmostEqual(0.0, step_r.total(what='time',
                                                     unit='minute'))
            tcx_string = (
                '<Step xsi:type="Repeat_t">\n' + '  <StepId>0</StepId>\n' +
                '  <Name>3 X (3 mile @ 10 min per mile + 15 minutes @ 19 min per mile)</Name>\n'
                + '  <Repetitions>3</Repetitions>\n' + '</Step>')
            self.assertEqual(tcx_string, step_r.tcx().indented_str())
        except TypeError as tex:
            self.fail(str(tex))
        except ValueError as vex:
            self.fail(str(vex))

        try:
            step_b1 = FirstStepBody(name='3 mile @ 10 min per mile',
                                    pace=pace,
                                    distance=distance)
            step_b2 = FirstStepBody(name='15 minutes @ 19 min per mile',
                                    pace=pace,
                                    time=time)
            step_r.add_step(step_b1)
            step_r.add_step(step_b2)
            short = 'Step: "' + name + '"  id = 0\ntype - repeat  repeat - 3\n'
            self.assertEqual(short, str(step_r))
            detail = 'Step: "3 X (3 mile @ 10 min per mile + 15 minutes @ 19 min per mile)"\n' + \
                     '  Step: "3 mile @ 10 min per mile"\n' + \
                     '    3.0 mile  at  0:10:00 min per mile\n' + \
                     '  Step: "15 minutes @ 19 min per mile"\n' + \
                     '    0:15:00  at  0:10:00 min per mile\n'
            self.assertEqual(detail, step_r.details())
            self.assertAlmostEqual(13.5, step_r.total(unit='mile'), 5)
            self.assertAlmostEqual(135.0,
                                   step_r.total(what='time', unit='minute'))
            tcx_string = (
                '<Step xsi:type="Repeat_t">\n' + '  <StepId>0</StepId>\n' +
                '  <Name>3 X (3 mile @ 10 min per mile + 15 minutes @ 19 min per mile)</Name>\n'
                + '  <Repetitions>3</Repetitions>\n' +
                '  <Child xsi:type="Step_t">\n' + '    <StepId>1</StepId>\n' +
                '    <Name>3 mile @ 10 min per mile</Name>\n' +
                '    <Duration xsi:type="Distance_t">\n' +
                '      <Meters>4828</Meters>\n' + '    </Duration>\n' +
                '    <Intensity>Active</Intensity>\n' +
                '    <Target xsi:type="Speed_t">\n' +
                '      <SpeedZone xsi:type="CustomSpeedZone_t">\n' +
                '        <LowInMetersPerSecond>2.6382689</LowInMetersPerSecond>\n'
                +
                '        <HighInMetersPerSecond>2.7277017</HighInMetersPerSecond>\n'
                + '      </SpeedZone>\n' + '    </Target>\n' + '  </Child>\n' +
                '  <Child xsi:type="Step_t">\n' + '    <StepId>2</StepId>\n' +
                '    <Name>15 minutes @ 19 min per mile</Name>\n' +
                '    <Duration xsi:type="Time_t">\n' +
                '      <Seconds>900</Seconds>\n' + '    </Duration>\n' +
                '    <Intensity>Active</Intensity>\n' +
                '    <Target xsi:type="Speed_t">\n' +
                '      <SpeedZone xsi:type="CustomSpeedZone_t">\n' +
                '        <LowInMetersPerSecond>2.6382689</LowInMetersPerSecond>\n'
                +
                '        <HighInMetersPerSecond>2.7277017</HighInMetersPerSecond>\n'
                + '      </SpeedZone>\n' + '    </Target>\n' + '  </Child>\n'
                '</Step>')
            self.assertEqual(tcx_string,
                             step_r.tcx(delta_seconds=10).indented_str())
            cmp_json = {
                'name':
                '3 X (3 mile @ 10 min per mile + 15 minutes @ 19 min per mile)',
                'repeat':
                3,
                'steps': [{
                    'distance': {
                        'distance': 3.0,
                        'unit': 'mile'
                    },
                    'name': '3 mile @ 10 min per mile',
                    'pace': {
                        'length_unit': 'mile',
                        'pace': '0:10:00 min per mile',
                        'time': {
                            'seconds': 600,
                            'time': '0:10:00'
                        }
                    }
                }, {
                    'name': '15 minutes @ 19 min per mile',
                    'pace': {
                        'length_unit': 'mile',
                        'pace': '0:10:00 min per mile',
                        'time': {
                            'seconds': 600,
                            'time': '0:10:00'
                        }
                    },
                    'time': {
                        'seconds': 900,
                        'time': '0:15:00'
                    }
                }]
            }
            self.assertEqual(cmp_json, step_r.to_json())
            cmp_json = {
                'name':
                '3 X (3 mile @ 10 min per mile + 15 minutes @ 19 min per mile)',
                'repeat':
                3,
                'steps': [{
                    'distance': {
                        'distance': 4.828032,
                        'unit': 'km'
                    },
                    'name': '3 mile @ 10 min per mile',
                    'pace': {
                        'length_unit': 'km',
                        'pace': '0:06:13 min per km',
                        'time': {
                            'seconds': 373,
                            'time': '0:06:13'
                        }
                    }
                }, {
                    'name': '15 minutes @ 19 min per mile',
                    'pace': {
                        'length_unit': 'km',
                        'pace': '0:06:13 min per km',
                        'time': {
                            'seconds': 373,
                            'time': '0:06:13'
                        }
                    },
                    'time': {
                        'seconds': 900,
                        'time': '0:15:00'
                    }
                }]
            }
            self.assertEqual(cmp_json, step_r.to_json(output_unit='km'))
            cmp_html = (
                '<div style="margin-left: 20px">\n' + '  <p>\n' +
                '    Repeat 3 times:\n' + '  </p>\n' +
                '  <div style="margin-left: 20px">\n' + '    <p>\n' +
                '      3 mile @ 10 min per mile - 3.000 mile at 0:10:00 min per mile\n'
                + '    </p>\n' + '  </div>\n' +
                '  <div style="margin-left: 20px">\n' + '    <p>\n' +
                '      15 minutes @ 19 min per mile - 0:15:00 at 0:10:00 min per mile\n'
                + '    </p>\n' + '  </div>\n' + '</div>')
            self.assertEqual(cmp_html, step_r.to_html().indented_str())
            cmp_html = (
                '<div style="margin-left: 20px">\n' + '  <p>\n' +
                '    Repeat 3 times:\n' + '  </p>\n' +
                '  <div style="margin-left: 20px">\n' + '    <p>\n' +
                '      3 mile @ 10 min per mile - 4.828 km at 0:06:13 min per km\n'
                + '    </p>\n' + '  </div>\n' +
                '  <div style="margin-left: 20px">\n' + '    <p>\n' +
                '      15 minutes @ 19 min per mile - 0:15:00 at 0:06:13 min per km\n'
                + '    </p>\n' + '  </div>\n' + '</div>')
            self.assertEqual(cmp_html,
                             step_r.to_html(output_unit='km').indented_str())
        except TypeError as tex:
            self.fail(str(tex))
        except ValueError as vex:
            self.fail(str(vex))

        try:  # negative repeat
            _ = FirstStepRepeat(name='bla', repeat=-3)
            self.fail('Should not get here with negative repeat value')
        except ValueError as ex:
            self.assertEqual('repeat must be greater than 0', str(ex))
Beispiel #24
0
    def test_body(self):

        FirstStepBase.reset_global_id()

        pace = FirstPace.from_string(str_input='0:10:00 min per mile')
        distance = FirstDistance.from_string(string='3 mile')
        time = FirstTime.from_string(string='0:15:00')

        try:  # distance step
            step_b = FirstStepBody(name='3 miles @ 10 minutes per mile',
                                   pace=pace,
                                   distance=distance)
            cmp_string = 'Step: "3 miles @ 10 minutes per mile"  id = 0\n' + \
                         'type - body  pace - 0:10:00 min per mile\nDistance - 3.0 mile\n'
            self.assertEqual(cmp_string, str(step_b))
            self.assertAlmostEqual(3.0, step_b.total(unit='mile'), 5)
            self.assertAlmostEqual(4.828032, step_b.total(unit='km'), 5)
            self.assertAlmostEqual(30.0,
                                   step_b.total(what='time', unit='minute'), 5)
            self.assertAlmostEqual(0.5, step_b.total(what='time', unit='hour'),
                                   5)
            tcx_string = (
                '<Step xsi:type="Step_t">\n' + '  <StepId>0</StepId>\n' +
                '  <Name>3 miles @ 10 minutes per mile</Name>\n' +
                '  <Duration xsi:type="Distance_t">\n' +
                '    <Meters>4828</Meters>\n' + '  </Duration>\n' +
                '  <Intensity>Active</Intensity>\n' +
                '  <Target xsi:type="Speed_t">\n' +
                '    <SpeedZone xsi:type="CustomSpeedZone_t">\n' +
                '      <LowInMetersPerSecond>2.6600727</LowInMetersPerSecond>\n'
                +
                '      <HighInMetersPerSecond>2.7047798</HighInMetersPerSecond>\n'
                + '    </SpeedZone>\n' + '  </Target>\n' + '</Step>')
            self.assertEqual(tcx_string,
                             step_b.tcx().indented_str())  # no indent
            tcx_string = (
                '<Step xsi:type="Step_t">\n' + '  <StepId>0</StepId>\n' +
                '  <Name>3 miles @ 10 minutes per mile</Name>\n' +
                '  <Duration xsi:type="Distance_t">\n' +
                '    <Meters>4828</Meters>\n' + '  </Duration>\n' +
                '  <Intensity>Active</Intensity>\n' +
                '  <Target xsi:type="Speed_t">\n' +
                '    <SpeedZone xsi:type="CustomSpeedZone_t">\n' +
                '      <LowInMetersPerSecond>2.6688955</LowInMetersPerSecond>\n'
                +
                '      <HighInMetersPerSecond>2.6957186</HighInMetersPerSecond>\n'
                + '    </SpeedZone>\n' + '  </Target>\n' + '</Step>')
            self.assertEqual(tcx_string,
                             step_b.tcx(delta_seconds=3).indented_str())
            cmp_json = {
                'distance': {
                    'distance': 3.0,
                    'unit': 'mile'
                },
                'name': '3 miles @ 10 minutes per mile',
                'pace': {
                    'length_unit': 'mile',
                    'pace': '0:10:00 min per mile',
                    'time': {
                        'seconds': 600,
                        'time': '0:10:00'
                    }
                }
            }
            self.assertEqual(cmp_json, step_b.to_json())
            cmp_json = {
                'distance': {
                    'distance': 4.828032,
                    'unit': 'km'
                },
                'name': '3 miles @ 10 minutes per mile',
                'pace': {
                    'length_unit': 'km',
                    'pace': '0:06:13 min per km',
                    'time': {
                        'seconds': 373,
                        'time': '0:06:13'
                    }
                }
            }
            self.assertEqual(cmp_json, step_b.to_json(output_unit='km'))
            cmp_html = (
                '<div style="margin-left: 20px">\n' + '  <p>\n' +
                '    3 miles @ 10 minutes per mile - 3.000 mile at 0:10:00 min per mile\n'
                + '  </p>\n' + '</div>')
            self.assertEqual(cmp_html, step_b.to_html().indented_str())
            cmp_html = (
                '<div style="margin-left: 20px">\n' + '  <p>\n' +
                '    3 miles @ 10 minutes per mile - 4.828 km at 0:06:13 min per km\n'
                + '  </p>\n' + '</div>')
            self.assertEqual(cmp_html,
                             step_b.to_html(output_unit='km').indented_str())
        except TypeError as tex:
            self.fail(str(tex))
        except ValueError as vex:
            self.fail(str(vex))

        try:  # time step
            step_b = FirstStepBody(name='15 minutes @ 10 minutes per mile',
                                   pace=pace,
                                   time=time)
            cmp_string = 'Step: "15 minutes @ 10 minutes per mile"  id = 1\n' + \
                         'type - body  pace - 0:10:00 min per mile\nTime - 0:15:00\n'
            self.assertEqual(cmp_string, str(step_b))
            self.assertAlmostEqual(15.0,
                                   step_b.total(what='time', unit='minute'), 5)
            self.assertAlmostEqual(7920.0, step_b.total(unit='ft'))
            tcx_string = (
                '<Step xsi:type="Step_t">\n' + '  <StepId>1</StepId>\n' +
                '  <Name>15 minutes @ 10 minutes per mile</Name>\n' +
                '  <Duration xsi:type="Time_t">\n' +
                '    <Seconds>900</Seconds>\n' + '  </Duration>\n' +
                '  <Intensity>Active</Intensity>\n' +
                '  <Target xsi:type="Speed_t">\n' +
                '    <SpeedZone xsi:type="CustomSpeedZone_t">\n' +
                '      <LowInMetersPerSecond>2.6600727</LowInMetersPerSecond>\n'
                +
                '      <HighInMetersPerSecond>2.7047798</HighInMetersPerSecond>\n'
                + '    </SpeedZone>\n' + '  </Target>\n' + '</Step>')
            self.assertEqual(tcx_string, step_b.tcx().indented_str())
            cmp_json = {
                'name': '15 minutes @ 10 minutes per mile',
                'pace': {
                    'length_unit': 'mile',
                    'pace': '0:10:00 min per mile',
                    'time': {
                        'seconds': 600,
                        'time': '0:10:00'
                    }
                },
                'time': {
                    'seconds': 900,
                    'time': '0:15:00'
                }
            }
            self.assertEqual(cmp_json, step_b.to_json())
            cmp_json = {
                'name': '15 minutes @ 10 minutes per mile',
                'pace': {
                    'length_unit': 'km',
                    'pace': '0:06:13 min per km',
                    'time': {
                        'seconds': 373,
                        'time': '0:06:13'
                    }
                },
                'time': {
                    'seconds': 900,
                    'time': '0:15:00'
                }
            }
            self.assertEqual(cmp_json, step_b.to_json(output_unit='km'))
            cmp_html = (
                '<div style="margin-left: 20px">\n' + '  <p>\n' +
                '    15 minutes @ 10 minutes per mile - 0:15:00 at 0:10:00 min per mile\n'
                + '  </p>\n' + '</div>')
            self.assertEqual(cmp_html, step_b.to_html().indented_str())
            cmp_html = (
                '<div style="margin-left: 20px">\n' + '  <p>\n' +
                '    15 minutes @ 10 minutes per mile - 0:15:00 at 0:06:13 min per km\n'
                + '  </p>\n' + '</div>')
            self.assertEqual(cmp_html,
                             step_b.to_html(output_unit='km').indented_str())
        except TypeError as tex:
            self.fail(str(tex))
        except ValueError as vex:
            self.fail(str(vex))

        try:  # no distance and no time
            _ = FirstStepBody(name='dummy', pace=pace)
            self.fail('Should not get here with neither distance nor duration')
        except ValueError as ex:
            self.assertEqual('Either distance or time must have a value',
                             str(ex))

        try:  # both distance and time
            _ = FirstStepBody(name='dummy',
                              pace=pace,
                              distance=distance,
                              time=time)
            self.fail('Should not get here with both distance and time')
        except ValueError as ex:
            self.assertEqual(
                'Cannot set both distance and duration in the same step',
                str(ex))
Beispiel #25
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))
Beispiel #26
0
    def test_steps(self):

        FirstStepBase.reset_global_id()
        t_warmup = FirstTime.from_string(string='0:15:00')
        p_warmup = FirstPace.from_string(str_input='0:10:00 min per mile')
        s_warmup = FirstStepBody(name='Warm up', pace=p_warmup, time=t_warmup)

        s_intervals = FirstStepRepeat(name='Intervals', repeat=8)
        d_interval = FirstDistance.from_string(string='400 m')
        p_fast = FirstPace.from_string(str_input='0:08:00 min per mile')
        s_fast = FirstStepBody(name='Fast', pace=p_fast, distance=d_interval)
        s_slow = FirstStepBody(name='Rest', pace=p_warmup, distance=d_interval)
        s_intervals.add_step(step=s_fast)
        s_intervals.add_step(step=s_slow)

        t_cooldown = FirstTime.from_string(string='0:10:00')
        s_cooldown = FirstStepBody(name='Cool down',
                                   pace=p_warmup,
                                   time=t_cooldown)

        try:  # positive
            wo = FirstWorkout(name='Week 1 Key-run 1',
                              workout_date=date(2017, 6, 24))
            wo.add_step(step=s_warmup)
            wo.add_step(step=s_intervals)
            wo.add_step(step=s_cooldown)
            cmp_string = ('Week 1 Key-run 1\n' + '2017-06-24\n' +
                          'scheduled\n' + '\tStep: "Warm up"  id = 0\n' +
                          'type - body  pace - 0:10:00 min per mile\n' +
                          'Time - 0:15:00\n' +
                          '\tStep: "Intervals"  id = 1\n' +
                          'type - repeat  repeat - 8\n' +
                          '\tStep: "Cool down"  id = 4\n' +
                          'type - body  pace - 0:10:00 min per mile\n' +
                          'Time - 0:10:00\n')
            self.assertEqual(cmp_string, str(wo))
            cmp_string = '"Week 1 Key-run 1"\n' + \
                         '  Sat 2017-06-24\n' + \
                         '  scheduled\n' + \
                         '  Step: "Warm up"\n' + \
                         '    0:15:00  at  0:10:00 min per mile\n' + \
                         '  Step: "Intervals"\n' + \
                         '    Step: "Fast"\n' + \
                         '      400.0 m  at  0:08:00 min per mile\n' + \
                         '    Step: "Rest"\n' + \
                         '      400.0 m  at  0:10:00 min per mile\n' + \
                         '  Step: "Cool down"\n' + \
                         '    0:10:00  at  0:10:00 min per mile\n' + \
                         '  Totals: distance = 6.48 miles   duration = 60.73 minutes\n'
            self.assertEqual(cmp_string, wo.details(level=2))
            total_distance_miles = 15.0 / 10 + 8 * (800 / 1609.344) + 10.0 / 10
            self.assertAlmostEqual(total_distance_miles, wo.total(), 5)
            total_distance_km = total_distance_miles * 1.609344
            self.assertAlmostEqual(total_distance_km, wo.total(unit='km'), 5)
            total_time_minutes = 15.0 + 8 * (
                round(400 / 1609.344 * 8 * 60) / 60 +
                round(400 / 1609.344 * 10 * 60) / 60) + 10.0
            self.assertAlmostEqual(total_time_minutes,
                                   wo.total(what='time', unit='minute'), 5)
            total_time_hours = total_time_minutes / 60.0
            self.assertAlmostEqual(total_time_hours,
                                   wo.total(what='time', unit='hour'))
            tcx_string = (
                '<Workout Sport="Running">\n' +
                '  <Name>Week 1 Key-run 1</Name>\n' +
                '  <Step xsi:type="Step_t">\n' + '    <StepId>0</StepId>\n' +
                '    <Name>Warm up</Name>\n' +
                '    <Duration xsi:type="Time_t">\n' +
                '      <Seconds>900</Seconds>\n' + '    </Duration>\n' +
                '    <Intensity>Active</Intensity>\n' +
                '    <Target xsi:type="Speed_t">\n' +
                '      <SpeedZone xsi:type="CustomSpeedZone_t">\n' +
                '        <LowInMetersPerSecond>2.6600727</LowInMetersPerSecond>\n'
                +
                '        <HighInMetersPerSecond>2.7047798</HighInMetersPerSecond>\n'
                + '      </SpeedZone>\n' + '    </Target>\n' + '  </Step>\n' +
                '  <Step xsi:type="Repeat_t">\n' + '    <StepId>1</StepId>\n' +
                '    <Name>Intervals</Name>\n' +
                '    <Repetitions>8</Repetitions>\n' +
                '    <Child xsi:type="Step_t">\n' +
                '      <StepId>2</StepId>\n' + '      <Name>Fast</Name>\n' +
                '      <Duration xsi:type="Distance_t">\n' +
                '        <Meters>400</Meters>\n' + '      </Duration>\n' +
                '      <Intensity>Active</Intensity>\n' +
                '      <Target xsi:type="Speed_t">\n' +
                '        <SpeedZone xsi:type="CustomSpeedZone_t">\n' +
                '          <LowInMetersPerSecond>3.3182351</LowInMetersPerSecond>\n'
                +
                '          <HighInMetersPerSecond>3.3880926</HighInMetersPerSecond>\n'
                + '        </SpeedZone>\n' + '      </Target>\n' +
                '    </Child>\n' + '    <Child xsi:type="Step_t">\n' +
                '      <StepId>3</StepId>\n' + '      <Name>Rest</Name>\n' +
                '      <Duration xsi:type="Distance_t">\n' +
                '        <Meters>400</Meters>\n' + '      </Duration>\n' +
                '      <Intensity>Active</Intensity>\n' +
                '      <Target xsi:type="Speed_t">\n' +
                '        <SpeedZone xsi:type="CustomSpeedZone_t">\n' +
                '          <LowInMetersPerSecond>2.6600727</LowInMetersPerSecond>\n'
                +
                '          <HighInMetersPerSecond>2.7047798</HighInMetersPerSecond>\n'
                + '        </SpeedZone>\n' + '      </Target>\n' +
                '    </Child>\n' + '  </Step>\n' +
                '  <Step xsi:type="Step_t">\n' + '    <StepId>4</StepId>\n' +
                '    <Name>Cool down</Name>\n' +
                '    <Duration xsi:type="Time_t">\n' +
                '      <Seconds>600</Seconds>\n' + '    </Duration>\n' +
                '    <Intensity>Active</Intensity>\n' +
                '    <Target xsi:type="Speed_t">\n' +
                '      <SpeedZone xsi:type="CustomSpeedZone_t">\n' +
                '        <LowInMetersPerSecond>2.6600727</LowInMetersPerSecond>\n'
                +
                '        <HighInMetersPerSecond>2.7047798</HighInMetersPerSecond>\n'
                + '      </SpeedZone>\n' + '    </Target>\n' + '  </Step>\n')
            tcx_string_end = ('  <ScheduledOn>2017-06-24</ScheduledOn>\n' +
                              '</Workout>')
            cmp_string = tcx_string + tcx_string_end
            self.assertEqual(cmp_string, wo.tcx().indented_str())
            steps = [{
                'name': 'Warm up',
                'pace': {
                    'length_unit': 'mile',
                    'pace': '0:10:00 min per mile',
                    'time': {
                        'seconds': 600,
                        'time': '0:10:00'
                    }
                },
                'time': {
                    'seconds': 900,
                    'time': '0:15:00'
                }
            }, {
                'name':
                'Intervals',
                'repeat':
                8,
                'steps': [{
                    'distance': {
                        'distance': 400.0,
                        'unit': 'm'
                    },
                    'name': 'Fast',
                    'pace': {
                        'length_unit': 'mile',
                        'pace': '0:08:00 min per mile',
                        'time': {
                            'seconds': 480,
                            'time': '0:08:00'
                        }
                    }
                }, {
                    'distance': {
                        'distance': 400.0,
                        'unit': 'm'
                    },
                    'name': 'Rest',
                    'pace': {
                        'length_unit': 'mile',
                        'pace': '0:10:00 min per mile',
                        'time': {
                            'seconds': 600,
                            'time': '0:10:00'
                        }
                    }
                }]
            }, {
                'name': 'Cool down',
                'pace': {
                    'length_unit': 'mile',
                    'pace': '0:10:00 min per mile',
                    'time': {
                        'seconds': 600,
                        'time': '0:10:00'
                    }
                },
                'time': {
                    'seconds': 600,
                    'time': '0:10:00'
                }
            }]
            cmp_json = {
                'date': '2017-06-24',
                'name': 'Week 1 Key-run 1',
                'note': None,
                'status': 'scheduled',
                'steps': steps,
                'total_distance': {
                    'distance': 6.47678,
                    'unit': 'mile'
                },
                'total_time': {
                    'time': 60.73333,
                    'unit': 'minute'
                }
            }
            FirstUtils.assert_deep_almost_equal(self, cmp_json, wo.to_json(),
                                                5)
            km_steps = [{
                'name': 'Warm up',
                'pace': {
                    'length_unit': 'km',
                    'pace': '0:06:13 min per km',
                    'time': {
                        'seconds': 373,
                        'time': '0:06:13'
                    }
                },
                'time': {
                    'seconds': 900,
                    'time': '0:15:00'
                }
            }, {
                'name':
                'Intervals',
                'repeat':
                8,
                'steps': [{
                    'distance': {
                        'distance': 0.4,
                        'unit': 'km'
                    },
                    'name': 'Fast',
                    'pace': {
                        'length_unit': 'km',
                        'pace': '0:04:58 min per km',
                        'time': {
                            'seconds': 298,
                            'time': '0:04:58'
                        }
                    }
                }, {
                    'distance': {
                        'distance': 0.4,
                        'unit': 'km'
                    },
                    'name': 'Rest',
                    'pace': {
                        'length_unit': 'km',
                        'pace': '0:06:13 min per km',
                        'time': {
                            'seconds': 373,
                            'time': '0:06:13'
                        }
                    }
                }]
            }, {
                'name': 'Cool down',
                'pace': {
                    'length_unit': 'km',
                    'pace': '0:06:13 min per km',
                    'time': {
                        'seconds': 373,
                        'time': '0:06:13'
                    }
                },
                'time': {
                    'seconds': 600,
                    'time': '0:10:00'
                }
            }]
            cmp_json['steps'] = km_steps
            cmp_json['total_distance'] = {'distance': 10.42336, 'unit': 'km'}
            FirstUtils.assert_deep_almost_equal(self, cmp_json,
                                                wo.to_json(output_unit='km'),
                                                5)
            cmp_html = (
                '<div style="margin-left: 20px">\n' +
                '  <h3>Week 1 Key-run 1 - Sat, Jun 24 2017</h3>\n' +
                '  <div style="margin-left: 20px">\n' + '    <p>\n' +
                '      Warm up - 0:15:00 at 0:10:00 min per mile\n' +
                '    </p>\n' + '  </div>\n' +
                '  <div style="margin-left: 20px">\n' + '    <p>\n' +
                '      Repeat 8 times:\n' + '    </p>\n' +
                '    <div style="margin-left: 20px">\n' + '      <p>\n' +
                '        Fast - 400.000 m at 0:08:00 min per mile\n' +
                '      </p>\n' + '    </div>\n' +
                '    <div style="margin-left: 20px">\n' + '      <p>\n' +
                '        Rest - 400.000 m at 0:10:00 min per mile\n' +
                '      </p>\n' + '    </div>\n' + '  </div>\n' +
                '  <div style="margin-left: 20px">\n' + '    <p>\n' +
                '      Cool down - 0:10:00 at 0:10:00 min per mile\n' +
                '    </p>\n' + '  </div>\n' +
                '  <table style="border-spacing: 15px 0">\n' +
                '    <tbody>\n' + '      <tr>\n' +
                '        <td>Total Distance:</td>\n' +
                '        <td><b>6.48 mile</b></td>\n' + '      </tr>\n' +
                '      <tr>\n' + '        <td>Total Time:</td>\n' +
                '        <td><b>61 minutes</b></td>\n' + '      </tr>\n' +
                '    </tbody>\n' + '  </table>\n' + '</div>')
            self.assertEqual(cmp_html, wo.to_html().indented_str())
            cmp_html = (
                '<div style="margin-left: 20px">\n' +
                '  <h3>Week 1 Key-run 1 - Sat, Jun 24 2017</h3>\n' +
                '  <div style="margin-left: 20px">\n' + '    <p>\n' +
                '      Warm up - 0:15:00 at 0:06:13 min per km\n' +
                '    </p>\n' + '  </div>\n' +
                '  <div style="margin-left: 20px">\n' + '    <p>\n' +
                '      Repeat 8 times:\n' + '    </p>\n' +
                '    <div style="margin-left: 20px">\n' + '      <p>\n' +
                '        Fast - 0.400 km at 0:04:58 min per km\n' +
                '      </p>\n' + '    </div>\n' +
                '    <div style="margin-left: 20px">\n' + '      <p>\n' +
                '        Rest - 0.400 km at 0:06:13 min per km\n' +
                '      </p>\n' + '    </div>\n' + '  </div>\n' +
                '  <div style="margin-left: 20px">\n' + '    <p>\n' +
                '      Cool down - 0:10:00 at 0:06:13 min per km\n' +
                '    </p>\n' + '  </div>\n' +
                '  <table style="border-spacing: 15px 0">\n' +
                '    <tbody>\n' + '      <tr>\n' +
                '        <td>Total Distance:</td>\n' +
                '        <td><b>10.42 km</b></td>\n' + '      </tr>\n' +
                '      <tr>\n' + '        <td>Total Time:</td>\n' +
                '        <td><b>61 minutes</b></td>\n' + '      </tr>\n' +
                '    </tbody>\n' + '  </table>\n' + '</div>')
            self.assertEqual(cmp_html,
                             wo.to_html(output_unit='km').indented_str())

            wo.add_step(step=s_warmup)
            cmp_string = ('Week 1 Key-run 1\n' + '2017-06-24\n' +
                          'scheduled\n' + '\tStep: "Warm up"  id = 0\n' +
                          'type - body  pace - 0:10:00 min per mile\n' +
                          'Time - 0:15:00\n' +
                          '\tStep: "Intervals"  id = 1\n' +
                          'type - repeat  repeat - 8\n' +
                          '\tStep: "Cool down"  id = 4\n' +
                          'type - body  pace - 0:10:00 min per mile\n' +
                          'Time - 0:10:00\n' + '\tStep: "Warm up"  id = 0\n' +
                          'type - body  pace - 0:10:00 min per mile\n' +
                          'Time - 0:15:00\n')
            self.assertEqual(cmp_string, str(wo))
            steps.append({
                'name': 'Warm up',
                'pace': {
                    'length_unit': 'mile',
                    'pace': '0:10:00 min per mile',
                    'time': {
                        'seconds': 600,
                        'time': '0:10:00'
                    }
                },
                'time': {
                    'seconds': 900,
                    'time': '0:15:00'
                }
            })
            cmp_json['steps'] = steps
            cmp_json['total_distance'] = {'distance': 7.97678, 'unit': 'mile'}
            cmp_json['total_time'] = {'time': 75.73333, 'unit': 'minute'}
            FirstUtils.assert_deep_almost_equal(self, cmp_json, wo.to_json(),
                                                5)
            km_steps.append({
                'name': 'Warm up',
                'pace': {
                    'length_unit': 'km',
                    'pace': '0:06:13 min per km',
                    'time': {
                        'seconds': 373,
                        'time': '0:06:13'
                    }
                },
                'time': {
                    'seconds': 900,
                    'time': '0:15:00'
                }
            })
            cmp_json['steps'] = km_steps
            cmp_json['total_distance'] = {'distance': 12.83738, 'unit': 'km'}
            FirstUtils.assert_deep_almost_equal(self, cmp_json,
                                                wo.to_json(output_unit='km'),
                                                5)
        except ValueError as vex:
            self.fail(str(vex))
        except TypeError as tex:
            self.fail(str(tex))

        wo1 = FirstWorkout(name='Week 1 Key-run 1',
                           workout_date=date(2017, 4, 1))

        try:  # change status
            wo1.set_status('skipped')
            self.assertEqual(
                'Week 1 Key-run 1\n2017-04-01\nskipped\n\tEmpty workout\n',
                str(wo1))
        except ValueError as ex:
            self.fail(str(ex))

        try:  # bad status
            wo1.set_status('lulu')
            self.fail('Should not get here with bad status')
        except ValueError as ex:
            self.assertEqual("Status not in ['scheduled', 'done', 'skipped']",
                             str(ex))
Beispiel #27
0
class FirstPace(object):
    def __init__(self,
                 minutes: int = 0,
                 seconds: int = 0,
                 length_unit: str = 'mile'):
        """
        Constructor

        :param minutes:
        :type minutes: int
        :param seconds:
        :type seconds: int
        :param length_unit:
        :type length_unit: str
        :return: instance of FirstPace
        :rtype: FirstPace
        """
        if not FirstDistance.is_valid_unit(unit=length_unit):
            raise ValueError(
                '"{}" is not a valid length unit'.format(length_unit))
        self.time = FirstTime(minutes=minutes, seconds=seconds)
        self.length_unit = length_unit

    def __str__(self):

        return str(self.time) + ' min per ' + self.length_unit

    def to_json(self, output_unit: Union[str, None] = None) -> Dict:

        if output_unit and output_unit != self.length_unit:
            dist = FirstDistance(1.0, output_unit)
            seconds_time = self.to_time(dist, 'second')
            output_pace = FirstPace(seconds=round(seconds_time),
                                    length_unit=output_unit)
            return {
                'pace': str(output_pace),
                'length_unit': output_unit,
                'time': output_pace.time.to_json()
            }
        else:
            return {
                'pace': str(self),
                'length_unit': self.length_unit,
                'time': self.time.to_json()
            }

    def to_html(self, output_unit: Union[str, None] = None) -> str:

        if output_unit and output_unit != self.length_unit:
            dist = FirstDistance(1.0, output_unit)
            seconds_time = self.to_time(dist, 'second')
            output_pace = FirstPace(seconds=round(seconds_time),
                                    length_unit=output_unit)
            return '{} min per {}'.format(str(output_pace.time), output_unit)
        else:
            return '{} min per {}'.format(str(self.time), self.length_unit)

    @classmethod
    def from_string(cls, str_input: str):
        """
        Constructor: Instantiate FirstPace from a string input
        
        :param str_input: format - '0:MM:SS per unit'
        :type str_input: str
        :return: instance of FirstPace
        :rtype: FirstPace
        """
        tokens = str_input.split()

        p_time = FirstTime.from_string(
            string=tokens[0])  # pass the exception on
        length_unit = tokens[-1]

        if not FirstDistance.is_valid_unit(unit=tokens[-1]):
            raise ValueError(
                '"{}" is not a valid length unit'.format(length_unit))

        return cls(minutes=p_time.seconds // 60,
                   seconds=p_time.seconds % 60,
                   length_unit=length_unit)

    @classmethod
    def copy(cls, from_pace: 'FirstPace'):

        return cls.from_string(str(from_pace))

    def to_time(self, distance: FirstDistance, unit: str) -> float:
        """
        How much time will take to run a given distance with this pace

        :param distance: the distance
        :type distance: FirstDistance
        :param unit: the desired unit of the result
        :type unit: str
        :return: the time value for this unit
        :rtype: float
        """
        factor = distance.convert_to(unit=self.length_unit)
        seconds = self.time.total_seconds() * factor
        result_time = FirstTime(seconds=round(seconds))
        return result_time.convert_to(unit=unit)

    def to_distance(self, time: FirstTime, unit: str) -> float:
        """
        How far you run given duration with this pace
        
        :param time: the duration
        :type time: FirstTime
        :param unit: the desired unit of the result
        :type unit: str
        :return: the distance value for this unit
        :rtype: float
        """
        factor = time.total_seconds() / self.time.total_seconds()
        result_distance = FirstDistance(distance=factor, unit=self.length_unit)
        return result_distance.convert_to(unit=unit)

    @classmethod
    def from_time_distance(cls,
                           time: FirstTime,
                           distance: FirstDistance,
                           unit: str = None):
        """
        Constructor: Initiate FirstPace from time/distance

        :param time:
        :type time: FirstTime
        :param distance:
        :type distance: FirstDistance
        :param unit: length unit
        :type unit: str
        :return: instance to FirstPace
        :rtype: FirstPace
        """
        if unit is None:
            unit = distance.unit
        factor = distance.convert_to(
            unit=unit)  # 400m with unit = mile will become ~0.25
        seconds = time.total_seconds(
        ) / factor  # 2 minutes for 400m will give ~(2*60)/0.25

        return cls(minutes=int(seconds // 60),
                   seconds=round(seconds % 60),
                   length_unit=unit)

    def increment(self, seconds: int) -> None:
        """
        Increment the pace by number of seconds - for instructions like 'RP+15'

        :param seconds:
        :type seconds: int
        """
        self.time = FirstTime(seconds=self.time.seconds + seconds)

    def meters_per_second_delta(self, delta_in_seconds: int) -> float:
        """
        Convert to speed in m/s for tcx with delta for tolerance

        :param delta_in_seconds:
        :type delta_in_seconds: int
        :return: calculated speed in m/s
        :rtype: float
        """
        seconds = self.time.total_seconds() + delta_in_seconds
        meters = FirstDistance(distance=1.0,
                               unit=self.length_unit).convert_to(unit='m')

        return meters / seconds
Beispiel #28
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))
Beispiel #29
0
    def test_add_workout(self):

        ws1 = [0, 2, 5]
        rt1 = FirstRaceType(name='Marathon', distance=42.195, unit='km')
        rd1 = date(year=2017, month=7, day=29)
        r1 = FirstRace(name='SFM', race_type=rt1, race_date=rd1)
        rn1 = FirstRunner(name='DBD')
        p1 = FirstPlan(name='My first marathon training plan',
                       weekly_schedule=ws1,
                       race=r1,
                       runner=rn1)

        t_warmup = FirstTime.from_string('0:15:00')
        p_warmup = FirstPace.from_string('0:10:00 min per mile')
        s_warmup = FirstStepBody(name='Warm up', pace=p_warmup, time=t_warmup)

        s_repeat = FirstStepRepeat(name='repeat X 8', repeat=8)
        d_interval = FirstDistance.from_string('400 m')
        p_fast = FirstPace.from_string('0:08:00 min per mile')
        s_fast = FirstStepBody(name='Fast', pace=p_fast, distance=d_interval)
        s_repeat.add_step(s_fast)
        s_slow = FirstStepBody(name='Rest', pace=p_warmup, distance=d_interval)
        s_repeat.add_step(s_slow)

        t_cooldown = FirstTime.from_string('0:10:00')
        s_cooldown = FirstStepBody(name='Cool down',
                                   pace=p_warmup,
                                   time=t_cooldown)

        wo = FirstWorkout(name='Week 1 Key-run 1',
                          workout_date=date(year=2017, month=6, day=24))
        wo.add_step(step=s_warmup)
        wo.add_step(step=s_repeat)
        wo.add_step(step=s_cooldown)

        try:  # first workout
            p1.add_workout(workout=wo)
            cmp_string = (
                'Training Plan:\nName - "My first marathon training plan"\n' +
                'Workout days: Mon, Wed, Sat\nRace:\n' +
                '  Name - "SFM" of type Marathon - 42.195 km\nRunner:\n  Name - "DBD"\nWorkouts:\n'
                + '  "Week 1 Key-run 1"\n    Sat 2017-06-24\n    scheduled\n' +
                'Total 1 workouts\n')
            self.assertEqual(cmp_string, str(p1))

            file_name = expanduser(
                '~/PycharmProjects/first/database/cmp_plan2.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 TypeError as ex:
            self.fail(str(ex))

        try:  # bad workout
            p1.add_workout(workout='workout')
            self.fail('Should not get here with bad workout')
        except TypeError as ex:
            self.assertEqual(
                'FirstPlan.add_workout - workout must be an instance of FirstWorkout',
                str(ex))
Beispiel #30
0
class FirstPace(object):

    def __init__(self, minutes=0, seconds=0, length_unit='mile'):

        """
        Constructor

        :param minutes:
        :type minutes: int
        :param seconds:
        :type seconds: int
        :param length_unit:
        :type length_unit: str
        :return: instance of FirstPace
        :rtype: FirstPace
        """
        where_am_i = 'FirstPace.__init__'
        if not FirstDistance.is_valid_unit(length_unit):
            raise ValueError(where_am_i + ' - "%1s" is not a valid length unit' % length_unit)
        self.time = FirstTime(minutes=minutes, seconds=seconds)
        self.length_unit = length_unit

    def __str__(self):

        return str(self.time) + ' min per ' + self.length_unit

    @classmethod
    def from_string(cls, str_input):

        """
        Constructor: Instantiate FirstPace from a string input
        
        :param str_input: format - '0:MM:SS per unit'
        :type str_input: str
        :return: instance of FirstPace
        :rtype: FirstPace
        """
        where_am_i = 'FirstPace.from_string'
        tokens = str_input.split()

        p_time = FirstTime.from_string(tokens[0])  # pass the exception on
        length_unit = tokens[-1]

        if not FirstDistance.is_valid_unit(unit=tokens[-1]):
            raise ValueError(where_am_i + ' - "%1s" is not a valid length unit' % length_unit)

        return cls(minutes=p_time.seconds//60, seconds=p_time.seconds % 60, length_unit=length_unit)

    def to_time(self, distance, unit):

        """
        How much time will take to run a given distance with this pace
        
        :type distance: FirstDistance
        :param distance: the distance
        :param unit: the desired unit of the result
        :return: the time value for this unit
        :rtype: float
        """
        factor = distance.convert_to(unit=self.length_unit)
        seconds = self.time.total_seconds() * factor
        result_time = FirstTime(seconds=seconds)
        return result_time.convert_to(unit=unit)

    def to_distance(self, time, unit):

        """
        How far you run given duration with this pace
        
        :param time: the duration
        :param unit: the desired unit of the result
        :return: the distance value for this unit
        :rtype: float
        """
        factor = time.total_seconds()/self.time.total_seconds()
        result_distance = FirstDistance(distance=factor, unit=self.length_unit)
        return result_distance.convert_to(unit=unit)

    @classmethod
    def from_time_distance(cls, time, distance, unit=None):

        """
        Constructor: Initiate FirstPace from time/distance

        :param time:
        :type time: FirstTime
        :param distance:
        :type distance: FirstDistance
        :param unit: length unit
        :type unit: str
        :return: instance to FirstPace
        :rtype: FirstPace
        """
        if unit is None:
            unit = distance.unit
        factor = distance.convert_to(unit=unit)  # 400m with unit = mile will become ~0.25
        seconds = time.total_seconds() / factor  # 2 minutes for 400m will give ~(2*60)/0.25

        return cls(minutes=seconds//60, seconds=round(seconds % 60), length_unit=unit)

    def increment(self, seconds):

        """
        Increment the pace by number of seconds - for instructions like 'RP+15'
        :param seconds:
        :type seconds: int
        """
        self.time += timedelta(seconds=seconds)

    def meters_per_second_delta(self, delta_in_seconds):

        """
        Convert to speed in m/s for tcx with delta for tolerance
        :param delta_in_seconds:
        :type delta_in_seconds: int
        :return:
        """
        where_am_i = 'FirstPace.meters_per_second_delta'
        if not isinstance(delta_in_seconds, int):
            raise ValueError(where_am_i + ' - delta_in_seconds must be an integer')

        seconds = self.time.total_seconds() + delta_in_seconds
        meters = FirstDistance(distance=1.0, unit=self.length_unit).convert_to(unit='m')

        return meters / seconds