def _print_qualifying_times(qt_by_event, heading, do_conversion): print('<table><tbody><tr class="' + heading.lower() + '"><th>' + heading + '</th>') for age in range(min_age, max_age + 1): print('<th>' + str(age) + '</th>') print('</tr>') course_code = "S" if is_long_course: course_code = "L" num_events = len(short_course_events) precision = 2 if do_conversion: precision = 1 for event_code in range(num_events): times = qt_by_event[event_code] if times is not None: event = Event.create_from_code(event_code, course_code) print('<tr class="' + event.short_stroke_name().lower() + '"><td>' + event.short_name_without_course() + '</td>') for time in times: converted_time = time if do_conversion: converted_time = event.convert_time(time) print('<td>' + str(RaceTime(converted_time, precision=precision)) + '</td>') print('</tr>') print('</tbody></table>')
def write_scores(sorted_scores, file_name): print('Writing file') scores_file = open(folder + file_name, 'w') number = 0 held_number = 0 previous_points = 0 for swimmer_times in sorted_scores: number = number + 1 if swimmer_times.points != previous_points: held_number = number previous_points = swimmer_times.points swimmer = swimmer_times.swimmer age_for_points = swimmer_times.age if age_for_points > 16: age_for_points = 16 age_column = age_for_points - 9 scores_file.write('<p class="question">') scores_file.write( str(held_number) + ': ' + swimmer_times.full_name + ' (' + str(swimmer_times.points) + ' points)') scores_file.write('</p>\n') scores_file.write( '<div class="answer"><table><tr><th>Event</th><th>Consideration Time</th><th>Race Time</th><th>Points</th></tr>\n' ) for race in swimmer_times.race_by_event: if (race is not None) and (race.time is not None): scores_file.write('<tr><td>' + race.event.short_name_without_course() + '</td>') if race.consideration_time is not None: nt_str = '' if race.consideration_is_nt: nt_str = '(NT) ' scores_file.write('<td>' + nt_str + str(RaceTime(race.consideration_time)) + '</td>') else: scores_file.write('<td>Missing Consideration Time</td>') scores_file.write('<td>' + str(RaceTime(race.time)) + '</td>') scores_file.write('<td>' + str(race.points) + '</td>') scores_file.write('</tr>\n') scores_file.write('</table></div>\n') scores_file.close()
def report(self, file, team, team_name): relay = () result = '' show_splits = True if self.finish_position == -2: result = 'DNC' show_splits = False elif self.finish_position == -1: result = 'DQ' show_splits = False else: result = str(RaceTime(self.finish_time)) show_splits = len(self.splits) > 1 if len(team.legs) != 1: # Relay or DNS or DQ file.write( '<tr class="team"><th colspan="3">{:}</th><td>{:}</td><td>{:}</td></tr>\n' .format(team_name, result, self.points)) if show_splits: if len(team.legs) <= 4: # Also write out the swimmer names file.write('<tr class="splits">') for swimmer in team.legs: file.write('<td>{:}</td>'.format(swimmer.full_name)) file.write('</tr>\n') file.write('<tr class="splits">') for split in self.splits: file.write('<td>{:}</td>'.format(str(RaceTime(split)))) file.write('</tr>\n') else: # Individual file.write( '<tr class="team"><th>{:}</th><td colspan="2">{:}</td><td>{:}</td><td>{:}</td></tr>\n' .format(team_name, team.legs[0].full_name, result, self.points)) if show_splits: file.write('<tr class="splits">') for split in self.splits: file.write('<td>{:}</td>'.format(str(RaceTime(split)))) file.write('</tr>\n')
def __init__(self, line): tokens = line.split("|") num_tokens = len(tokens) assert ((num_tokens == 2) or (num_tokens == 3)) self.event_code = Event.create_from_str( tokens[0], "S").get_short_course_event_code() self.time = float(RaceTime(tokens[1])) if num_tokens == 3: self.is_nt = (tokens[2] == 'nt\n') else: self.is_nt = False
def __init__(self, asa_number, event, tokens): token_offset = 0 if len(tokens) == 6: token_offset = 1 self.asa_number = asa_number self.event = event self.date = helpers.ParseDate_dmY(tokens[token_offset]) self.meet = tokens[token_offset + 1].strip() self.asa_swim_id = 0 self.splits = None self.level = int(tokens[token_offset + 3]) self.race_time = float(RaceTime(tokens[token_offset + 4])) self.short_course_race_time = self.race_time if self.event.is_long_course(): self.short_course_race_time = self.event.convert_time( self.race_time)
def _parse_spreadsheet_data( spreadsheet_data ): rows = spreadsheet_data.split( '\n' ) num_events = len( short_course_events ) nt_times_by_event = [ None ] * num_events for row in rows: columns = row.split( '\t' ) # Parse the event name event_code = Event.create_from_str( columns[0], 'S' ).event_code if len( columns ) != 9: raise RuntimeError( "Unexpected number of columns in spreadsheet data" ) nt_times_for_event = [] nt_times_by_event[ event_code ] = nt_times_for_event for i in range( 1, 9 ): if len( columns[i] ) == 0: nt_times_for_event.append( None ) else: nt_times_for_event.append( float( RaceTime( columns[i] ) ) ) return nt_times_by_event
def _parse_spreadsheet_data(spreadsheet_data): rows = spreadsheet_data.split('\n') num_events = len(short_course_events) qt_by_event = [None] * num_events expected_num_columns = max_age - min_age + 2 for row in rows: columns = row.split('\t') # Parse the event name event_code = Event.create_from_str(columns[0], 'S').event_code if len(columns) != expected_num_columns: raise RuntimeError( "Unexpected number of columns in spreadsheet data") qt_for_event = [] qt_by_event[event_code] = qt_for_event for i in range(1, expected_num_columns): if len(columns[i]) == 0: qt_for_event.append(None) else: qt_for_event.append(float(RaceTime(columns[i]))) return qt_by_event
def process_swimmer(swimmer, swims): age = helpers.CalcAge(swimmer.date_of_birth, age_on_date) full_name = swimmer.full_name() if full_name in excluded_swimmers: return if age > maximum_age: return full_name = swimmer.alternate_name() # Find PB in the qualifying window, and qualifying PB pb_by_event = [] qual_pb_by_event = [] print(full_name) for i in range(0, num_events): pb_by_event.append(None) qual_pb_by_event.append(None) for swim in swims: if swim.date >= earliest_pb_date: event_code = swim.event.get_short_course_event_code() swim.converted_time = swim.race_time swim.is_converted = False swim.qualifies = (swim.level <= max_qualifying_meet_level) if swim.event.is_long_course() != is_long_course: swim.converted_time = swim.event.convert_time(swim.race_time) swim.is_converted = True #print( swim.event.short_name_without_course() + "\t" + str( swim.event.to_int() ) + "\t" + str( RaceTime( swim.race_time ) ) + "\t" + str( RaceTime( swim.converted_time ) ) + "\t" + swim.meet ) # Round to 0.1s swim.converted_time = math.floor((swim.converted_time * 10) + 0.5) * 0.1 qt = get_qualifying_time(event_code, swimmer.is_male, age) if (qt is None) or (swim.converted_time > qt): swim.qualifies = False if swim.qualifies: qual_pb = qual_pb_by_event[event_code] #print( "Considering: " + swim.event.short_name_without_course() + "\t" + str( swim.event.to_int() ) + "\t" + str( RaceTime( swim.race_time ) ) + "\t" + str( RaceTime( swim.converted_time ) ) + "\t" + swim.meet ) if prefer_qualifying_times_in_target_course and (qual_pb is not None): # This meet insists that if you have qualifying times in the target course # then they should take precedence over other swims if qual_pb.is_converted and (not swim.is_converted): # Pretend the current qual_pb does not exist qual_pb = None if ((qual_pb is None) or (swim.converted_time < qual_pb.converted_time)): use_this_swim = True if prefer_qualifying_times_in_target_course and ( qual_pb is not None): # This meet insists that if you have qualifying times in the target course # then they should take precedence over other swims if (not qual_pb.is_converted) and swim.is_converted: # Which means we can't use this swim use_this_swim = False if use_this_swim: qual_pb_by_event[event_code] = swim pb = pb_by_event[event_code] if (pb is None) or (swim.converted_time < pb.converted_time): #print( "PB: " + swim.event.short_name_without_course() + "\t" + str( swim.event.to_int() ) + "\t" + str( RaceTime( swim.race_time ) ) + "\t" + str( RaceTime( swim.converted_time ) ) + "\t" + swim.meet ) pb_by_event[event_code] = swim printed_name = False converted_from_course_name = "LC" if is_long_course: converted_from_course_name = "SC" for i in range(0, num_events): qt = get_qualifying_time(i, swimmer.is_male, age) if qt is not None: pb = qual_pb_by_event[i] if (pb is None) or (pb.converted_time > qt): # There isn't a time from a qualifying event, or it was too slow. # Let's consider a non-qualifying event instead (if there is one). #if pb is not None: # print( "Slow: " + str(i) + ", " + str( pb.converted_time ) + ", " + str( qt ) ) pb = pb_by_event[i] if pb is not None: race_time = pb.converted_time if race_time <= qt: tag_class = "qualified" if not pb.qualifies: tag_class = "not-qualified" if not printed_name: qt_file.write(full_name + " (" + str(age) + ")\n") qt_html_file.write( '<tr class="name"><th colspan="5">' + full_name + " (" + str(age) + ")</th></tr>\n") printed_name = True precision = 2 conversion_str = "" if pb.is_converted: precision = 1 conversion_str = '<small> (' + str( RaceTime(pb.race_time) ) + ' ' + converted_from_course_name + ')</small>' qt_file.write("\t" + pb.event.short_name_without_course() + "\t" + str(RaceTime(race_time, precision)) + "\t" + pb.meet + "\t" + pb.date.strftime('%d/%m/%Y') + "\n") qt_html_file.write('<tr class="' + tag_class + '"><td> </td><td>' + pb.event.short_name_without_course() + "</td><td>" + str(RaceTime(race_time, precision)) + "</td><td>" + pb.meet + "</td><td>" + pb.date.strftime('%d/%m/%Y') + "</td></tr>\n") if printed_name: qt_file.write("\n")
# This is a club champs swim swim_by_event[swim.event.get_short_course_event_code()] = swim has_entered = True if has_entered: print(full_name + ', ' + str(age)) swimmer_times = SwimmerTimes(swimmer, full_name) swimmer_times.swim_by_event = swim_by_event all_swimmer_times.append(swimmer_times) # Read all the data from the club rankings exports swimmers = read_club_rankings.ReadClubRankingsFiles() for swimmer in swimmers: process_swimmer(swimmer, swimmer.swims) # Now we produce the output file print('Writing file') first = True for swimmer_times in all_swimmer_times: if not first: race_times_file.write('\n') swimmer = swimmer_times.swimmer race_times_file.write(str(swimmer) + '\n') for swim in swimmer_times.swim_by_event: if swim is not None: race_times_file.write(swim.event.short_name_without_course() + '|' + str(RaceTime(swim.race_time)) + '\n') first = False race_times_file.close()
# it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program (file LICENSE); if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import hytek_hy3_parser import hytek_helpers from race_time import RaceTime # Open a sample HY3 file and parse the lines f = open('C:\Users\Oli\Documents\TimeTrialResults_11_5_2013.HY3', 'r') parsed_lines = hytek_hy3_parser.parse( f ) f.close() # Now take those parsed lines and parse them as race results to get # a list of swimmers, each with a list of race results swimmers = hytek_helpers.parse_results( parsed_lines ) # Print them out to show it works.... for swimmer in swimmers: print( "Swimmer: " + swimmer.first_name + " " + swimmer.last_name + ", DoB: " + str( swimmer.date_of_birth ) ) for swim in swimmer.swims: print( "Meet: " + swim.meet + ", Date: " + str( swim.date ) + ", Event: " + str( swim.event ) + ", Time: " + str( RaceTime( swim.race_time ) ) )
def scrape_meet(asa_meet_code, page_number, meet_name, date, course_code): logging.info("Attempting to parse meet " + meet_name + ", meet code: " + str(asa_meet_code) + ", page: " + str(page_number)) # Load a meet page from a URL like this... # https://www.swimmingresults.org/showmeetsbyclub/index.php?meetcode=19611&targetclub=WINNCHRN url = "https://www.swimmingresults.org/showmeetsbyclub/index.php?meetcode=" + str( asa_meet_code) + "&targetclub=WINNCHRN&page=" + str(page_number) page = helpers.FetchUrl(url) if page is None: logging.error("Failed to get page " + url) return 503 tree = html.fromstring(page) meet_has_been_parsed(asa_meet_code) try: table = tree.get_element_by_id("rankTable") except: logging.info("No rankTable for " + url + ". Presuming no Winsford swimmers at that meet") return if page_number == 1: # When scraping the first page, one of our jobs is to count how many other pages # there are and add tasks to scrape those pages num_pages = scrape_num_pages(tree) logging.info("Meet contains " + str(num_pages) + " pages ") date_str = date.strftime("%d/%m/%y") for i in range(2, num_pages + 1): logging.info("Queing update of page " + str(i) + " of " + meet_name) taskqueue.add(url='/admin/scrape_meet', params={ 'asa_meet_code': str(asa_meet_code), 'meet_name': meet_name, 'date': date_str, 'course_code': course_code, 'page': str(i) }) swimmers_checked = set() update_swimmer_list = False for row in TableRows(table, _meet_headers_of_interest): # First we look at the swimmer. # Is it one we've already seen while scraping this meet, or is it a new one? # If it's a new one, is it a swimmer that's in our database? # Perhaps it's a swimmer that's in our database as Cat 1 and needs upgrading. asa_number = int(row[0].text) if asa_number not in swimmers_checked: swimmers_checked.add(asa_number) swimmer = Swimmer.get("Winsford", asa_number) if swimmer is None: swimmer = SwimmerCat1.get("Winsford", asa_number) if swimmer is None: # This looks like a new Winsford swimmer that isn't in the database # Add a task to add them logging.info("Found new Winsford swimmer: " + str(asa_number) + ". Adding task to scrape.") taskqueue.add(url='/admin/update_swimmers', params={'name_search': str(asa_number)}) #QueueUpdateSwimsForSwimmer( str(asa_number) ) update_swimmer_list = True else: # This is a swimmer that's in our database as Cat1 # Add a task to upgrade them logging.info("Found new Cat 2 Winsford swimmer: " + str(asa_number) + ". Adding task to upgrade.") taskqueue.add(url='/admin/check_for_swimmer_upgrade', params={'asa_number': str(asa_number)}) update_swimmer_list = True else: logging.info("Found existing Winsford swimmer: " + swimmer.full_name()) if update_swimmer_list: taskqueue.add(url='/admin/update_swimmer_list') swims_for_swimmer = {} for row in TableRows(table, _meet_headers_of_interest): # Now look at the actual swims. # If there's a swim link, then that means there are some splits. In those # cases we also add a task to parse the splits and add them to the Swim. asa_number = int(row[0].text) event_str = row[1].text date_of_birth = helpers.ParseDate_dmy(row[2].text) race_time = float(RaceTime(row[3].text)) event = Event.create_from_str(event_str, course_code) asa_swim_id = get_asa_swim_id(row[3]) swim = Swim.create(asa_number, date_of_birth, event, date, meet_name, race_time, asa_swim_id) if asa_swim_id is not None: # Swim link. Add a task to parse the splits. swim_key_str = swim.create_swim_key_str() logging.info("Adding split scraping task for swim " + swim_key_str) taskqueue.add(url='/admin/scrape_splits', params={'swim': swim_key_str}) # Record this swim if asa_number not in swims_for_swimmer: swims_for_swimmer[asa_number] = [] swims_for_swimmer[asa_number].append(swim) for asa_number, swims in swims_for_swimmer.iteritems(): num_swims = len(swims) logging.info("Putting " + str(num_swims) + " swims for " + str(asa_number)) put_new_swims(asa_number, swims)
def post(self): records_for_each_gender = [{}, {}] swimmers = Swimmer.query_club("Winsford") num_event_codes = len(short_course_events) for gender_idx in range(0, 2): gender_code = gender_codes[gender_idx] is_male = (gender_idx == 0) records_for_each_age = records_for_each_gender[gender_idx] for age in range(9, 17): records_for_this_age = {} records_for_each_age[age] = records_for_this_age for event_code in range(0, num_event_codes): sc_event = short_course_events[event_code] lc_event = long_course_events[event_code] best_race_time = 9999999 best_swimmer = None best_swim = None for swimmer in swimmers: if swimmer.is_male == is_male: # Figure out this swimmer's best time at this age converted # to short course time sc_pb_time = 9999999 sc_pb_swim = Swim.fetch_pb(swimmer, sc_event, age) pb_swim = sc_pb_swim lc_pb_swim = Swim.fetch_pb(swimmer, lc_event, age) if sc_pb_swim is not None: sc_pb_time = sc_pb_swim.race_time if lc_pb_swim is not None: lc_pb_time_converted = lc_event.convertTime( lc_pb_swim.race_time) if lc_pb_time_converted < sc_pb_time: sc_pb_time = lc_pb_time_converted pb_swim = lc_pb_swim if (pb_swim is not None) and (sc_pb_time < best_race_time): # This is the best time we've seen so far best_race_time = sc_pb_time best_swimmer = swimmer best_swim = pb_swim if best_swim is not None: record = Record(age, best_swimmer, best_swim, best_race_time) records_for_this_age[event_code] = record logging.info('Record for ' + gender_code + ' ' + str(age) + ' ' + sc_event.short_name_without_course() + ': ' + str(RaceTime(best_race_time)) + ', ' + best_swimmer.full_name()) # Now tabulate and send a plain text response club_records = '' self.response.headers['Content-Type'] = 'text/plain' for gender_idx in range(0, 2): records_for_each_age = records_for_each_gender[gender_idx] gender_code = gender_codes[gender_idx] for age, records_for_age in records_for_each_age.iteritems(): for event_code, record in records_for_age.iteritems(): swimmer = record.swimmer swim = record.swim club_records += (gender_code + '^' + str(record.age) + '^' + str(event_code) + '^' + swimmer.full_name() + '^' + str(swim) + '\n') self.response.out.write(club_records) StaticData.set_club_records(club_records)