ssscoring.ssscoremultiple
Experimental
Process a group of jumps uploaded from a file uploader.
1# See: https://github.com/pr3d4t0r/SSScoring/blob/master/LICENSE.txtl 2 3""" 4## Experimental 5 6Process a group of jumps uploaded from a file uploader. 7""" 8 9from ssscoring import __VERSION__ 10from ssscoring.appcommon import displayJumpDataIn 11from ssscoring.appcommon import displayTrackOnMap 12from ssscoring.appcommon import fetchResource 13from ssscoring.appcommon import initFileUploaderState 14from ssscoring.appcommon import interpretJumpResult 15from ssscoring.appcommon import plotJumpResult 16from ssscoring.appcommon import setSideBarAndMain 17from ssscoring.calc import aggregateResults 18from ssscoring.calc import collateAnglesByTimeFromExit 19from ssscoring.calc import dropNonSkydiveDataFrom 20from ssscoring.calc import processAllJumpFiles 21from ssscoring.calc import totalResultsFrom 22from ssscoring.constants import DEFAULT_PLOT_INCREMENT 23from ssscoring.constants import DEFAULT_PLOT_MAX_V_SCALE 24from ssscoring.constants import M_2_FT 25from ssscoring.constants import SPEED_ACCURACY_THRESHOLD 26from ssscoring.constants import SSSCORE_INSTRUCTIONS_MD 27from ssscoring.datatypes import JumpStatus 28from ssscoring.datatypes import PerformanceWindow 29from ssscoring.mapview import multipleSpeedJumpsTrajectories 30from ssscoring.mapview import speedJumpTrajectory 31from ssscoring.notebook import SPEED_COLORS 32from ssscoring.notebook import graphJumpResult 33from ssscoring.notebook import initializePlot 34 35import bokeh.plotting as bp 36import pandas as pd 37import streamlit as st 38 39 40# +++ implementation +++ 41 42def _selectDZState(*args, **kwargs): 43 if st.session_state.elevation: 44 st.session_state.uploaderKey += 1 45 st.session_state.trackFiles = None 46 47 48def _styleShowMaxIn(scores: pd.Series) -> pd.DataFrame: 49 return [ 50 'background-color: mediumseagreen' if v == scores.max() else \ 51 '' for v in scores ] 52 53 54def _displayAllJumpDataIn(data: pd.DataFrame): 55 if data is not None: 56 columns = [ 'plotTime' ] + [ column for column in data.columns if column != 'plotTime' and column != 'timeUnix' ] 57 st.html('<h3>All jump data from exit</h3>') 58 st.dataframe(data, 59 column_order=columns, 60 column_config={ 61 'plotTime': st.column_config.NumberColumn(format='%.02f'), 62 'speedAngle': st.column_config.NumberColumn(format='%.02f'), 63 'speedAccuracyISC': st.column_config.NumberColumn(format='%.02f'), 64 }, 65 hide_index=True) 66 67 68def _displayScoresIn(scoresData: dict): 69 if scoresData is not None: 70 st.html('<h3>All 3-sec sliding window scores</h3>') 71 data = pd.DataFrame.from_dict({ 'time': scoresData.values(), 'score': scoresData.keys(), }) 72 data.time = data.time.apply(lambda x: '%.2f' % x) 73 st.dataframe(data, hide_index=True) 74 75 76def _displayBadRowsISCAccuracyExceeded(data: pd.DataFrame, window: PerformanceWindow): 77 badRows = data[data.speedAccuracyISC >= SPEED_ACCURACY_THRESHOLD] 78 badRows = dropNonSkydiveDataFrom(badRows) 79 times = pd.to_datetime(badRows.timeUnix, unit='s').dt.strftime('%Y-%m-%d %H:%M:%S.%f').str[:-4] 80 badRows.insert(0, 'time', times) 81 badRows.drop(columns = [ 82 'timeUnix', 83 'altitudeMSL', 84 'altitudeMSLFt', 85 'speedAccuracy', 86 'hMetersPerSecond', 87 'hKMh', 88 'speedAngle', 89 'latitude', 90 'longitude', 91 'verticalAccuracy', ], inplace=True) 92 st.html('<h3>Performance window:<br>start = %.2f m (%.2f ft)<br>end = %.2f m (%.2f ft)<br>validation start = %.2f m (%.2f ft)</h3>' % \ 93 (window.start, M_2_FT*window.start, window.end, M_2_FT*window.end, window.validationStart, M_2_FT*window.validationStart)) 94 st.html('<h3>%d track rows where the ISC speed accuracy threshold was exceeded during the speed run:</h3>' % len(badRows)) 95 st.dataframe(badRows, hide_index=True) 96 97 98 workData = data.copy() 99 workData = dropNonSkydiveDataFrom(workData) 100 times = pd.to_datetime(workData.timeUnix, unit='s').dt.strftime('%Y-%m-%d %H:%M:%S.%f').str[:-4] 101 workData.insert(0, 'time', times) 102 st.html('<h3>Full speed run data (%d rows)</h3>' % len(workData)) 103 st.dataframe(workData, hide_index=True) 104 105 106def _styleShowMinMaxIn(scores: pd.Series) -> pd.DataFrame: 107 return [ 108 'background-color: green' if v == scores.max() else \ 109 'background-color: orangered' if v == scores.min() else \ 110 '' for v in scores ] 111 112 113def _displayJumpsInSet(aggregate: pd.DataFrame): 114 with st.expander('**Jumps in this set**', expanded=True, icon=':material/dataset:'): 115 displayAggregate = aggregate.style.apply(_styleShowMinMaxIn, subset=[ 'score', ]).apply(_styleShowMaxIn, subset=[ 'maxSpeed', ]).format(precision=2) 116 st.dataframe(displayAggregate) 117 118 119def _displaySpeedSummary(aggregate: pd.DataFrame, 120 allJumpsPlot: bp.Figure): 121 st.html('<h2>Speed summary</h2>') 122 st.dataframe(totalResultsFrom(aggregate), hide_index = True) 123 st.bokeh_chart(allJumpsPlot, use_container_width=True) 124 125 126def _displaySpeedAngles(jumpResults: dict): 127 with st.expander('**Speed angles**', icon=':material/arrow_back_ios_new:'): 128 angles = collateAnglesByTimeFromExit(jumpResults).style.format(precision=1) 129 st.dataframe(angles) 130 131 132def _displayAllTracksOnMap(jumpResults: dict): 133 with st.expander('**All jumps trajectories**'): 134 displayTrackOnMap(multipleSpeedJumpsTrajectories(jumpResults)) 135 136 137def _maxSpeedScaleFrom(jumpResults: dict) -> float: 138 maxScore = max(result.score if result.score != None else 0 for result in jumpResults.values()) 139 try: 140 return DEFAULT_PLOT_MAX_V_SCALE if maxScore <= DEFAULT_PLOT_MAX_V_SCALE else maxScore + DEFAULT_PLOT_INCREMENT 141 except TypeError: 142 return DEFAULT_PLOT_MAX_V_SCALE 143 144 145def main(): 146 st.set_page_config( 147 layout = 'wide', 148 page_title='SSScore %s' % __VERSION__, 149 ) 150 initFileUploaderState('trackFiles') 151 setSideBarAndMain('🔢', False, _selectDZState) 152 153 if st.session_state.trackFiles: 154 jumpResults = processAllJumpFiles(st.session_state.trackFiles, altitudeDZMeters=st.session_state.elevation) 155 allJumpsPlot = initializePlot('All jumps', backgroundColorName='#2c2c2c', yMax=_maxSpeedScaleFrom(jumpResults)) 156 mixColor = 0 157 jumpResultsSubset = dict() 158 resultTags = sorted(list(jumpResults.keys()), reverse=True) 159 tabs = st.tabs(['Totals']+resultTags) 160 index = 1 161 jumpStatus = JumpStatus.OK 162 for tag in resultTags: 163 jumpResult = jumpResults[tag] 164 mixColor = (mixColor+1)%len(SPEED_COLORS) 165 with tabs[index]: 166 jumpStatusInfo,\ 167 scoringInfo,\ 168 badJumpLegend,\ 169 jumpStatus = interpretJumpResult(tag, jumpResult, st.session_state.processBadJump) 170 if jumpStatus != JumpStatus.OK: 171 st.toast('#### %s - %s' % (tag, str(jumpStatus)), icon='⚠️') 172 if (st.session_state.processBadJump and jumpStatus != JumpStatus.OK) or jumpStatus == JumpStatus.OK: 173 jumpResultsSubset[tag] = jumpResult 174 st.html('<h3>'+jumpStatusInfo+scoringInfo+(str(badJumpLegend) if badJumpLegend else '')) 175 st.html("<br>If this was NOT a warm-up file, it's probably an ISC altitude violation; please report to Eugene/pr3d4t0r and attach the TRACK.CSV file</h3>" if jumpStatus in [ JumpStatus.WARM_UP_FILE, ] else '</h3>') 176 if (st.session_state.processBadJump and jumpStatus != JumpStatus.OK) or jumpStatus == JumpStatus.OK: 177 displayJumpDataIn(jumpResult.table) 178 st.write('Max score = crosshairs. Max speed = diamond.') 179 plotJumpResult(tag, jumpResult) 180 graphJumpResult( 181 allJumpsPlot, 182 jumpResult, 183 lineColor=SPEED_COLORS[mixColor], 184 legend='%s = %.2f' % (tag, jumpResult.score if jumpResult.score else -1.0), 185 showIt=False 186 ) 187 st.session_state.displayScorePoint = st.toggle('Display max score / max speed point', value=True, help='Show the fastest speed or score point along the flight path', key=tag) 188 displayTrackOnMap(speedJumpTrajectory(jumpResult, st.session_state.displayScorePoint), st.session_state.displayScorePoint) 189 _displayAllJumpDataIn(jumpResult.data) 190 _displayScoresIn(jumpResult.scores) 191 elif jumpStatus == JumpStatus.SPEED_ACCURACY_EXCEEDS_LIMIT: 192 _displayBadRowsISCAccuracyExceeded(jumpResult.data, jumpResult.window) 193 index += 1 194 with tabs[0]: 195 if len(resultTags): 196 if (st.session_state.processBadJump and jumpStatus != JumpStatus.OK) or jumpStatus == JumpStatus.OK: 197 aggregate = aggregateResults(jumpResultsSubset) 198 if len(aggregate) > 0: 199 _displayJumpsInSet(aggregate) 200 _displaySpeedAngles(jumpResults) 201 _displaySpeedSummary(aggregate, allJumpsPlot) 202 _displayAllTracksOnMap(jumpResults) 203 else: 204 st.write(fetchResource(SSSCORE_INSTRUCTIONS_MD).read(), unsafe_allow_html=True) 205 206 207if '__main__' == __name__: 208 main()
def
main():
146def main(): 147 st.set_page_config( 148 layout = 'wide', 149 page_title='SSScore %s' % __VERSION__, 150 ) 151 initFileUploaderState('trackFiles') 152 setSideBarAndMain('🔢', False, _selectDZState) 153 154 if st.session_state.trackFiles: 155 jumpResults = processAllJumpFiles(st.session_state.trackFiles, altitudeDZMeters=st.session_state.elevation) 156 allJumpsPlot = initializePlot('All jumps', backgroundColorName='#2c2c2c', yMax=_maxSpeedScaleFrom(jumpResults)) 157 mixColor = 0 158 jumpResultsSubset = dict() 159 resultTags = sorted(list(jumpResults.keys()), reverse=True) 160 tabs = st.tabs(['Totals']+resultTags) 161 index = 1 162 jumpStatus = JumpStatus.OK 163 for tag in resultTags: 164 jumpResult = jumpResults[tag] 165 mixColor = (mixColor+1)%len(SPEED_COLORS) 166 with tabs[index]: 167 jumpStatusInfo,\ 168 scoringInfo,\ 169 badJumpLegend,\ 170 jumpStatus = interpretJumpResult(tag, jumpResult, st.session_state.processBadJump) 171 if jumpStatus != JumpStatus.OK: 172 st.toast('#### %s - %s' % (tag, str(jumpStatus)), icon='⚠️') 173 if (st.session_state.processBadJump and jumpStatus != JumpStatus.OK) or jumpStatus == JumpStatus.OK: 174 jumpResultsSubset[tag] = jumpResult 175 st.html('<h3>'+jumpStatusInfo+scoringInfo+(str(badJumpLegend) if badJumpLegend else '')) 176 st.html("<br>If this was NOT a warm-up file, it's probably an ISC altitude violation; please report to Eugene/pr3d4t0r and attach the TRACK.CSV file</h3>" if jumpStatus in [ JumpStatus.WARM_UP_FILE, ] else '</h3>') 177 if (st.session_state.processBadJump and jumpStatus != JumpStatus.OK) or jumpStatus == JumpStatus.OK: 178 displayJumpDataIn(jumpResult.table) 179 st.write('Max score = crosshairs. Max speed = diamond.') 180 plotJumpResult(tag, jumpResult) 181 graphJumpResult( 182 allJumpsPlot, 183 jumpResult, 184 lineColor=SPEED_COLORS[mixColor], 185 legend='%s = %.2f' % (tag, jumpResult.score if jumpResult.score else -1.0), 186 showIt=False 187 ) 188 st.session_state.displayScorePoint = st.toggle('Display max score / max speed point', value=True, help='Show the fastest speed or score point along the flight path', key=tag) 189 displayTrackOnMap(speedJumpTrajectory(jumpResult, st.session_state.displayScorePoint), st.session_state.displayScorePoint) 190 _displayAllJumpDataIn(jumpResult.data) 191 _displayScoresIn(jumpResult.scores) 192 elif jumpStatus == JumpStatus.SPEED_ACCURACY_EXCEEDS_LIMIT: 193 _displayBadRowsISCAccuracyExceeded(jumpResult.data, jumpResult.window) 194 index += 1 195 with tabs[0]: 196 if len(resultTags): 197 if (st.session_state.processBadJump and jumpStatus != JumpStatus.OK) or jumpStatus == JumpStatus.OK: 198 aggregate = aggregateResults(jumpResultsSubset) 199 if len(aggregate) > 0: 200 _displayJumpsInSet(aggregate) 201 _displaySpeedAngles(jumpResults) 202 _displaySpeedSummary(aggregate, allJumpsPlot) 203 _displayAllTracksOnMap(jumpResults) 204 else: 205 st.write(fetchResource(SSSCORE_INSTRUCTIONS_MD).read(), unsafe_allow_html=True)