コード例 #1
0
    def calc_pace(self, session, distance_units='miles'):
        """Calculate pace per mile for this split (if applicable)."""
        current_time = self.time
        current_distance = self.distance(session, units=distance_units)

        previous_split = session.splits.filter(
            time__lt=self.time,
            athlete=self.athlete,
            splitfilter__filtered=False).order_by('-time').first()

        if previous_split is None:
            if session.start_button_time is None:
                # This split is the first in the session, can't calculate
                # pace.
                return None
            else:
                previous_time = session.start_button_time
                previous_distance = 0.0
        else:
            previous_time = previous_split.time
            previous_distance = previous_split.distance(session,
                                                        units=distance_units)

        if current_distance is None or previous_distance is None:
            return None

        pace_seconds = (((current_time - previous_time) /
                         (current_distance - previous_distance)) / 1000.0)
        return '{} min/{}'.format(format_total_seconds(pace_seconds),
                                  distance_units[:-1])
コード例 #2
0
def notify(request):
    """Send notifications to all subscribers listening to a session.
    Each notification message includes the session name, athlete name,
    and total cumulative time.
    """
    split = Split.objects.filter(pk=request.data.get('split'),
                                 splitfilter__filtered=False).first()
    if split is not None:

        subscriptions = Subscription.objects.filter(
            session__in=split.timingsession_set.all(), athlete=split.athlete)

        for subscription in subscriptions:
            # Athlete must be in results since we are receiving a split
            # that says so.
            result = subscription.session.individual_results(
                athlete_ids=[subscription.athlete_id])[0]
            text = _message_template.format(name=result.name,
                                            session=subscription.session.name,
                                            time=format_total_seconds(
                                                result.total))
            message, created = Message.objects.get_or_create(
                subscription=subscription, message=text)
            if created:
                message.send()

    return Response(status=status.HTTP_200_OK)
コード例 #3
0
ファイル: test_models.py プロジェクト: tractiming/trac-gae
 def test_calc_pace_one_checkpoint_with_start(self):
     """Test calculating pace with a start and one checkpoint."""
     self.session.start_button_time = 0
     self.session.save()
     checkpoint1 = Checkpoint.objects.create(session=self.session,
                                             name='A',
                                             distance=1)
     checkpoint1.readers.add(self.reader1)
     SplitFilter.objects.create(timingsession=self.session,
                                split=self.split1)
     correct_pace = '{} min/mile'.format(format_total_seconds(300))
     pace = self.split1.calc_pace(self.session)
     results = self.session._calc_athlete_splits(self.athlete.id,
                                                 calc_paces=True)
     self.assertEqual(pace, correct_pace)
     self.assertEqual(results.paces[0], correct_pace)
コード例 #4
0
ファイル: test_models.py プロジェクト: tractiming/trac-gae
    def test_calc_pace_unit_conversion(self):
        """Test calculating pace with a unit conversion."""
        checkpoint1 = Checkpoint.objects.create(session=self.session,
                                                name='A',
                                                distance=1609.34,
                                                distance_units='m')
        checkpoint2 = Checkpoint.objects.create(session=self.session,
                                                name='B',
                                                distance=3218.68,
                                                distance_units='m')
        checkpoint1.readers.add(self.reader1)
        checkpoint2.readers.add(self.reader2)
        SplitFilter.objects.create(timingsession=self.session,
                                   split=self.split1)
        SplitFilter.objects.create(timingsession=self.session,
                                   split=self.split2)

        pace = self.split2.calc_pace(self.session)
        self.assertEqual(pace,
                         '{} min/mile'.format(format_total_seconds(300.001)))
コード例 #5
0
ファイル: test_models.py プロジェクト: tractiming/trac-gae
    def test_calc_pace_multiple_checkpoints(self):
        """Test calculating pace in a session with multiple checkpoints."""
        checkpoint1 = Checkpoint.objects.create(session=self.session,
                                                name='A',
                                                distance=1)
        checkpoint2 = Checkpoint.objects.create(session=self.session,
                                                name='B',
                                                distance=2)
        checkpoint1.readers.add(self.reader1)
        checkpoint2.readers.add(self.reader2)
        SplitFilter.objects.create(timingsession=self.session,
                                   split=self.split1)
        SplitFilter.objects.create(timingsession=self.session,
                                   split=self.split2)

        correct_pace = '{} min/mile'.format(format_total_seconds(300))
        pace = self.split2.calc_pace(self.session)
        results = self.session._calc_athlete_splits(self.athlete.id,
                                                    calc_paces=True)
        self.assertEqual(pace, correct_pace)
        self.assertEqual(results.paces[0], correct_pace)
コード例 #6
0
    def export_results(self, request, pk=None):
        """Get a link to a text results file.

        The file will be formatted as a CSV with one column for name
        and one for total time. NOTE: This creates a public link to
        the results. Anyone with this link will be able to download
        the file.
        ---
        omit_serializer: true
        omit_parameters:
        - query
        parameters_strategy:
          form: replace
        parameters:
        - name: file_format
          description: Type of file to write ("csv", "pdf", or "tfrrs")
        - name: results_type
          description: Whether to return all splits ("splits") or final
          results only ("final"). Only applied when `file_format` is "csv",
          otherwise this setting is ignored.
        type:
          uri:
            required: true
            type: url
            description: Link to downloadable results file
        """
        session = self.get_object()

        file_format = request.data.get('file_format', 'csv')
        if file_format not in ('csv', 'pdf', 'tfrrs'):
            return Response('Invalid file format',
                            status=status.HTTP_400_BAD_REQUEST)

        results_type = request.data.get('results_type', 'final')
        if results_type not in ('splits', 'final'):
            return Response('Invalid results type',
                            status=status.HTTP_400_BAD_REQUEST)

        if file_format == 'tfrrs':
            modifier = '-tfrrs'
            extension = 'csv'
        elif file_format == 'csv':
            modifier = '-splits' if results_type == 'splits' else ''
            extension = 'csv'
        else:
            modifier = ''
            extension = 'pdf'
        storage_path = '/'.join(
            (settings.GCS_RESULTS_DIR, str(session.pk),
             'individual{modifier}.{extension}'.format(extension=extension,
                                                       modifier=modifier)))

        if file_format == 'tfrrs':
            results_to_write = tfrrs.format_tfrrs_results(session)
            header = tfrrs._TFRRS_FIELDS
        else:
            results = session.individual_results()
            if results_type == 'final':
                results_to_write = (OrderedDict(
                    (('Name', result.name), ('Gender', result.gender),
                     ('Age', result.age), ('Time',
                                           format_total_seconds(result.total)),
                     ('Team', result.team.name))) for result in results)
                header = ('Name', 'Gender', 'Age', 'Time', 'Team')
            else:
                max_splits = max(len(result.splits) for result in results)
                header = list(
                    itertools.chain(('Name', ), ('Gender', ), ('Age', ),
                                    ('Interval {}'.format(i + 1)
                                     for i in xrange(max_splits)), ('Total', ),
                                    ('Team', )))
                results_to_write = (OrderedDict(
                    list(
                        itertools.chain(
                            (('Name', result.name), ),
                            (('Gender', result.gender), ),
                            (('Age', result.age), ),
                            (('Interval {}'.format(num + 1), split)
                             for num, split in enumerate(result.splits)),
                            (('Total', format_total_seconds(result.total)), ),
                            (('Team', result.team.name), ))))
                                    for result in results)

        #print results_to_write
        with gcs_writer(settings.GCS_RESULTS_BUCKET,
                        storage_path,
                        make_public=True) as _results:
            if file_format in ('csv', 'tfrrs'):
                writer = csv.DictWriter(_results, fieldnames=header)
                writer.writeheader()
                for result in results_to_write:
                    writer.writerow(result)
            elif file_format == 'pdf':
                write_pdf_results(_results, results_to_write)

        log.debug('Saved results to %s', storage_path)

        return Response({
            'uri':
            get_public_link(settings.GCS_RESULTS_BUCKET, storage_path)
        })
コード例 #7
0
    def team_csv_results(self, request, pk=None):
        session = self.get_object()
        file_format = request.data.get('file_format', 'csv')

        if file_format not in ('csv'):
            return Response('Invalid file format',
                            status=status.HTTP_400_BAD_REQUEST)

        results_type = request.data.get('results_type', 'teams')
        if results_type not in ('teams'):
            return Response('Invalid results type',
                            status=status.HTTP_400_BAD_REQUEST)

        if file_format == 'csv':
            modifier = '-teams7' if results_type == 'teams' else ''
            extension = 'csv'

        storage_path = '/'.join(
            (settings.GCS_RESULTS_DIR, str(session.pk),
             'individual{modifier}.{extension}'.format(extension=extension,
                                                       modifier=modifier)))

        raw_results = session.team_results()
        results = []

        with gcs_writer(settings.GCS_RESULTS_BUCKET,
                        storage_path,
                        make_public=True) as _results:
            if file_format in ('csv'):
                writer = csv.writer(_results)
                writer.writerow(['Team Name', 'Team Score', 'Team Place'])
            counter = 1
            for place, result in enumerate(raw_results):
                team_result = result
                if team_result['score'] != 0:
                    team_result['place'] = place + counter
                else:
                    team_result['place'] = 0
                    counter -= 1
                print team_result
                results.append(team_result)
                if file_format in ('csv'):
                    writer = csv.writer(_results)
                    writer.writerow([
                        team_result['name'], team_result['score'],
                        team_result['place']
                    ])
                    writer.writerow([''])
                    writer.writerow(
                        ['', 'Athlete Name', 'Athlete Time', 'Athlete Place'])
                    tmp = 0
                    for atl in team_result['athletes']:
                        tmp += 1
                        time = format_total_seconds(atl['total'])
                        if tmp > team_result['num_scorers']:
                            writer.writerow([
                                '', atl['name'], time,
                                '(' + str(atl['place']) + ')'
                            ])
                        else:
                            writer.writerow(
                                [' ', atl['name'], time, atl['place']])
                    writer.writerow([''])
        log.debug('Saved results to %s', storage_path)

        return Response({
            'uri':
            get_public_link(settings.GCS_RESULTS_BUCKET, storage_path)
        })

        return Response("team CSV")
コード例 #8
0
    def export_age_results(self, request, pk=None):
        session = self.get_object()
        file_format = request.data.get('file_format', 'csv')

        if file_format not in ('csv'):
            return Response('Invalid file format',
                            status=status.HTTP_400_BAD_REQUEST)

        results_type = request.data.get('results_type', 'age')
        if results_type not in ('age'):
            return Response('Invalid results type',
                            status=status.HTTP_400_BAD_REQUEST)

        if file_format == 'csv':
            modifier = '-age19' if results_type == 'age' else ''
            extension = 'csv'

        storage_path = '/'.join(
            (settings.GCS_RESULTS_DIR, str(session.pk),
             'age{modifier}.{extension}'.format(extension=extension,
                                                modifier=modifier)))
        raw_results = session.individual_results()
        results = []
        male_list = []
        female_list = []
        unknown_list = []
        for runner in raw_results:
            if runner.gender == 'M':
                male_list.append(runner)
            elif runner.gender == 'F':
                female_list.append(runner)
            else:
                unknown_list.append(runner)
        male_list.sort(key=lambda x: x.age, reverse=True)
        female_list.sort(key=lambda x: x.age, reverse=True)
        with gcs_writer(settings.GCS_RESULTS_BUCKET,
                        storage_path,
                        make_public=True) as _results:
            if file_format in ('csv'):
                writer = csv.writer(_results)
                writer.writerow(['Gender'])
                writer.writerow([''])
                writer.writerow(['M'])
                writer.writerow(['', 'Age Range'])
                written_row = False
                written_athlete_row = False
                BIG_ARRAY = [[0, 19], [20, 29], [30, 39], [40, 49], [50, 59],
                             [60, 69], [70, 120]]

                while male_list:

                    athlete_in_question = male_list.pop()
                    age = athlete_in_question.age

                    for row in BIG_ARRAY:
                        if age >= row[0] and age <= row[1]:
                            row.append(athlete_in_question)

                for array in BIG_ARRAY:
                    writer.writerow(['', str(array[0]) + '-' + str(array[1])])
                    array.pop(0)
                    array.pop(0)
                    array.sort(key=lambda x: x.total)
                    if array:
                        writer.writerow([
                            '', '', 'Athlete Name', 'Athlete Time',
                            'Athlete Age', 'Athlete Team'
                        ])
                    else:
                        pass
                    for element in array:
                        writer.writerow([
                            '', '', element.name,
                            format_total_seconds(element.total), element.age,
                            element.team.name
                        ])

                writer.writerow(['F'])
                writer.writerow([''])
                writer.writerow(['', 'Age Range'])
                BIG_ARRAY = [[0, 19], [20, 29], [30, 39], [40, 49], [50, 59],
                             [60, 69], [70, 120]]

                while female_list:

                    athlete_in_question = female_list.pop()
                    age = athlete_in_question.age

                    for row in BIG_ARRAY:
                        if age >= row[0] and age <= row[1]:
                            row.append(athlete_in_question)

                for array in BIG_ARRAY:
                    writer.writerow(['', str(array[0]) + '-' + str(array[1])])
                    array.pop(0)
                    array.pop(0)
                    array.sort(key=lambda x: x.total)
                    if array:
                        writer.writerow([
                            '', '', 'Athlete Name', 'Athlete Time',
                            'Athlete Age', 'Athlete Team'
                        ])
                    else:
                        pass
                    for element in array:
                        writer.writerow([
                            '', '', element.name,
                            format_total_seconds(element.total), element.age,
                            element.team.name
                        ])

        return Response({
            'uri':
            get_public_link(settings.GCS_RESULTS_BUCKET, storage_path)
        })
        return "Age CSV"
コード例 #9
0
    def _calc_athlete_splits(self,
                             athlete_id,
                             use_cache=True,
                             apply_filter=True,
                             calc_paces=False):
        """Calculate splits for a single athlete.

        First try to read results from the cache. If results are not found,
        get the tag and all its times. Iterate through times to calculate
        splits. Save new results to cache.

        Returns namedtuple of (user id, name, team, splits, total,
        first_seen, paces)
        """
        # Try to read from the cache. Note that results are cached on a per
        # tag basis.
        if use_cache:
            results = cache.get(
                ('ts_%i_athlete_%i_results' % (self.id, athlete_id)))
        else:
            results = None

        Results = namedtuple(
            'Results',
            'user_id name team splits total first_seen last_seen paces bib gender age info'
        )
        if not results:
            athlete = Athlete.objects.get(id=athlete_id)
            name = athlete.user.get_full_name() or athlete.user.username

            filter_ = (models.Q(
                splitfilter__filtered=False) if apply_filter else models.Q())
            raw_splits = self.splits.filter(
                filter_, athlete_id=athlete.id).order_by('time')
            times = list(raw_splits.values_list('time', flat=True))

            # Offset for start time if needed.
            if self.start_button_time is not None:
                times.insert(0, self.start_button_time)

            splits = [
                round((t2 - t1) / 1000.0, 3)
                for t1, t2 in zip(times, times[1:])
            ]

            if len(times) > 0:
                raw_time = timezone.datetime.utcfromtimestamp(times[0] /
                                                              1000.0)
                first_seen = raw_time.strftime("%Y/%m/%d %H:%M:%S.%f")[:-3]

                last_split = timezone.datetime.utcfromtimestamp(times[-1] /
                                                                1000.0)
                last_seen = last_split.strftime("%Y/%m/%d %H:%M:%S.%f")[:-3]

            else:
                first_seen = None
                last_seen = None

            if calc_paces:
                distances = [split.distance(self) for split in raw_splits]
                if self.start_button_time is not None:
                    distances.insert(0, 0.0)

                paces = []
                for i, diff in enumerate(zip(distances, distances[1:])):
                    if diff[0] is not None and diff[1] is not None:
                        pace = '{} min/mile'.format(
                            format_total_seconds(splits[i] /
                                                 (diff[1] - diff[0])))
                    else:
                        pace = None
                    paces.append(pace)

            else:
                paces = None

            try:
                bib = athlete.tag.bib
            except ObjectDoesNotExist:
                bib = None

            try:
                gender = athlete.gender
            except ObjectDoesNotExist:
                gender = None

            try:
                age = athlete.age()
            except ObjectDoesNotExist:
                age = None

            try:
                info = athlete.info_set.filter(
                    timingsession_id=self.id).first().info
            except AttributeError:
                info = None

            results = (athlete_id, name, athlete.team, splits, sum(splits),
                       first_seen, last_seen, paces, bib, gender, age, info)

            if use_cache:
                cache.set(('ts_%i_athlete_%i_results' % (self.id, athlete_id)),
                          results)

        return Results(*results)