def update_scoring(self, e): '''This method updates the result and points for a given entry, e.''' if e.points is None and len(e.result) > 0: if e.result.startswith("S"): accum_value = e.result[len("Semis-"):] e.result = "Semis" placement = -2 elif e.result.startswith("q"): accum_value = e.result[len("quarters-"):] e.result = "quarters" placement = -1 elif e.result.startswith("round 3-"): accum_value = e.result[len("round 3-"):] e.result = "round 3" placement = -3 elif e.result.startswith("round 2-"): accum_value = e.result[len("round 2-"):] e.result = "round 2" placement = -5 elif e.result.startswith("round 1-"): accum_value = e.result[len("round 1-"):] e.result = "round 1" placement = -10 else: try: placement = int(e.result) accum_value = 0 except: return None e.points = calc_points(self.heat.base_value, placement, num_competitors=self.entries_in_event, rounds=self.heat.rounds, accum=int(accum_value)) #print(str(e.couple) + " finish " + e.result + " for " + str(e.points) + " points") e.save() return e.result else: return None
def process_response(self, entries, e): '''This routine processes the response returned by the form submittal. It is the scoresheet results for a single dancer. We use this to extract the results of the heat we are interested in .''' # determine heat to search for in the scoresheet if e.heat.heat_number == 0: heat_string = e.heat.extra elif len(e.heat.extra) > 0 and e.heat.extra[0] != '[': heat_string = str(e.heat.heat_number) + e.heat.extra else: heat_string = str(e.heat.heat_number) # save the level of the event (e.g. Open vs. Rising Star, Bronze, Silver, Gold, etc.) level = e.heat.base_value # set the rounds indicator to finals, until proven otherwise rounds = "F" # find the beginning of the JSON data if self.response.text[0] == "{": start_pos = 0 else: start_pos = self.response.text.find("{") try: json_data = json.loads(self.response.text[start_pos:]) except: print("Unable to parse scoresheet - " + str(e.heat)) return if json_data['Status'] == 0: # no results for this competitor print("No scoresheet") return # search all the events on this dancer's scoresheet for ev in json_data['Result']['Events']: # once we find the heat we want if ev['Heat'] == heat_string: for r in ev['Rounds']: if r['Name'] == "Final": # process final round results self.entries_in_event = 0 # don't process Solo - keep looking for heat_string if r['Scoring_Method'] == "Solos": continue # loop through all the competitors in this heat for c in r['Summary']['Competitors']: self.entries_in_event += 1 for entry in entries: # find matching entry by shirt number if str(entry.shirt_number) == c['Bib']: # This Result list is length 1, unless there are tiebreakers, so use the last item result_str = c['Result'][-1] if len(entry.result) == 0: entry.result = result_str break elif entry.result == result_str: break else: print( str(entry.heat.heat_number) + " Same number - new result: " + " " + str(entry.couple.dancer_1) + " " + str(entry.couple.dancer_2) + " " + entry.result + " " + result_str) entry.result = result_str res_error = Result_Error() res_error.comp = entry.heat.comp res_error.heat = entry.heat res_error.couple = entry.couple res_error.error = Result_Error.TWO_RESULTS_FOR_COUPLE res_error.save() break else: # late entry in final if 'Participants' in c: couple_names = self.get_couple_names( c['Participants']) else: couple_names = [] if len(couple_names) == 2: self.build_late_entry( entry.heat, shirt_number=c['Bib'], result=c['Result'][-1], couple_names=couple_names) else: entry.result = result_str res_error = Result_Error() res_error.comp = entry.heat.comp res_error.heat = entry.heat res_error.couple = None res_error.error = Result_Error.LATE_ENTRY res_error.save() # now we know the number of entries, calculate points for e in entries: if e.points is None and len(e.result) > 0: e.points = calc_points( level, int(e.result), num_competitors=self.entries_in_event, rounds=rounds) if e.points is not None: #print(e, e.result, e.points) e.save() for late_entry in self.late_entries: if late_entry.points is None: late_entry.points = calc_points( level, int(late_entry.result), num_competitors=self.entries_in_event, rounds=rounds) late_entry.save() else: # process early rounds, looking for those who were eliminated if r['Name'] == "Semi-Final": if rounds == "F": rounds = "S" result_index = -2 temp_result = "Semis" elif r['Name'] == "Quarter-Final": if rounds == "F": rounds = "Q" result_index = -1 temp_result = "quarters" elif r['Name'] == "Round 1": if rounds == "F": rounds = "R1" result_index = -10 temp_result = "round 1" elif r['Name'] == "Round 2": if rounds == "F" or rounds == "R1": rounds = "R21" result_index = -5 temp_result = "round 2" elif r['Name'] == "Round 3": if rounds == "F" or rounds == "R1" or rounds == "R21": rounds = "R321" result_index = -3 temp_result = "round 3" else: print("Unknown Round") print(r) xxx() for c in r['Summary']['Competitors']: if c['Recalled'] == 0: for entry in entries: if str(entry.shirt_number) == c['Bib']: if len( entry.result ) == 0 or entry.result == temp_result: # If the couple was not recalled, their result is the round # in which they were eliminated entry.result = temp_result # Lookup their points, and exit the loop entry.points = calc_points( level, result_index, rounds=rounds, accum=c['Total']) break else: res_error = Result_Error() res_error.comp = e.heat.comp res_error.heat = e.heat res_error.couple = e.couple res_error.error = Result_Error.TWO_RESULTS_FOR_COUPLE res_error.save() entry.result = temp_result break else: # late entry try: couple_names = self.get_couple_names( c['Participants']) if len(couple_names) == 2: points = calc_points( level, result_index, rounds=rounds, accum=c['Total']) self.build_late_entry( entry.heat, shirt_number=c['Bib'], result=temp_result, couple_names=couple_names, points=points) else: print("error in late entry names") xxx() except: print("error in late entry names")
def process_scoresheet_for_event(self, entries, url): '''This is the method that requests and processes the scoresheet for a particular heat''' # on the o2cm site, we only handle final rounds at this time looking_for_final_round = True looking_for_score_column = False looking_for_final_summary = False looking_for_final_dance = False looking_for_result_column = False looking_for_recall_column = False looking_for_finalists = False looking_for_quarterfinal = False looking_for_prelim_round = False looking_for_eliminations = False process_finalists = False looking_for_semifinal = False # on the o2cm site, the couple names are listed separately from the heat results looking_for_couple_names = False self.entries_in_event = 0 # for e in entries: # print(e) self.heat = entries.first().heat # request the scoresheet and split the response into lines response = requests.get(url) lines = response.text.splitlines() i = 0 # loop through all the lines while i < len(lines): l = lines[i] if looking_for_final_round: if 'class="h4"' in l: # once the find the title with the final results, look for the score column or summary. looking_for_score_column = True num_scores = 0 total_score = 0 looking_for_final_round = False #print("Found Final results") else: i += 1 elif looking_for_score_column: if 'class=h3' in l: if "Score" in l: next_row = False rows = l.split("</tr>") for r in rows: if next_row: fields = r.split("</td>") score_column = len(fields) - 4 score = float(fields[score_column][len("<td>"):]) num_scores += 1 total_score += score break if "Score" in r: next_row = True i += 1 else: looking_for_score_column = False looking_for_final_summary = True elif "Couples" in l: e = entries[0] e.result = 1 avg_score = total_score / num_scores e.points = round(calc_points(self.heat.base_value, placement=1, num_competitors=1, score=avg_score), 2) #print(str(e), total_score, avg_score, e.points) e.save() return 1 i += 1 else: i += 1 elif looking_for_final_summary: if 'class=h3' in l and "Summary" in l: # once the summary header is found, look for the result columen looking_for_final_summary = False looking_for_result_column = True #print("Found Summary") col_count = 0 else: i += 1 elif looking_for_result_column: #print(l) rows = l.split("</tr>") fields = rows[0].split("</td>") # count the number of columns until the result column is found if "Res" in l: # once the result column is found, process each finalist col_count = len(fields) - 2 #print("Found Results in Column", col_count) looking_for_result_column = False looking_for_finalists = True results = list() else: i += 1 # elif looking_for_recall_column: # # when looking for recalls, determine the number of columns # fields = l.split("</th>") # recall_column = len(fields) - 2 # accum_column = recall_column - 1 # i += 1 # looking_for_recall_column = False # # start looking for couples that were eliminated in the current round # looking_for_eliminations = True elif looking_for_finalists: rows = l.split("</tr>") # once the closing table tag is found we have one row per couple if "</table>" in rows[-1]: rows = rows[:-1] looking_for_finalists = False #print("Found last finalist") looking_for_couple_names = True i += 1 # after processing the final results, look for possible semifinal #looking_for_semifinal = True else: i += 1 # loop through all the couples for r in rows: # split the row into columns fields = r.split("</td>") #print(fields) if len(fields) == col_count + 1: # get the couple name and number from the first column couple_field = fields[0].split("t1b>")[1] # get the result info from the last column result_field = fields[col_count-1].split("<td>")[1] # try: # result_place = int(result_field) # except: # result_place = None #print("Couple:", couple_field, "Result:", result_field) entry={} entry["couple"] = couple_field entry["result"] = result_field #print(entry) results.append(entry) self.entries_in_event += 1 elif looking_for_couple_names: if 'class=t1b>Couples' in l: #print("Found start of couple list") #print(l) rows = l.split("<tr>") for r in rows: if 'class=t1b>Judges' in r: looking_for_couple_names = False break elif 'individual.asp' in r: # this row has a couple number and names cols = r.split("</td>") # find the shirt number of this couple to match with results start_pos = cols[0].find("t1b>") + len("t1b>") number = cols[0][start_pos:] # extract the couple names couple_names = cols[1].split(",") dancer_name = self.find_name_from_couple_table(couple_names[0]) partner_name = self.find_name_from_couple_table(couple_names[1]) # match the shirt number with the results found earlier for e in results: if e["couple"] == number: e["dancer"] = dancer_name e["partner"] = partner_name #print(e) self.process_couple(entries, dancer_name, partner_name, e["couple"], e["result"]) break i += 1 # elif looking_for_semifinal: # # if we find a semifinal, set the rounds and look for the results of this round # if 'class="roundHeader"' in l: # #print("Found semi-final") # self.heat.rounds = "S" # looking_for_semifinal = False # looking_for_final_dance = True # dance_count = 0 # else: # i += 1 # elif looking_for_quarterfinal: # # if we find a quarter final, set the rounds and look for the results of this round # if 'class="roundHeader"' in l: # #print("Found quarter-final") # self.heat.rounds = "Q" # looking_for_quarterfinal = False # looking_for_final_dance = True # dance_count = 0 # else: # i += 1 # elif looking_for_prelim_round: # # if we find an earlier round, set the rounds and look for the results of this round # if 'class="roundHeader">Third' in l: # #print("Found Third Round") # self.heat.rounds = "R3" # looking_for_prelim_round = False # looking_for_final_dance = True # dance_count = 0 # elif 'class="roundHeader">Second' in l: # #print("Found Second Round") # if self.heat.rounds != "R3": # self.heat.rounds = "R32" # else: # self.heat.rounds = "R2" # looking_for_prelim_round = False # looking_for_final_dance = True # dance_count = 0 # elif 'class="roundHeader">First' in l: # #print("Found First Round") # if self.heat.rounds == "R32": # self.heat.rounds = "R321" # elif self.heat.rounds == "R2": # self.heat.rounds = "R21" # else: # self.heat.rounds = "R1" # looking_for_prelim_round = False # looking_for_final_dance = True # dance_count = 0 # else: # i += 1 # elif looking_for_final_dance: # if 'class="eventResults"' in l: # dance_count += 1 # # TODO: some prelim rounds may not dance all the dances. Need to handle this. # if dance_count == num_dances: # looking_for_final_dance = False # looking_for_recall_column = True # i += 1 # elif looking_for_eliminations: # # once we find the results of an early round, look for earlier rounds # if "</table>" in l: # looking_for_eliminations = False # if self.heat.rounds == "S": # looking_for_quarterfinal = True # elif self.heat.rounds == "Q": # looking_for_prelim_round = True # elif self.heat.rounds == "R3": # looking_for_prelim_round = True # elif self.heat.rounds == "R2": # looking_for_prelim_round = True # else: # # process the result of the next couple # fields = l.split("</td>") # # get the couple name and shirt number # couple_field = fields[0].split("<td>")[1] # # determine if the couple was recalled # recall_place = fields[recall_column].split("<td>")[1] # if recall_place != "Recall": # # if the couple was not recalled determine how many votes they got # accum_value = fields[accum_column].split("<td>")[1] # result_str = self.temp_result(self.heat.rounds, accum_value) # # # process this couple # self.process_couple(entries, couple_field, result_str) # # i += 1 else: i+= 1 # entire scoresheet was processed # for each entry in the event, extract the recall votes and calculate the points event_result = None for e in entries: temp_result = self.update_scoring(e) if event_result is None: event_result = temp_result for late_entry in self.late_entries: temp_result = self.update_scoring(late_entry) if temp_result is not None: print("LATE ENTRY SCORING: " + late_entry.result + " " + str(late_entry.points)) if event_result is None: event_result = temp_result return event_result
def process_response(self, entries, e): '''This routine processes the response returned by the form submittal. It is the scoresheet results for a single dancer. We use this to extract the results of the heat we are interested in .''' # Build the string to find the results for the heat we want. # For example: Pro Heat 5: heat_string = e.heat.get_category_display() + " " # if the heat number is 0, leave it out if e.heat.heat_number != 0: heat_string += str(e.heat.heat_number) # if extra field was TBD or BOBR, there may be more than one, so include the heat info field if e.heat.extra in ["TBD", "BOBR"]: heat_string += e.heat.extra + ": " + e.heat.info elif len(e.heat.extra) > 0 and e.heat.extra[0].isalpha(): # if the extra field is a single character, include it (Heat 123A:) heat_string += e.heat.extra[0] + ":" else: # need the colon directly after the number, to distinguish 5 from 51, etc. heat_string += ":" heat_info_from_scoresheet = None # If there are parenthesis in the heat info, the heat has multiple dances # For example (W/T/F). # At this point, we only care about the final results of the heat, not the # individual dances. if e.heat.multi_dance(): event = "Multi-Dance" else: event = "Single Dance" # save the level of the event (e.g. Open vs. Rising Star, Bronze, Silver, Gold, etc.) level = e.heat.base_value # set the rounds indicator to finals, until proven otherwise rounds = "F" # these are state variables to keep track of where we are in the parsing looking_for_recall_column = False looking_for_result_column = False looking_for_eliminations = False looking_for_finalists = False result = None # split the response into separate lines so we can loop through them lines = self.response.text.splitlines() for line in lines: # the result column is the last in the table, but there is one # column per judge, and we don't know how many judges there are if looking_for_result_column: # find the start of a table row if "<tr>" in line: count = 0 # If we have found the result column, save which column it is # and change the state to looking_for_finalists elif "<td>" in line and "Result" in line: result_column = count looking_for_finalists = True looking_for_result_column = False self.entries_in_event = 0 count = 0 # skip past this column, it is not the last elif "<td>" in line: count += 1 # If we are processing the preliminary round results, # we look for the Recall column in the results of the last dance. # The logic is basically the same as looking for results, except # when we find that column, the next state is looking_for_eliminations elif looking_for_recall_column: if "<tr>" in line: count = 0 # this should only be true on sheets that don't identify the semi-final heat elif "<td>" in line and "Result" in line: looking_for_recall_column = False result = "Finals" elif "<td>" in line and "Recall" in line: recall_column = count accum_column = recall_column - 1 looking_for_eliminations = True looking_for_recall_column = False count = 0 if rounds == "F": temp_result = "Semis" result_index = -2 rounds = "S" print("Found Semis unexpectedly") res_error = Result_Error() res_error.comp = e.heat.comp res_error.heat = e.heat res_error.error = Result_Error.UNEXPECTED_EARLY_ROUND res_error.save() elif "<td>" in line: count += 1 # If we are processing the preliminary round results, # we want to know which entries were not recalled to the next round. elif looking_for_eliminations: if "<td>" in line: if count == 0: # This is the first column, get the competitor information. # This could be any of the entries in this heat, not necessarily # the dancer we used to submit the form current_competitor = self.get_table_data(line) count += 1 elif count == accum_column: # This column indicates how many recall votes a couple received. # If the couple was not recalled, this number affects their points for the heat. try: accum = int(self.get_table_data(line)) except: accum = 0 count += 1 elif count == recall_column: # If the data indicates the couple was recalled, we can ignore them, # as we will get their results in the next round. # If the couple was not recalled, we need to capture those results if self.get_table_data(line) != "Recall": # extract the shirt number from the scoresheet #couple_names = self.get_couple_names(current_competitor) shirt_number = self.get_shirt_number( current_competitor) # try to find this couple from the scoresheet in the original heat report for e in entries: if e.shirt_number == shirt_number: if len(e.result) == 0: # If the couple was not recalled, their result is the round # in which they were eliminated e.result = temp_result # Lookup their points, and exit the loop e.points = calc_points(level, result_index, rounds=rounds, accum=accum) break elif e.result == temp_result: break else: print( str(e.heat.heat_number) + " Same shirt # - new result: " + str(e.couple) + " " + e.result + " " + temp_result + " " + str(accum)) res_error = Result_Error() res_error.comp = e.heat.comp res_error.heat = e.heat res_error.couple = e.couple res_error.error = Result_Error.TWO_RESULTS_FOR_COUPLE res_error.save() e.result = temp_result # If we get here, we didn't find an entry on the heatsheet that matches # this line on the scoresheet. This is the dreaded late entry. else: # Build a structure for the late entry couple with the results couple_names = self.get_couple_names( current_competitor) points = calc_points(level, result_index, rounds=rounds, accum=accum) self.build_late_entry( e.heat, shirt_number=shirt_number, result=temp_result, couple_names=couple_names, points=points) # reset the count to prepare for the next line of the scoresheet count = 0 else: # skip this column, it is not the recall column count += 1 elif "</table>" in line: # once we get to the end of the table, there are no more entries to process looking_for_eliminations = False # When we are looking for finalists, the logic is similar to looking for eliminations elif looking_for_finalists: num_competitors = len(entries) if "<td>" in line: if count == 0: current_competitor = self.get_table_data(line) self.entries_in_event += 1 count += 1 elif count == result_column: # When we get to the result column, we want to extract the number that indicates # the finishing position of this couple in this heat. # Need to check for non-digit, as the result could include a tiebreaker rule # For example: 3(R11) means they finished in 3rd place. result_field = self.get_table_data(line) index = 0 while index < len(result_field) and result_field[ index] in string.digits: index += 1 result_place = int(result_field[:index]) #couple_names = self.get_couple_names(current_competitor) shirt_number = self.get_shirt_number( current_competitor) # loop through all entries on the heatsheet to find a match for e in entries: if e.shirt_number == shirt_number: if len(e.result) == 0: e.result = str(result_place) break elif e.result == str(result_place): break else: print( str(e.heat.heat_number) + " Same number - new result: " + " " + str(e.couple.dancer_1) + " " + str(e.couple.dancer_2) + " " + e.result + " " + str(result_place)) e.result = str(result_place) res_error = Result_Error() res_error.comp = e.heat.comp res_error.heat = e.heat res_error.couple = e.couple res_error.error = Result_Error.TWO_RESULTS_FOR_COUPLE res_error.save() break else: # this code runs when competitor not found in heat couple_names = self.get_couple_names( current_competitor) if len(couple_names) > 1: self.build_late_entry( e.heat, shirt_number=shirt_number, result=str(result_place), couple_names=couple_names) else: print("Error in couple " + " " + str(couple_names)) # reset for next line of the scoresheet count = 0 else: # skip past this column count += 1 # When we see the closing table tag, we are done with this heat. elif "</table>" in line: looking_for_finalists = False for e in entries: if e.points is None and len(e.result) > 0: e.points = calc_points( level, int(e.result), num_competitors=self.entries_in_event, rounds=rounds) if e.points is not None: #print(e, e.result, e.points) e.save() for late_entry in self.late_entries: if late_entry.points is None: late_entry.points = calc_points( level, int(late_entry.result), num_competitors=self.entries_in_event, rounds=rounds) late_entry.save() #print("LATE ENTRY SCORING: " + late_entry.result + " " + str(late_entry.points)) break # We get here if we aren't in any of the "looking" states # Note: These scoresheets list the preliminary rounds first, ending with the final round # If this check is true, we found first round results for this heat elif heat_string in line and "First Round" in line and ( "<p>" in line or "<h3>" in line): temp_result = "round 1" # indicate which round we are in result_index = -10 # use this to pull values from the points table rounds = "R1" heat_info_from_scoresheet = self.get_heat_info( line, heat_string, "First Round") looking_for_recall_column = True # enter the next state # If this check is true, we found second round results for this heat elif heat_string in line and "Second Round" in line and ( "<p>" in line or "<h3>" in line): temp_result = "round 2" # indicate which round we are in result_index = -5 # use this to pull values from the points table rounds = "R21" heat_info_from_scoresheet = self.get_heat_info( line, heat_string, "Second Round") looking_for_recall_column = True # enter the next state # If this check is true, we found third round results for this heat elif heat_string in line and "Third Round" in line and ( "<p>" in line or "<h3>" in line): temp_result = "round 3" # indicate which round we are in result_index = -53 # use this to pull values from the points table rounds = "R321" heat_info_from_scoresheet = self.get_heat_info( line, heat_string, "Third Round") looking_for_recall_column = True # enter the next state # If this check is true, we found quarter-final results for this heat elif heat_string in line and "QUARTER" in line.upper( ) and "FINAL" in line.upper() and ("<p>" in line or "<h3>" in line): temp_result = "quarters" # indicate which round we are in result_index = -1 # use this to pull values from the points table #print("Found Quarterfinals") # if we haven't seen a prelim round, set rounds indicator to quarters if rounds == "F": rounds = "Q" heat_info_from_scoresheet = self.get_heat_info( line, heat_string, "Quarter-final") looking_for_recall_column = True # enter the next state # If this check is true, we found Semi-final results for this heat elif heat_string in line and "SEMI-FINAL" in line.upper() and ( "<p>" in line or "<h3>" in line): temp_result = "Semis" result_index = -2 #print("Found Semifinals") if rounds == "F": rounds = "S" heat_info_from_scoresheet = self.get_heat_info( line, heat_string, "Semi-final") looking_for_recall_column = True # If this check is true, we found the Final results for this heat elif heat_string in line and ("<p>" in line or "<h3>" in line): # and "Final" in line: #print("Found " + heat_string) heat_info_from_scoresheet = self.get_heat_info( line, heat_string, "Final") # if this is a single dance event, we can look for the results now if event == "Single Dance": result = "Finals" #print("Found Finals - single dance") looking_for_result_column = True else: looking_for_recall_column = True # this may not be the final on some websites # If this is the Final of a Multi-Dance event, we process the Final Summary elif result == "Finals" and "Final summary" in line and ( "<p>" in line or "<h3>" in line): if event == "Multi-Dance": #print("Found Finals") looking_for_result_column = True elif "Place" in line and "<th>" in line: print("Found a table header with Place " + line) # Return which level of results we were able to find on this dancer's scoresheet # If they were eliminated before the finals, the final results will not appear, # and the calling routine will have to try another dancer to get those. return result