def opt_wp_enter(opt, t1, enter): # search for point that opt line crosses enter cylinder bearing = calcBearing(opt.lat, opt.lon, t1.lat, t1.lon) dist = geod.Inverse(opt.lat, opt.lon, t1.lat, t1.lon)['s12'] point = opt found = False d1 = dist / 2 direction = '' while not found: p = geod.Direct(opt.lat, opt.lon, bearing, dist) point = Turnpoint(lat=p['lat2'], lon=p['lon2'], type='optimised', radius=0, shape='optimised', how='optimised') dist_from_centre = int(c_round(geod.Inverse(point.lat, point.lon, enter.lat, enter.lon)['s12'])) if dist_from_centre > enter.radius: if direction != 'plus': d1 = d1 / 2 # if we change direction halve the increment dist = dist + d1 direction = 'plus' elif dist_from_centre < enter.radius: if direction != 'minus': d1 = d1 / 2 # if we change direction halve the increment dist = dist - d1 direction = 'minus' else: found = True return point
def avail_validity(self): if len(self.tasks) > 0: if self.formula.overall_validity == 'ftv': return c_round(self.total_validity * self.formula.validity_param, 4) else: return self.total_validity return 0
def rawtime_float_to_hms(timef): """Converts time from floating point seconds to hours/minutes/seconds. Args: timef: A floating point time in seconds to be converted Returns: A namedtuple with hours, minutes and seconds elements """ time = int(c_round(timef)) hms = namedtuple('hms', ['hours', 'minutes', 'seconds']) return hms(math.floor(time / 3600), math.floor((time % 3600) / 60), math.floor(time % 60))
def tp_time_civl(fix, next, tp): """return correct time of achieving a turnpoint based on CIVL rules""" ''' The time of a crossing depends on whether it actually cuts across the actual cylinder, or whether both points lie within the tolerance band, but on the same side of the actual cylinder. (distance(center , trackpoint[j] ) < radius ∧ distance(center , trackpoint[j+1] ) < radius ) ∨ (distance(center , trackpoint[j] ) > radius ∧ distance(center , trackpoint[j+1] ) > radius ) ∧ turnpoint = ESS: crossing.time = trackpoint[j+1].time (distance(center , trackpoint[j] ) < radius ∧ distance(center , trackpoint[j+1] ) < radius ) ∨ (distance(center , trackpoint[j] ) > radius ∧ distance(center , trackpoint[j+1] ) > radius ) ∧ turnpoint ≠ ESS: crossing.time = trackpoint[j].time (distance(center , trackpoint[j] ) < radius ∧ distance(center , trackpoint[j+1] ) > radius ) ∨ (distance(center , trackpoint[j] ) > radius ∧ distance(center , trackpoint[j+1] ) < radius ) crossing.time = interpolateTime(trackpoint[j],trackpoint[j+1]) ''' if ((tp.in_radius(fix, 0, 0) and tp.in_radius(next, 0, 0)) or (not (tp.in_radius(fix, 0, 0)) and not (tp.in_radius(next, 0, 0)))): return fix.rawtime if tp.type != 'endspeed' else next.rawtime else: '''interpolate time: Will use distance from radius of the two points, and the proportion of times''' d1 = abs(distance(tp, fix) - tp.radius) d2 = abs(distance(tp, next) - tp.radius) speed = (d1 + d2) / (next.rawtime - fix.rawtime) t = c_round((fix.rawtime + d1 / speed), 2) return t
def get_final_scores(self, d=0): """calculate final scores depending on overall validity: - all: sum of all tasks results - round: task discard every [param] tasks - ftv: calculate task scores and total score based on FTV [param] input: results: participant list tasks: tasks list formula: comp formula dict d: decimals on single tasks score, default 0 """ from calcUtils import c_round val = self.formula.overall_validity param = self.formula.validity_param avail_validity = self.avail_validity for pil in self.results: pil['score'] = 0 ''' if we score all tasks, or tasks are not enough to ha discards, or event has just one valid task regardless method, we can simply sum all score values ''' if not ((val == 'all') or (val == 'round' and self.dropped_tasks == 0) or (len(self.tasks) < 2)): '''create a ordered list of results, perf desc''' # sorted_results = sorted(pil['results'], key=lambda x: (x[1]['perf'], x[1]['pre']), reverse=True) sorted_results = sorted( [x for x in pil.items() if isinstance(x[1], dict)], key=lambda x: (x[1]['perf'], x[1]['pre']), reverse=True) if val == 'round' and len(self.tasks) >= param: '''we need to order by score desc and sum only the ones we need''' for i in range(self.dropped_tasks): idx = sorted_results.pop()[ 0] # getting id of worst result task pil[idx]['score'] = 0 elif val == 'ftv' and len(self.tasks) > 1: '''ftv calculation''' pval = avail_validity for idx, s in sorted_results: if not (pval > 0): pil[idx]['score'] = 0 else: '''get ftv_validity of corresponding task''' tval = next(t.ftv_validity for t in self.tasks if t.task_code == idx) if pval > tval: '''we can use the whole score''' pval -= tval else: '''we need to calculate proportion''' pil[idx]['score'] = c_round( pil[idx]['score'] * (pval / tval), d) pval = 0 '''calculates final pilot score''' pil['score'] = sum(pil[x]['score'] for x in pil.keys() if isinstance(pil[x], dict)) ''' order list''' self.results = sorted(self.results, key=lambda x: x['score'], reverse=True)
def create_results(comp_id, status=None, decimals=None, name_suffix=None): """creates the json result file and the database entry :param name_suffix: optional name suffix to be used in filename. This is so we can overwrite comp results that are only used in front end to create competition page not display results """ from calcUtils import c_round comp = Comp.read(comp_id) '''PARAMETER: decimal positions''' if decimals is None or not isinstance(decimals, int): decimals = comp.formula.comp_result_decimal '''retrieve active task result files and reads info''' files = get_tasks_result_files(comp_id) '''initialize obj attributes''' comp.participants = get_participants(comp_id) ''' get rankings ''' comp.get_rankings() for idx, t in enumerate(files): task = Task.create_from_json(task_id=t.task_id, filename=t.file) comp.tasks.append(task) ''' task validity (if not using ftv, ftv_validity = day_quality)''' r = task.ftv_validity * 1000 '''get pilots result''' for p in comp.participants: res = next((d for d in comp.results if d.get('par_id', None) == p.par_id), False) if res is False: ''' create pilot result Dict (once)''' # comp.results.append({'par_id': p.par_id, 'results': []}) comp.results.append({'par_id': p.par_id}) res = comp.results[-1] s = next((c_round(pil.score or 0, decimals) for pil in task.pilots if pil.par_id == p.par_id), 0) if r > 0: # sanity perf = c_round(s / r, decimals + 3) # res['results'].append({task.task_code: {'pre': s, 'perf': perf, 'score': s}}) res.update( {task.task_code: { 'pre': s, 'perf': perf, 'score': s }}) else: # res['results'].append({task.task_code: {'pre': s, 'perf': 0, 'score': 0}}) res.update( {task.task_code: { 'pre': s, 'perf': 0, 'score': 0 }}) '''calculate final score''' comp.get_final_scores(decimals) ''' create result elements''' results = [] for res in comp.results: '''create results dict''' p = next(x for x in comp.participants if x.par_id == res['par_id']) r = { x: getattr(p, x) for x in CompResult.result_list if x in dir(p) } r['score'] = res['score'] r['results'] = { x: res[x] for x in res.keys() if isinstance(res[x], dict) } results.append(r) '''create json file''' result = { 'info': {x: getattr(comp, x) for x in CompResult.info_list}, 'rankings': comp.rankings, 'tasks': [{x: getattr(t, x) for x in CompResult.task_list} for t in comp.tasks], 'results': results, 'formula': {x: getattr(comp.formula, x) for x in CompResult.formula_list}, 'stats': {x: getattr(comp, x) for x in CompResult.stats_list} } ref_id, filename, timestamp = create_json_file(comp_id=comp.id, task_id=None, code=comp.comp_code, elements=result, status=status, name_suffix=name_suffix) return comp, ref_id, filename, timestamp
def total_validity(self): if len(self.tasks) > 0: return c_round(sum([t.ftv_validity for t in self.tasks]), 4) else: return 0
def to_file(self, participants_fsdb: bool = False): """ returns: - filename: STR - fsdb: FSDB xml data, to be used in frontend.""" formula = self.comp.formula pilots = self.comp.participants '''create dicts of attributes for each section''' comp_attr = { 'id': '', # still to do 'name': self.comp.comp_name, 'location': self.comp.comp_site, 'from': self.comp.date_from, 'to': self.comp.date_to, 'utc_offset': self.comp.time_offset / 3600, 'discipline': self.comp.comp_class.lower(), 'ftv_factor': c_round(1 - formula.validity_param, 4), 'fai_sanctioning': (1 if self.comp.sanction == 'FAI 1' else 2 if self.comp.sanction == 'FAI 2' else 0) } formula_attr = { 'id': formula.formula_name, 'min_dist': km(formula.min_dist, 1), 'nom_dist': km(formula.nominal_dist, 1), 'nom_time': formula.nominal_time / 3600, 'nom_launch': formula.nominal_launch, 'nom_goal': formula.nominal_goal, 'day_quality_override': 0, # still to implement 'bonus_gr': formula.glide_bonus, 'jump_the_gun_factor': (0 if formula.max_JTG == 0 else c_round( 1 / formula.JTG_penalty_per_sec, 1)), 'jump_the_gun_max': formula.max_JTG, 'normalize_1000_before_day_quality': 0, # still to implement 'time_points_if_not_in_goal': c_round(1 - formula.no_goal_penalty, 1), 'use_1000_points_for_max_day_quality': 0, # still to implement 'use_arrival_position_points': 1 if formula.formula_arrival == 'position' else 0, 'use_arrival_time_points': 1 if formula.formula_arrival == 'time' else 0, 'use_departure_points': 1 if formula.formula_departure == 'departure' else 0, 'use_difficulty_for_distance_points': 1 if formula.formula_distance == 'difficulty' else 0, 'use_distance_points': 1 if formula.formula_distance != 'off' else 0, 'use_distance_squared_for_LC': 1 if formula.comp_class == 'PG' else 0, # still to implement 'use_leading_points': 1 if formula.formula_departure == 'leadout' else 0, 'use_semi_circle_control_zone_for_goal_line': 1, # still to implement 'use_time_points': 1 if formula.formula_time == 'on' else 0, 'scoring_altitude': 'GPS' if formula.scoring_altitude == 'GPS' else 'QNH', 'final_glide_decelerator': 'none' if formula.arr_alt_bonus == 0 else 'aatb', 'no_final_glide_decelerator_reason': '', 'min_time_span_for_valid_task': 60 if self.comp_class == 'PG' else 0, # still to implement 'score_back_time': formula.score_back_time / 60, 'use_proportional_leading_weight_if_nobody_in_goal': '', # still to implement 'leading_weight_factor': (0 if formula.formula_departure != 'leadout' else c_round( formula.lead_factor, 3)), 'turnpoint_radius_tolerance': formula.tolerance, 'use_arrival_altitude_points': 0 if formula.arr_alt_bonus == 0 else '' # still to implement } if formula.arr_alt_bonus > 0: formula_attr['aatb_factor'] = c_round(formula.arr_alt_bonus, 3) '''create the file structure''' root = ET.Element('Fs') root.set('version', '3.5') root.set('comment', 'generated by AirScore') '''FsCompetition''' comp = ET.SubElement(root, 'FsCompetition') for k, v in comp_attr.items(): comp.set(k, str(v)) formula = ET.SubElement(comp, 'FsScoreFormula') for k, v in formula_attr.items(): formula.set(k, str(v)) notes = ET.SubElement(comp, 'FsCompetitionNotes') notes.text = CDATA('Generated by AirScore') # notes.text = '<![CDATA[Generated by AirScore]]>' '''FsParticipants''' participants = ET.SubElement(comp, 'FsParticipants') for p in pilots: pil = ET.SubElement(participants, 'FsParticipant') pilot_attr = { 'id': p.ID or p.par_id, 'name': p.name, 'birthday': p.pilot_birthdate_str, 'glider': p.glider, 'glider_main_colors': '', 'fai_licence': 1 if p.fai_id else 0, 'female': p.female, 'nat_code_3166_a3': p.nat, 'sponsor': p.sponsor, 'CIVLID': p.civl_id, } custom_attr = { 'fai_n': p.fai_id, 'class': p.glider_cert, 'team': p.team, 'LIVE': p.live_id } for k, v in pilot_attr.items(): pil.set(k, str(v)) cus = ET.SubElement(pil, 'FsCustomAttributes') for k, v in custom_attr.items(): sub = ET.SubElement(cus, 'FsCustomAttribute') sub.set('name', k) sub.set('value', str(v)) if not participants_fsdb: '''FsTasks''' tasks = ET.SubElement(comp, 'FsTasks') for idx, t in enumerate(self.tasks): task = ET.SubElement(tasks, 'FsTask') task.set('id', str(idx + 1)) task.set('name', t.task_name) task.set('tracklog_folder', '') task_f = ET.SubElement(task, 'FsScoreFormula') task_d = ET.SubElement(task, 'FsTaskDefinition') task_s = ET.SubElement(task, 'FsTaskState') task_p = ET.SubElement(task, 'FsParticipants') task_sp = ET.SubElement(task, 'FsTaskScoreParams') # tf = dict(t.formula.to_dict(), **t.stats) '''FsTaskState''' task_s.set( 'task_state', ('REGULAR' if not t.stopped_time else 'STOPPED')) # ? task_s.set('score_back_time', str(t.formula.score_back_time / 60)) task_s.set('cancel_reason', t.comment) '''FsScoreFormula''' # we permit just few changes in single tasks from comp formula, so we just update those tf_attr = formula_attr tf_attr.update({ 'jump_the_gun_factor': (0 if not t.formula.JTG_penalty_per_sec else round( 1 / t.formula.JTG_penalty_per_sec, 1)), 'time_points_if_not_in_goal': 1 - t.formula.no_goal_penalty, 'use_arrival_position_points': 1 if t.formula.arrival == 'position' else 0, 'use_arrival_time_points': 1 if t.formula.arrival == 'time' else 0, 'use_departure_points': 1 if t.formula.departure == 'departure' else 0, 'use_difficulty_for_distance_points': 1 if t.formula.distance == 'difficulty' else 0, 'use_distance_points': 0 if t.formula.distance == 'off' else 1, 'use_leading_points': 0 if t.formula.departure == 'off' else 1, 'use_time_points': 0 if t.formula.time == 'off' else 1, 'scoring_altitude': 'GPS' if t.formula.scoring_altitude == 'GPS' else 'QNH', 'final_glide_decelerator': 'none' if t.formula.arr_alt_bonus == 0 else 'aatb', 'use_arrival_altitude_points': 0 if t.formula.arr_alt_bonus == 0 else 1, 'turnpoint_radius_tolerance': t.formula.tolerance, }) for k, v in tf_attr.items(): task_f.set(k, str(v)) '''FsTaskDefinition''' tps = t.turnpoints td_attr = { 'ss': [i + 1 for i, tp in enumerate(tps) if tp.type == 'speed'].pop(0), 'es': [ i + 1 for i, tp in enumerate(tps) if tp.type == 'endspeed' ].pop(0), 'goal': next(tp.shape for tp in tps if tp.type == 'goal').upper(), 'groundstart': 0, # still to implement 'qnh_setting': 1013.25 # still to implement } for k, v in td_attr.items(): task_d.set(k, str(v)) t_open = get_isotime(t.date, t.window_open_time, t.time_offset) t_close = get_isotime(t.date, t.task_deadline, t.time_offset) ss_open = get_isotime(t.date, t.start_time, t.time_offset) if t.start_close_time: ss_close = get_isotime(t.date, t.start_close_time, t.time_offset) else: ss_close = t_close if t.window_close_time: w_close = get_isotime(t.date, t.window_close_time, t.time_offset) else: w_close = ss_close for i, tp in enumerate(tps): task_tp = ET.SubElement(task_d, 'FsTurnpoint') tp_attr = { 'id': tp.name, 'lat': round(tp.lat, 5), 'lon': round(tp.lon, 5), 'altitude': tp.altitude, 'radius': tp.radius, 'open': t_open if i < (td_attr['ss'] - 1) else ss_open, 'close': w_close if i == 0 else ss_close if i == (td_attr['ss'] - 1) else t_close } for k, v in tp_attr.items(): task_tp.set(k, str(v)) '''we add also FsTaskDistToTp during tp iteration''' sp_dist = ET.SubElement(task_sp, 'FsTaskDistToTp') sp_dist.set('tp_no', str(i + 1)) sp_dist.set('distance', str(t.partial_distance[i])) '''add start gates''' gates = 1 if t.SS_interval > 0: gates += t.start_iteration for i in range(gates): task_sg = ET.SubElement(task_d, 'FsStartGate') intv = 0 if not t.SS_interval else t.SS_interval * i i_time = get_isotime(t.date, (t.start_time + intv), t.time_offset) task_sg.set('open', str(i_time)) '''FsTaskScoreParams''' launch_ess = [ t.partial_distance[i] for i, tp in enumerate(t.turnpoints) if tp.type == 'endspeed' ].pop() sp_attr = { 'ss_distance': km(t.SS_distance), 'task_distance': km(t.opt_dist), 'launch_to_ess_distance': km(launch_ess), 'no_of_pilots_present': t.pilots_present, 'no_of_pilots_flying': t.pilots_launched, 'no_of_pilots_lo': t.pilots_launched - t.pilots_goal, 'no_of_pilots_reaching_nom_dist': len([ x for x in t.valid_results if x.distance_flown > t.formula.nominal_dist ]), 'no_of_pilots_reaching_es': t.pilots_ess, 'no_of_pilots_reaching_goal': t.pilots_goal, 'sum_flown_distance': km(t.tot_distance_flown), 'best_dist': km(t.max_distance or 0), 'best_time': round((t.fastest or 0) / 3600, 14), 'worst_time': round( max((x.ESS_time or 0) - (x.SSS_time or 0) for x in t.valid_results) / 3600, 14), 'no_of_pilots_in_competition': len(self.comp.participants), 'no_of_pilots_landed_before_stop': 0 if not t.stopped_time else t.pilots_landed, 'sum_dist_over_min': km(t.tot_dist_over_min), 'sum_real_dist_over_min': km(t.tot_dist_over_min), # not yet implemented 'best_real_dist': km(t.max_distance), # not yet implemented 'last_start_time': get_isotime( t.date, max([ x.SSS_time for x in t.valid_results if x.SSS_time is not None ]), t.time_offset), 'first_start_time': ('' if not t.min_dept_time else get_isotime( t.date, t.min_dept_time, t.time_offset)), 'first_finish_time': ('' if not t.min_ess_time else get_isotime( t.date, t.min_ess_time, t.time_offset)), 'max_time_to_get_time_points': round(0 / 3600, 14), # not yet implemented 'no_of_pilots_with_time_points': len([x for x in t.valid_results if x.time_score > 0]), 'goal_ratio': (0 if t.pilots_launched == 0 else round( t.pilots_goal / t.pilots_launched, 15)), 'arrival_weight': 0 if t.arrival == 0 else c_round(t.arr_weight, 3), 'departure_weight': 0 if t.departure != 'on' else c_round(t.dep_weight, 3), 'leading_weight': 0 if t.departure != 'leadout' else c_round( t.dep_weight, 3), 'time_weight': 0 if t.arrival == 'off' else c_round(t.time_weight, 3), 'distance_weight': c_round(t.dist_weight, 3), # not yet implemented 'smallest_leading_coefficient': round(t.min_lead_coeff, 14), 'available_points_distance': round(t.avail_dist_points, 14), 'available_points_time': round(t.avail_time_points, 14), 'available_points_departure': (0 if not t.formula.departure == 'departure' else round( t.avail_dep_points, 14)), 'available_points_leading': (0 if not t.formula.departure == 'leadout' else round( t.avail_dep_points, 14)), 'available_points_arrival': round(t.avail_arr_points, 14), 'time_validity': c_round(t.time_validity, 3), 'launch_validity': c_round(t.launch_validity, 3), 'distance_validity': c_round(t.dist_validity, 3), 'stop_validity': c_round(t.stop_validity, 3), 'day_quality': c_round(t.day_quality, 3), 'ftv_day_validity': t.ftv_validity, 'time_points_stop_correction': 0 # not yet implemented } for k, v in sp_attr.items(): task_sp.set(k, str(v)) '''FsParticipants''' for i, pil in enumerate(t.pilots): '''create pilot result for the task''' pil_p = ET.SubElement(task_p, 'FsParticipant') pil_p.set('id', str(pil.ID or pil.par_id)) if not (pil.result_type in ('abs', 'dnf', 'nyp')): '''only if pilot flew''' pil_fd = ET.SubElement(pil_p, 'FsFlightData') pil_r = ET.SubElement(pil_p, 'FsResult') if not (pil.result_type in ['mindist', 'min_dist']): fd_attr = { 'distance': km(pil.distance_flown), 'bonus_distance': km(pil.distance), # ?? seems 0 for PG and more than dist for HG 'started_ss': '' if not pil.real_start_time else get_isotime( t.date, pil.real_start_time, t.time_offset), 'finished_ss': '' if not pil.ESS_time else get_isotime( t.date, pil.ESS_time, t.time_offset), 'altitude_at_ess': pil.ESS_altitude, 'finished_task': '' if not pil.goal_time else get_isotime( t.date, pil.goal_time, t.time_offset), 'tracklog_filename': pil.track_file, 'lc': pil.lead_coeff, 'iv': pil.fixed_LC or '', 'ts': get_isotime(t.date, pil.first_time, t.time_offset), 'alt': pil.last_altitude, # ?? 'bonus_alt': '', # ?? not implemented 'max_alt': pil.max_altitude, 'last_tracklog_point_distance': '', # not implemented yet 'bonus_last_tracklog_point_distance': '', # ?? not implemented 'last_tracklog_point_time': get_isotime(t.date, pil.landing_time, t.time_offset), 'last_tracklog_point_alt': pil.landing_altitude, 'landed_before_deadline': '1' if pil.landing_time < (t.task_deadline if not t.stopped_time else t.stopped_time) else '0', 'reachedGoal': 1 if pil.goal_time else 0 # only deadline? } for k, v in fd_attr.items(): pil_fd.set(k, str(v)) r_attr = { 'rank': i + 1, # not implemented, they should be ordered tho # Rank IS NOT SAFE (I guess) 'points': c_round(pil.score), 'distance': km(pil.total_distance if pil. total_distance else pil.distance_flown), 'ss_time': '' if not pil.ss_time else sec_to_time( pil.ss_time).strftime('%H:%M:%S'), 'finished_ss_rank': '' if not pil.ESS_time and pil.ESS_rank else pil.ESS_rank, 'distance_points': 0 if not pil.distance_score else c_round( pil.distance_score, 1), 'time_points': 0 if not pil.time_score else c_round( pil.time_score, 1), 'arrival_points': 0 if not pil.arrival_score else c_round( pil.arrival_score, 1), 'departure_points': 0 if not t.formula.departure == 'departure' else c_round(pil.departure_score, 1), 'leading_points': 0 if not t.formula.departure == 'leadout' else c_round(pil.departure_score, 1), 'penalty': 0 if not [ n for n in pil.notifications if n.percentage_penalty > 0 ] else max(n.percentage_penalty for n in pil.notifications), 'penalty_points': 0 if not [ n for n in pil.notifications if n.flat_penalty > 0 ] else max(n.flat_penalty for n in pil.notifications), 'penalty_reason': '; '.join([ n.comment for n in pil.notifications if n.flat_penalty + n.percentage_penalty > 0 and not n.notification_type == 'jtg' ]), 'penalty_points_auto': sum(n.flat_penalty for n in pil.notifications if n.notification_type == 'jtg'), 'penalty_reason_auto': '' if not [ n for n in pil.notifications if n.notification_type == 'jtg' ] else next( n for n in pil.notifications if n.notification_type == 'jtg').flat_penalty, 'penalty_min_dist_points': 0, # ?? 'got_time_but_not_goal_penalty': (pil.ESS_time or 0) > 0 and not pil.goal_time, 'started_ss': '' if not pil.real_start_time else get_isotime( t.date, pil.SSS_time, t.time_offset), 'finished_ss': '' if not pil.ESS_time else get_isotime( t.date, pil.ESS_time, t.time_offset), 'ss_time_dec_hours': 0 if not pil.ESS_time else round( pil.ss_time / 3600, 14), 'ts': get_isotime(t.date, pil.first_time, t.time_offset), # flight origin time 'real_distance': km(pil.distance_flown), 'last_distance': '', # ?? last fix distance? 'last_altitude_above_goal': pil.last_altitude, 'altitude_bonus_seconds': 0, # not implemented 'altitude_bonus_time': sec_to_time(0).strftime( '%H:%M:%S'), # not implemented 'altitude_at_ess': pil.ESS_altitude, 'scored_ss_time': ('' if not pil.ss_time else sec_to_time( pil.ss_time).strftime('%H:%M:%S')), 'landed_before_stop': t.stopped_time and pil.landing_time < t.stopped_time } for k, v in r_attr.items(): pil_r.set(k, str(v)) '''creates the file to store''' fsdb = ET.tostring(root, pretty_print=True, xml_declaration=True, encoding='UTF-8') return self.filename, fsdb
def _get_livetracking(taskid): from livetracking import get_live_json from calcUtils import sec_to_string, time_to_seconds, c_round from datetime import datetime result_file = get_live_json(int(taskid)) formatted = frontendUtils.get_pretty_data(result_file) timestamp = result_file['file_stats']['timestamp'] offset = result_file['info']['time_offset'] formatted['headers'] = result_file['headers'] updated = datetime.fromtimestamp(timestamp + (offset or 0)) formatted['file_stats'][ 'updated'] = f"Updated at {updated.strftime('%H:%M:%S')} Local Time" if result_file['data']: rawtime = time_to_seconds(datetime.fromtimestamp(timestamp).time()) results = [] goal = [p for p in result_file['data'] if p['goal_time']] results.extend(sorted(goal, key=lambda k: k['ss_time'])) ess = [ p for p in result_file['data'] if p['ESS_time'] and not p['goal_time'] ] results.extend(sorted(ess, key=lambda k: k['ss_time'])) others = [p for p in result_file['data'] if not p['ESS_time']] results.extend( sorted(others, key=lambda k: k['distance'], reverse=True)) data = [] for idx, el in enumerate(results, 1): status = '' res = '' '''status, time or distance''' if el['first_time'] is None: '''not launched''' status = '[not launched yet]' elif el['ESS_time']: val = sec_to_string(el['ss_time']) res = f"<del>{val}</del>" if not el[ 'goal_time'] else f"<b>{val}</b>" else: res = str(c_round(el['distance'] / 1000, 2)) + ' Km' if el['distance'] > 500 else '' '''display status or altitude and height if reading is correct''' if el['landing_time']: status = '[landed]' elif -100 < el['last_altitude'] < 10000: status = f"{el['last_altitude']} m. [{el['height']} agl]" else: status = 'unreliable altitude reading' '''notifications''' if el['notifications']: comment = '; '.join( [n['comment'].split('.')[0] for n in el['notifications']]) res = f"<span class='warning'>{res}</span>" else: comment = '' '''delay''' if not (el['landing_time'] or el['goal_time']) and el['last_time'] and rawtime - el[ 'last_time'] > 120: # 2 mins old if rawtime - el['last_time'] > 600: # 10 minutes old status = f"disconnected" else: m, s = divmod(rawtime - el['last_time'], 60) status = f"[{m:02d}:{s:02d} old]" time = sec_to_string(el['last_time'], offset) if el['last_time'] else '' p = dict(rank=idx, id=el['ID'], name=f"<span class='sex-{el['sex']}'>{el['name']}</span>", sex=el['sex'], result=res, comment=comment, time=time, status=status) data.append(p) formatted['data'] = data return formatted
def get_fsdb_info(formula, form): formula.formula_name = form.get('id') '''scoring parameters''' # formula.comp_class = comp.comp_class formula.min_dist = 0 + float( form.get('min_dist')) * 1000 # min. distance, meters formula.nominal_dist = 0 + float( form.get('nom_dist')) * 1000 # nom. distance, meters formula.nominal_time = 0 + int( float(form.get('nom_time')) * 3600) # nom. time, seconds formula.nominal_launch = 0 + float( form.get('nom_launch')) # nom. launch, perc / 100 formula.nominal_goal = 0 + float( form.get('nom_goal')) # nom. goal, perc / 100 formula.scoring_altitude = 'GPS' if form.get( 'scoring_altitude') == 'GPS' else 'QNH' # print(f"min. dist.: {float(form.get('min_dist'))} - {formula.min_dist}") # print(f"nom. dist.: {float(form.get('nom_dist'))} - {formula.nominal_dist}") # print(f"Altitude.: {formula.scoring_altitude}") '''formula parameters''' # distance point: on, difficulty, off formula.formula_distance = ( 'difficulty' if form.get('use_difficulty_for_distance_points') == '1' else 'on' if form.get('use_distance_points') == '1' else 'off') # arrival points: position, time, off formula.formula_arrival = ( 'position' if form.get('use_arrival_position_points') == '1' else 'time' if form.get('use_arrival_time_points') == '1' else 'off') # departure points: leadout, on, off formula.formula_departure = ( 'leadout' if form.get('use_leading_points') == '1' else 'on' if form.get('use_departure_points') == '1' else 'off') # time points: on, off formula.formula_time = 'on' if form.get( 'use_time_points') == '1' else 'off' # leading points factor: probably needs to be linked to GAP version formula.lead_factor = (None if form.get('use_leading_points') == '0' else float( form.get('leading_weight_factor') if form. get('leading_weight_factor') else 1)) '''tolerance''' formula.tolerance = 0.0 + float( form.get('turnpoint_radius_tolerance') if form.get( 'turnpoint_radius_tolerance') else 0.001) # tolerance, perc / 100 '''stopped task parameters''' formula.validity_min_time = 0 + int( form.get('min_time_span_for_valid_task') ) * 60 # min. time for valid task, seconds formula.score_back_time = 0 + int( form.get('score_back_time')) * 60 # Scoreback Time, seconds formula.glide_bonus = 0.0 + float(form.get('bonus_gr')) # glide ratio '''bonus and penalties''' formula.no_goal_penalty = c_round( 1.0 - float(form.get('time_points_if_not_in_goal')), 4) formula.arr_alt_bonus = float( form.get('aatb_factor') if form.get('final_glide_decelerator') == 'aatb' else 0) '''jump the gun''' formula.max_JTG = int(form.get('jump_the_gun_max')) # seconds formula.JTG_penalty_per_sec = ( None if form.get('jump_the_gun_factor') == '0' else c_round( 1 / float(form.get('jump_the_gun_factor')), 4)) return formula