ssscoring.appcommon
Common functions, classes, and objects to all Streamlit apps in the SSScoring package.
1# See: https://github.com/pr3d4t0r/SSScoring/blob/master/LICENSE.txt 2 3""" 4Common functions, classes, and objects to all Streamlit apps in the SSScoring 5package. 6""" 7 8from importlib_resources import files 9from io import StringIO 10 11from ssscoring import __VERSION__ 12from ssscoring.calc import isValidMaximumAltitude 13from ssscoring.calc import isValidMinimumAltitude 14from ssscoring.constants import FLYSIGHT_FILE_ENCODING 15from ssscoring.constants import M_2_FT 16from ssscoring.datatypes import JumpResults 17from ssscoring.datatypes import JumpStatus 18from ssscoring.errors import SSScoringError 19from ssscoring.notebook import SPEED_COLORS 20from ssscoring.notebook import graphAltitude 21from ssscoring.notebook import graphAngle 22from ssscoring.notebook import graphJumpResult 23from ssscoring.notebook import initializeExtraYRanges 24from ssscoring.notebook import initializePlot 25 26import os 27 28import bokeh.models as bm 29import pandas as pd 30import pydeck as pdk 31import streamlit as st 32 33 34# *** constants *** 35 36DEFAULT_DATA_LAKE = './data' 37""" 38Default data lake directory when reading files from the local file system. 39""" 40 41DZ_DIRECTORY = 'drop-zones-loc-elev.csv' 42""" 43The CSV file database dump of the drop zones directory. 44""" 45 46RESOURCES = 'ssscoring.resources' 47""" 48The package resources in the manifest or package wheel resources. 49""" 50 51STREAMLIT_SIG_KEY = 'HOSTNAME' 52""" 53Environment key used by the Streamlig.app environment when running an 54application. 55""" 56 57STREAMLIT_SIG_VALUE = 'streamlit' 58""" 59Expected value associate with the environment variable `STREAMLIT_SIG_KEY` when 60running in a Streamlit.app environment. 61""" 62 63 64# *** implementation *** 65 66def isStreamlitHostedApp() -> bool: 67 """ 68 Detect if the hosting environment is a native Python system or a Streamlit 69 app environment hosted by streamlit.io or Snowflake. 70 71 Returns 72 ------- 73 `True` if the app is running in the Streamlit app or Snoflake app 74 environment, otherwise `False`. 75 """ 76 keys = tuple(os.environ.keys()) 77 if STREAMLIT_SIG_KEY not in keys: 78 return False 79 if os.environ[STREAMLIT_SIG_KEY] == STREAMLIT_SIG_VALUE: 80 return True 81 return False 82 83 84@st.cache_data 85def initDropZonesFromResource(resourceName: str) -> pd.DataFrame: 86 """ 87 Get the DZs directory from a CSV enclosed in the distribution package as a 88 resource. The resources package is fixed to `ssscoring.resources`, the 89 default resource file is defined by `DZ_DIRECTORY` but can be anything. 90 91 Arguments 92 --------- 93 resourceName 94 A string representing the resource file name, usually a CSV file. 95 96 Returns 97 ------- 98 The global drop zones directory as a dataframe. 99 100 Raises 101 ------ 102 `SSScoringError` if the resource dataframe isn't the global drop zones 103 directory or the file is invalid in any way. 104 """ 105 try: 106 buffer = StringIO(files(RESOURCES).joinpath(resourceName).read_bytes().decode(FLYSIGHT_FILE_ENCODING)) 107 dropZones = pd.read_csv(buffer, sep=',') 108 except Exception as e: 109 raise SSScoringError('Invalid resource - %s' % str(e)) 110 111 if 'dropZone' not in dropZones.columns: 112 raise SSScoringError('dropZone object is a dataframe but not the drop zones directory') 113 114 return dropZones 115 116 117def displayJumpDataIn(resultsTable: pd.DataFrame): 118 """ 119 Display the individual results, as a table. 120 121 Arguments 122 --------- 123 resultsTable: pd.DataFrame 124 The results from a speed skydiving jump. 125 126 See 127 --- 128 `ssscoring.datatypes.JumpResults` 129 """ 130 table = resultsTable.copy() 131 table.vKMh = table.vKMh.apply(lambda x: round(x, 2)) 132 table.hKMh = table.hKMh.apply(lambda x: round(x, 2)) 133 table.deltaV = table.deltaV.apply(lambda x: round(x, 2)) 134 table.deltaAngle = table.deltaAngle.apply(lambda x: round(x, 2)) 135 table['altitude (ft)'] = table['altitude (ft)'].apply(lambda x: round(x, 1)) 136 table.index = ['']*len(table) 137 st.dataframe(table, hide_index=True) 138 139 140def interpretJumpResult(tag: str, 141 jumpResult: JumpResults, 142 processBadJump: bool): 143 """ 144 Interpret the jump results and generate the corresponding labels and 145 warnings if a jump is invalid, or only "somewhat valid" according to ISC 146 rules. The caller turns results display on/off depending on training vs 147 in-competition settins. Because heuristics are a beautiful thing. 148 149 Arguments 150 --------- 151 tag 152 A string that identifies a specific jump and the FlySight version that 153 generated the corresponding track file. Often in the form: `HH-mm-ss:vX` 154 where `X` is the FlySight hardware version. 155 156 jumpResult 157 An instance of `ssscoring.datatypes.JumpResults` with jump data. 158 159 processBadJump 160 If `True`, generate end-user warnings as part of its results processing if 161 the jump is invalid because of ISC rules, but set the status to OK so that 162 the jump may be displayed. 163 164 Returns 165 ------- 166 A `tuple` of these objects: 167 168 - `jumpStatusInfo` - the jump status, in human-readable form 169 - `scoringInfo` - Max speed, scoring window, etc. 170 - `badJumpLegend` - A warning or error if the jump is invalid according to 171 ISC rules 172 - `jumpStatus` - An instance of `JumpStatus` that may have been overriden to 173 `OK` if `processBadJump` was set to `True` and the jump was invalid. Used 174 only for display. Use `jumpResult.status` to determine the actual result 175 of the jump from a strict scoring perspective. `jumpStatus` is used 176 for display override purposes only. 177 """ 178 maxSpeed = jumpResult.maxSpeed 179 window = jumpResult.window 180 jumpStatus = jumpResult.status 181 jumpStatusInfo = '' 182 if jumpResult.status == JumpStatus.WARM_UP_FILE: 183 badJumpLegend = '<span style="color: red">Warm up file or SMD ran out of battery - nothing to do<br>' 184 scoringInfo = '' 185 elif jumpResult.status == JumpStatus.SPEED_ACCURACY_EXCEEDS_LIMIT: 186 badJumpLegend = '<span style="color: red">%s - RE-JUMP: speed accuracy exceeds ISC threshold<br>' % tag 187 scoringInfo = '' 188 else: 189 scoringInfo = 'Max speed = {0:,.0f}; '.format(maxSpeed)+('exit at %d m (%d ft)<br>Validation window starts at %d m (%d ft)<br>End scoring window at %d m (%d ft)<br>' % \ 190 (window.start, M_2_FT*window.start, window.validationStart, M_2_FT*window.validationStart, window.end, M_2_FT*window.end)) 191 if (processBadJump and jumpStatus != JumpStatus.OK and jumpStatus != JumpStatus.WARM_UP_FILE) or jumpStatus == JumpStatus.OK: 192 jumpStatusInfo = '<span style="color: %s">%s jump - %s - %.02f km/h</span><br>' % ('green', tag, 'VALID', jumpResult.score) 193 belowMaxAltitude = isValidMaximumAltitude(jumpResult.data.altitudeAGL.max()) 194 badJumpLegend = None 195 if not isValidMinimumAltitude(jumpResult.data.altitudeAGL.max()): 196 badJumpLegend = '<span style="color: yellow"><span style="font-weight: bold">Warning:</span> exit altitude AGL was lower than the minimum scoring altitude<br>' 197 jumpStatus = JumpStatus.ALTITUDE_EXCEEDS_MINIMUM 198 if not belowMaxAltitude: 199 jumpStatusInfo = '<span style="color: %s">%s jump - %s - %.02f km/h</span><br>' % ('red', tag, 'INVALID', jumpResult.score) 200 badJumpLegend = '<span style="color: red"><span style="font-weight: bold">RE-JUMP:</span> exit altitude AGL exceeds the maximum altitude<br>' 201 jumpStatus = JumpStatus.ALTITUDE_EXCEEDS_MAXIMUM 202 return jumpStatusInfo, scoringInfo, badJumpLegend, jumpStatus 203 204 205def plotJumpResult(tag: str, 206 jumpResult: JumpResults): 207 """ 208 Plot the jump results including altitude, horizontal speed, time, etc. for 209 evaluation and interpretation. 210 211 Arguments 212 --------- 213 tag 214 A string that identifies a specific jump and the FlySight version that 215 generated the corresponding track file. Often in the form: `HH-mm-ss:vX` 216 where `X` is the FlySight hardware version. 217 218 jumpResult 219 An instance of `ssscoring.datatypes.JumpResults` with jump data. 220 """ 221 plot = initializePlot(tag) 222 plot = initializeExtraYRanges(plot, startY=min(jumpResult.data.altitudeAGLFt)-500.0, endY=max(jumpResult.data.altitudeAGLFt)+500.0) 223 graphAltitude(plot, jumpResult) 224 graphAngle(plot, jumpResult) 225 hoverValue = bm.HoverTool(tooltips=[('time', '@x{0.0}s'), ('y-val', '@y{0.00}')]) 226 plot.add_tools(hoverValue) 227 graphJumpResult(plot, jumpResult, lineColor=SPEED_COLORS[0]) 228 st.bokeh_chart(plot, use_container_width=True) 229 230 231def initFileUploaderState(filesObject:str, uploaderKey:str ='uploaderKey'): 232 """ 233 Initialize the session state for the Streamlit app uploader so that 234 selections can be cleared in callbacks later. 235 236 **Important**: `initFileUploaderState()` __must__ be called after setting the 237 page configuration (per Streamlit architecture rules) and before adding any 238 widgets to the sidebars, containers, or main application. 239 240 Argument 241 -------- 242 filesObject 243 A `str` name that is either `'trackFile'` for the single track file process 244 or `'trackFiles'` for the app page that handles more than one track file at 245 a time. 246 247 uploaderKey 248 A unique identifier for the uploader key component, usually set to 249 `'uploaderKey'` but can be any arbitrary name. This value must match the 250 `file_uploader(..., key=uploaderKey,...)` value. 251 252 """ 253 if filesObject not in st.session_state: 254 st.session_state[filesObject] = None 255 if uploaderKey not in st.session_state: 256 st.session_state[uploaderKey] = 0 257 258 259def displayTrackOnMap(deck: pdk.Deck): 260 """ 261 Displays a track map drawn using PyDeck. 262 263 Arguments 264 --------- 265 deck 266 A PyDeck initialized with map layers. 267 """ 268 st.write('Brightest green point shows the max speed point. Exit at orange point. Each track dot is 4 m in diameter.') 269 st.pydeck_chart(deck) 270 271 272def setSideBarAndMain(icon: str, singleTrack: bool, selectDZState): 273 """ 274 Set all the interactive and navigational components for the app's side bar. 275 276 Arguments 277 --------- 278 icon 279 A meaningful Emoji associated with the the side bar's title. 280 281 singleTrack 282 A flag for allowing selection of a single or multiple track files in the 283 corresponding selector component. Determines whether the side bar is used 284 for the single- or multiple selection application. 285 286 selectDZState 287 A callback for the drop zone selector selection box, affected by events in 288 the main application. 289 290 Notes 291 ----- 292 All the aplication level values associated with the components and 293 selections from the side bar are stored in `st.session_state` and visible 294 across the whole application. 295 296 **Do not** cache calls to `ssscoring.appcommon.setSideBarAndMain()` because 297 this can result in unpredictable behavior since the cache may never be 298 cleared until application reload. 299 """ 300 dropZones = initDropZonesFromResource(DZ_DIRECTORY) 301 dropZone = None 302 elevation = None 303 st.sidebar.title('%s SSScore %s' % (icon, __VERSION__)) 304 st.session_state.processBadJump = st.sidebar.checkbox('Process bad jumps', value=True, help='Display results from invalid jumps') 305 dropZone = st.sidebar.selectbox('Select the drop zone:', dropZones.dropZone, index=None, on_change=selectDZState, disabled=(elevation != None and elevation != 0.0)) 306 elevation = st.sidebar.number_input('...or enter the DZ elevation in meters:', min_value=0.0, max_value=4000.0, value='min', format='%.2f', disabled=(dropZone != None), on_change=selectDZState) 307 if dropZone: 308 st.session_state.elevation = dropZones[dropZones.dropZone == dropZone ].iloc[0].elevation 309 elif elevation != None and elevation != 0.0: 310 st.session_state.elevation= elevation 311 else: 312 st.session_state.elevation = None 313 st.session_state.trackFiles = None 314 st.sidebar.metric('Elevation', value='%.1f m' % (0.0 if st.session_state.elevation == None else st.session_state.elevation)) 315 if singleTrack: 316 trackFile = st.sidebar.file_uploader('Track file', [ 'CSV' ], disabled=st.session_state.elevation == None, key = st.session_state.uploaderKey) 317 if trackFile: 318 st.session_state.trackFile = trackFile 319 else: 320 trackFiles = st.sidebar.file_uploader( 321 'Track files', 322 [ 'CSV' ], 323 disabled=st.session_state.elevation == None, 324 accept_multiple_files=True, 325 key = st.session_state.uploaderKey 326 ) 327 if trackFiles: 328 st.session_state.trackFiles = trackFiles 329 st.sidebar.button('Clear', on_click=selectDZState) 330 st.sidebar.link_button('Report missing DZ', 'https://github.com/pr3d4t0r/SSScoring/issues/new?template=report-missing-dz.md', icon=':material/breaking_news_alt_1:') 331 st.sidebar.link_button('Feature request or bug report', 'https://github.com/pr3d4t0r/SSScoring/issues/new?template=Blank+issue', icon=':material/breaking_news_alt_1:')
Default data lake directory when reading files from the local file system.
The CSV file database dump of the drop zones directory.
The package resources in the manifest or package wheel resources.
Environment key used by the Streamlig.app environment when running an application.
Expected value associate with the environment variable STREAMLIT_SIG_KEY
when
running in a Streamlit.app environment.
67def isStreamlitHostedApp() -> bool: 68 """ 69 Detect if the hosting environment is a native Python system or a Streamlit 70 app environment hosted by streamlit.io or Snowflake. 71 72 Returns 73 ------- 74 `True` if the app is running in the Streamlit app or Snoflake app 75 environment, otherwise `False`. 76 """ 77 keys = tuple(os.environ.keys()) 78 if STREAMLIT_SIG_KEY not in keys: 79 return False 80 if os.environ[STREAMLIT_SIG_KEY] == STREAMLIT_SIG_VALUE: 81 return True 82 return False
Detect if the hosting environment is a native Python system or a Streamlit app environment hosted by streamlit.io or Snowflake.
Returns
True
if the app is running in the Streamlit app or Snoflake app
environment, otherwise False
.
85@st.cache_data 86def initDropZonesFromResource(resourceName: str) -> pd.DataFrame: 87 """ 88 Get the DZs directory from a CSV enclosed in the distribution package as a 89 resource. The resources package is fixed to `ssscoring.resources`, the 90 default resource file is defined by `DZ_DIRECTORY` but can be anything. 91 92 Arguments 93 --------- 94 resourceName 95 A string representing the resource file name, usually a CSV file. 96 97 Returns 98 ------- 99 The global drop zones directory as a dataframe. 100 101 Raises 102 ------ 103 `SSScoringError` if the resource dataframe isn't the global drop zones 104 directory or the file is invalid in any way. 105 """ 106 try: 107 buffer = StringIO(files(RESOURCES).joinpath(resourceName).read_bytes().decode(FLYSIGHT_FILE_ENCODING)) 108 dropZones = pd.read_csv(buffer, sep=',') 109 except Exception as e: 110 raise SSScoringError('Invalid resource - %s' % str(e)) 111 112 if 'dropZone' not in dropZones.columns: 113 raise SSScoringError('dropZone object is a dataframe but not the drop zones directory') 114 115 return dropZones
Get the DZs directory from a CSV enclosed in the distribution package as a
resource. The resources package is fixed to ssscoring.resources
, the
default resource file is defined by DZ_DIRECTORY
but can be anything.
Arguments
resourceName
A string representing the resource file name, usually a CSV file.
Returns
The global drop zones directory as a dataframe.
Raises
SSScoringError
if the resource dataframe isn't the global drop zones
directory or the file is invalid in any way.
118def displayJumpDataIn(resultsTable: pd.DataFrame): 119 """ 120 Display the individual results, as a table. 121 122 Arguments 123 --------- 124 resultsTable: pd.DataFrame 125 The results from a speed skydiving jump. 126 127 See 128 --- 129 `ssscoring.datatypes.JumpResults` 130 """ 131 table = resultsTable.copy() 132 table.vKMh = table.vKMh.apply(lambda x: round(x, 2)) 133 table.hKMh = table.hKMh.apply(lambda x: round(x, 2)) 134 table.deltaV = table.deltaV.apply(lambda x: round(x, 2)) 135 table.deltaAngle = table.deltaAngle.apply(lambda x: round(x, 2)) 136 table['altitude (ft)'] = table['altitude (ft)'].apply(lambda x: round(x, 1)) 137 table.index = ['']*len(table) 138 st.dataframe(table, hide_index=True)
Display the individual results, as a table.
Arguments
resultsTable: pd.DataFrame
The results from a speed skydiving jump.
See
141def interpretJumpResult(tag: str, 142 jumpResult: JumpResults, 143 processBadJump: bool): 144 """ 145 Interpret the jump results and generate the corresponding labels and 146 warnings if a jump is invalid, or only "somewhat valid" according to ISC 147 rules. The caller turns results display on/off depending on training vs 148 in-competition settins. Because heuristics are a beautiful thing. 149 150 Arguments 151 --------- 152 tag 153 A string that identifies a specific jump and the FlySight version that 154 generated the corresponding track file. Often in the form: `HH-mm-ss:vX` 155 where `X` is the FlySight hardware version. 156 157 jumpResult 158 An instance of `ssscoring.datatypes.JumpResults` with jump data. 159 160 processBadJump 161 If `True`, generate end-user warnings as part of its results processing if 162 the jump is invalid because of ISC rules, but set the status to OK so that 163 the jump may be displayed. 164 165 Returns 166 ------- 167 A `tuple` of these objects: 168 169 - `jumpStatusInfo` - the jump status, in human-readable form 170 - `scoringInfo` - Max speed, scoring window, etc. 171 - `badJumpLegend` - A warning or error if the jump is invalid according to 172 ISC rules 173 - `jumpStatus` - An instance of `JumpStatus` that may have been overriden to 174 `OK` if `processBadJump` was set to `True` and the jump was invalid. Used 175 only for display. Use `jumpResult.status` to determine the actual result 176 of the jump from a strict scoring perspective. `jumpStatus` is used 177 for display override purposes only. 178 """ 179 maxSpeed = jumpResult.maxSpeed 180 window = jumpResult.window 181 jumpStatus = jumpResult.status 182 jumpStatusInfo = '' 183 if jumpResult.status == JumpStatus.WARM_UP_FILE: 184 badJumpLegend = '<span style="color: red">Warm up file or SMD ran out of battery - nothing to do<br>' 185 scoringInfo = '' 186 elif jumpResult.status == JumpStatus.SPEED_ACCURACY_EXCEEDS_LIMIT: 187 badJumpLegend = '<span style="color: red">%s - RE-JUMP: speed accuracy exceeds ISC threshold<br>' % tag 188 scoringInfo = '' 189 else: 190 scoringInfo = 'Max speed = {0:,.0f}; '.format(maxSpeed)+('exit at %d m (%d ft)<br>Validation window starts at %d m (%d ft)<br>End scoring window at %d m (%d ft)<br>' % \ 191 (window.start, M_2_FT*window.start, window.validationStart, M_2_FT*window.validationStart, window.end, M_2_FT*window.end)) 192 if (processBadJump and jumpStatus != JumpStatus.OK and jumpStatus != JumpStatus.WARM_UP_FILE) or jumpStatus == JumpStatus.OK: 193 jumpStatusInfo = '<span style="color: %s">%s jump - %s - %.02f km/h</span><br>' % ('green', tag, 'VALID', jumpResult.score) 194 belowMaxAltitude = isValidMaximumAltitude(jumpResult.data.altitudeAGL.max()) 195 badJumpLegend = None 196 if not isValidMinimumAltitude(jumpResult.data.altitudeAGL.max()): 197 badJumpLegend = '<span style="color: yellow"><span style="font-weight: bold">Warning:</span> exit altitude AGL was lower than the minimum scoring altitude<br>' 198 jumpStatus = JumpStatus.ALTITUDE_EXCEEDS_MINIMUM 199 if not belowMaxAltitude: 200 jumpStatusInfo = '<span style="color: %s">%s jump - %s - %.02f km/h</span><br>' % ('red', tag, 'INVALID', jumpResult.score) 201 badJumpLegend = '<span style="color: red"><span style="font-weight: bold">RE-JUMP:</span> exit altitude AGL exceeds the maximum altitude<br>' 202 jumpStatus = JumpStatus.ALTITUDE_EXCEEDS_MAXIMUM 203 return jumpStatusInfo, scoringInfo, badJumpLegend, jumpStatus
Interpret the jump results and generate the corresponding labels and warnings if a jump is invalid, or only "somewhat valid" according to ISC rules. The caller turns results display on/off depending on training vs in-competition settins. Because heuristics are a beautiful thing.
Arguments
tag
A string that identifies a specific jump and the FlySight version that
generated the corresponding track file. Often in the form: HH-mm-ss:vX
where X
is the FlySight hardware version.
jumpResult
An instance of ssscoring.datatypes.JumpResults
with jump data.
processBadJump
If True
, generate end-user warnings as part of its results processing if
the jump is invalid because of ISC rules, but set the status to OK so that
the jump may be displayed.
Returns
A tuple
of these objects:
jumpStatusInfo
- the jump status, in human-readable formscoringInfo
- Max speed, scoring window, etc.badJumpLegend
- A warning or error if the jump is invalid according to ISC rulesjumpStatus
- An instance ofJumpStatus
that may have been overriden toOK
ifprocessBadJump
was set toTrue
and the jump was invalid. Used only for display. UsejumpResult.status
to determine the actual result of the jump from a strict scoring perspective.jumpStatus
is used for display override purposes only.
206def plotJumpResult(tag: str, 207 jumpResult: JumpResults): 208 """ 209 Plot the jump results including altitude, horizontal speed, time, etc. for 210 evaluation and interpretation. 211 212 Arguments 213 --------- 214 tag 215 A string that identifies a specific jump and the FlySight version that 216 generated the corresponding track file. Often in the form: `HH-mm-ss:vX` 217 where `X` is the FlySight hardware version. 218 219 jumpResult 220 An instance of `ssscoring.datatypes.JumpResults` with jump data. 221 """ 222 plot = initializePlot(tag) 223 plot = initializeExtraYRanges(plot, startY=min(jumpResult.data.altitudeAGLFt)-500.0, endY=max(jumpResult.data.altitudeAGLFt)+500.0) 224 graphAltitude(plot, jumpResult) 225 graphAngle(plot, jumpResult) 226 hoverValue = bm.HoverTool(tooltips=[('time', '@x{0.0}s'), ('y-val', '@y{0.00}')]) 227 plot.add_tools(hoverValue) 228 graphJumpResult(plot, jumpResult, lineColor=SPEED_COLORS[0]) 229 st.bokeh_chart(plot, use_container_width=True)
Plot the jump results including altitude, horizontal speed, time, etc. for evaluation and interpretation.
Arguments
tag
A string that identifies a specific jump and the FlySight version that
generated the corresponding track file. Often in the form: HH-mm-ss:vX
where X
is the FlySight hardware version.
jumpResult
An instance of ssscoring.datatypes.JumpResults
with jump data.
232def initFileUploaderState(filesObject:str, uploaderKey:str ='uploaderKey'): 233 """ 234 Initialize the session state for the Streamlit app uploader so that 235 selections can be cleared in callbacks later. 236 237 **Important**: `initFileUploaderState()` __must__ be called after setting the 238 page configuration (per Streamlit architecture rules) and before adding any 239 widgets to the sidebars, containers, or main application. 240 241 Argument 242 -------- 243 filesObject 244 A `str` name that is either `'trackFile'` for the single track file process 245 or `'trackFiles'` for the app page that handles more than one track file at 246 a time. 247 248 uploaderKey 249 A unique identifier for the uploader key component, usually set to 250 `'uploaderKey'` but can be any arbitrary name. This value must match the 251 `file_uploader(..., key=uploaderKey,...)` value. 252 253 """ 254 if filesObject not in st.session_state: 255 st.session_state[filesObject] = None 256 if uploaderKey not in st.session_state: 257 st.session_state[uploaderKey] = 0
Initialize the session state for the Streamlit app uploader so that selections can be cleared in callbacks later.
Important: initFileUploaderState()
__must__ be called after setting the
page configuration (per Streamlit architecture rules) and before adding any
widgets to the sidebars, containers, or main application.
Argument
filesObject
A str
name that is either 'trackFile'
for the single track file process
or 'trackFiles'
for the app page that handles more than one track file at
a time.
uploaderKey
A unique identifier for the uploader key component, usually set to
'uploaderKey'
but can be any arbitrary name. This value must match the
file_uploader(..., key=uploaderKey,...)
value.
260def displayTrackOnMap(deck: pdk.Deck): 261 """ 262 Displays a track map drawn using PyDeck. 263 264 Arguments 265 --------- 266 deck 267 A PyDeck initialized with map layers. 268 """ 269 st.write('Brightest green point shows the max speed point. Exit at orange point. Each track dot is 4 m in diameter.') 270 st.pydeck_chart(deck)
Displays a track map drawn using PyDeck.
Arguments
deck
A PyDeck initialized with map layers.
273def setSideBarAndMain(icon: str, singleTrack: bool, selectDZState): 274 """ 275 Set all the interactive and navigational components for the app's side bar. 276 277 Arguments 278 --------- 279 icon 280 A meaningful Emoji associated with the the side bar's title. 281 282 singleTrack 283 A flag for allowing selection of a single or multiple track files in the 284 corresponding selector component. Determines whether the side bar is used 285 for the single- or multiple selection application. 286 287 selectDZState 288 A callback for the drop zone selector selection box, affected by events in 289 the main application. 290 291 Notes 292 ----- 293 All the aplication level values associated with the components and 294 selections from the side bar are stored in `st.session_state` and visible 295 across the whole application. 296 297 **Do not** cache calls to `ssscoring.appcommon.setSideBarAndMain()` because 298 this can result in unpredictable behavior since the cache may never be 299 cleared until application reload. 300 """ 301 dropZones = initDropZonesFromResource(DZ_DIRECTORY) 302 dropZone = None 303 elevation = None 304 st.sidebar.title('%s SSScore %s' % (icon, __VERSION__)) 305 st.session_state.processBadJump = st.sidebar.checkbox('Process bad jumps', value=True, help='Display results from invalid jumps') 306 dropZone = st.sidebar.selectbox('Select the drop zone:', dropZones.dropZone, index=None, on_change=selectDZState, disabled=(elevation != None and elevation != 0.0)) 307 elevation = st.sidebar.number_input('...or enter the DZ elevation in meters:', min_value=0.0, max_value=4000.0, value='min', format='%.2f', disabled=(dropZone != None), on_change=selectDZState) 308 if dropZone: 309 st.session_state.elevation = dropZones[dropZones.dropZone == dropZone ].iloc[0].elevation 310 elif elevation != None and elevation != 0.0: 311 st.session_state.elevation= elevation 312 else: 313 st.session_state.elevation = None 314 st.session_state.trackFiles = None 315 st.sidebar.metric('Elevation', value='%.1f m' % (0.0 if st.session_state.elevation == None else st.session_state.elevation)) 316 if singleTrack: 317 trackFile = st.sidebar.file_uploader('Track file', [ 'CSV' ], disabled=st.session_state.elevation == None, key = st.session_state.uploaderKey) 318 if trackFile: 319 st.session_state.trackFile = trackFile 320 else: 321 trackFiles = st.sidebar.file_uploader( 322 'Track files', 323 [ 'CSV' ], 324 disabled=st.session_state.elevation == None, 325 accept_multiple_files=True, 326 key = st.session_state.uploaderKey 327 ) 328 if trackFiles: 329 st.session_state.trackFiles = trackFiles 330 st.sidebar.button('Clear', on_click=selectDZState) 331 st.sidebar.link_button('Report missing DZ', 'https://github.com/pr3d4t0r/SSScoring/issues/new?template=report-missing-dz.md', icon=':material/breaking_news_alt_1:') 332 st.sidebar.link_button('Feature request or bug report', 'https://github.com/pr3d4t0r/SSScoring/issues/new?template=Blank+issue', icon=':material/breaking_news_alt_1:')
Set all the interactive and navigational components for the app's side bar.
Arguments
icon
A meaningful Emoji associated with the the side bar's title.
singleTrack
A flag for allowing selection of a single or multiple track files in the corresponding selector component. Determines whether the side bar is used for the single- or multiple selection application.
selectDZState
A callback for the drop zone selector selection box, affected by events in the main application.
Notes
All the aplication level values associated with the components and
selections from the side bar are stored in st.session_state
and visible
across the whole application.
Do not cache calls to ssscoring.appcommon.setSideBarAndMain()
because
this can result in unpredictable behavior since the cache may never be
cleared until application reload.