Browse Source

First Commit

master
melancholytron 10 months ago
commit
b35f866ee7
  1. 1
      README.md
  2. 468
      fatwalk.py

1
README.md

@ -0,0 +1 @@
hi

468
fatwalk.py

@ -0,0 +1,468 @@
import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QLabel,
QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout,
QGridLayout, QFrame, QSlider, QRadioButton,
QButtonGroup, QCheckBox, QComboBox, QStackedWidget)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont, QDoubleValidator
class VO2Calculator(QFrame):
def __init__(self, parent=None):
super().__init__(parent)
self.setFrameStyle(QFrame.StyledPanel)
self.layout = QVBoxLayout(self)
# Initialize inputs dictionary
self.inputs = {} # Add this line at the beginning
# Create method selector
self.method_selector = QComboBox()
self.method_selector.addItems([
"Age/RHR Method",
"Heart Rate Reserve Method",
"Cooper Test (12-min run)",
"Rockport Walking Test",
"Lab Test Results",
])
self.layout.addWidget(QLabel("VO2 Max Calculation Method:"))
self.layout.addWidget(self.method_selector)
# Create stacked widget for different input methods
self.stacked_widget = QStackedWidget()
# Create different input panels
self.age_rhr_panel = self.create_age_rhr_panel()
self.hrr_panel = self.create_hrr_panel()
self.cooper_panel = self.create_cooper_panel()
self.rockport_panel = self.create_rockport_panel()
self.lab_panel = self.create_lab_panel()
# Add panels to stacked widget
self.stacked_widget.addWidget(self.age_rhr_panel)
self.stacked_widget.addWidget(self.hrr_panel)
self.stacked_widget.addWidget(self.cooper_panel)
self.stacked_widget.addWidget(self.rockport_panel)
self.stacked_widget.addWidget(self.lab_panel)
self.layout.addWidget(self.stacked_widget)
# Connect method selector to panel switching
self.method_selector.currentIndexChanged.connect(self.stacked_widget.setCurrentIndex)
def create_age_rhr_panel(self):
panel = QWidget()
layout = QGridLayout(panel)
self.inputs['resting_hr'] = QLineEdit()
layout.addWidget(QLabel("Resting Heart Rate (bpm):"), 0, 0)
layout.addWidget(self.inputs['resting_hr'], 0, 1)
return panel
def create_hrr_panel(self):
panel = QWidget()
layout = QGridLayout(panel)
self.inputs['max_hr'] = QLineEdit()
self.inputs['exercise_hr'] = QLineEdit()
self.inputs['exercise_intensity'] = QComboBox()
self.inputs['exercise_intensity'].addItems([
'Light (50%)', 'Moderate (65%)',
'Hard (80%)', 'Very Hard (90%)'
])
layout.addWidget(QLabel("Maximum Heart Rate (bpm):"), 0, 0)
layout.addWidget(self.inputs['max_hr'], 0, 1)
layout.addWidget(QLabel("Exercise Heart Rate (bpm):"), 1, 0)
layout.addWidget(self.inputs['exercise_hr'], 1, 1)
layout.addWidget(QLabel("Exercise Intensity:"), 2, 0)
layout.addWidget(self.inputs['exercise_intensity'], 2, 1)
return panel
def create_cooper_panel(self):
panel = QWidget()
layout = QGridLayout(panel)
self.inputs['cooper_distance'] = QLineEdit()
layout.addWidget(QLabel("Distance covered in 12 minutes (meters):"), 0, 0)
layout.addWidget(self.inputs['cooper_distance'], 0, 1)
return panel
def create_rockport_panel(self):
panel = QWidget()
layout = QGridLayout(panel)
self.inputs['walk_time'] = QLineEdit()
self.inputs['end_hr'] = QLineEdit()
layout.addWidget(QLabel("Time to walk 1 mile (minutes):"), 0, 0)
layout.addWidget(self.inputs['walk_time'], 0, 1)
layout.addWidget(QLabel("Heart Rate at end (bpm):"), 1, 0)
layout.addWidget(self.inputs['end_hr'], 1, 1)
return panel
def create_lab_panel(self):
panel = QWidget()
layout = QGridLayout(panel)
self.inputs['lab_vo2'] = QLineEdit()
layout.addWidget(QLabel("Lab Test VO2 Max Result:"), 0, 0)
layout.addWidget(self.inputs['lab_vo2'], 0, 1)
return panel
def calculate_vo2max(self, age, gender, weight_lbs):
"""Calculate VO2 max based on selected method"""
method = self.method_selector.currentIndex()
try:
if method == 0: # Age/RHR Method
resting_hr = float(self.inputs['resting_hr'].text())
# Using the Heart Rate Reserve (HRR) method with estimated max HR
max_hr = 220 - age
hrr = max_hr - resting_hr
if gender.lower() == 'male':
vo2max = 15.3 * (hrr/resting_hr)
else:
vo2max = 15.3 * (hrr/resting_hr) * 0.9
elif method == 1: # Heart Rate Reserve Method
max_hr = float(self.inputs['max_hr'].text())
exercise_hr = float(self.inputs['exercise_hr'].text())
resting_hr = float(self.inputs['resting_hr'].text())
hrr = max_hr - resting_hr
intensity = ((exercise_hr - resting_hr) / hrr)
vo2max = (exercise_hr / max_hr) * 100
elif method == 2: # Cooper Test
distance = float(self.inputs['cooper_distance'].text())
vo2max = (distance - 504.9) / 44.73
elif method == 3: # Rockport Walking Test
time = float(self.inputs['walk_time'].text())
end_hr = float(self.inputs['end_hr'].text())
weight_kg = weight_lbs * 0.453592
if gender.lower() == 'male':
vo2max = 132.853 - (0.0769 * weight_kg) - (0.3877 * age) + (6.315 * 1) - (3.2649 * time) - (0.1565 * end_hr)
else:
vo2max = 132.853 - (0.0769 * weight_kg) - (0.3877 * age) + (6.315 * 0) - (3.2649 * time) - (0.1565 * end_hr)
elif method == 4: # Lab Test
vo2max = float(self.inputs['lab_vo2'].text())
return round(vo2max, 1)
except ValueError:
return None
def get_current_method(self):
return self.method_selector.currentText()
class HikingCalculator(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Hiking Difficulty Calculator")
self.setMinimumWidth(700)
# Create main widget and layout
main_widget = QWidget()
self.setCentralWidget(main_widget)
layout = QVBoxLayout(main_widget)
# Create input fields
input_frame = QFrame()
input_frame.setFrameStyle(QFrame.StyledPanel)
input_layout = QGridLayout(input_frame)
# Create labels and input fields
self.inputs = {}
input_fields = {
'distance': 'Distance (miles)',
'elevation': 'Elevation Gain (feet)',
'height': 'Height (inches)',
'weight': 'Weight (lbs)',
'age': 'Age (years)',
'vo2max': 'VO2 Max'
}
row = 0
for key, label_text in input_fields.items():
label = QLabel(label_text)
input_field = QLineEdit()
input_field.setValidator(QDoubleValidator())
self.inputs[key] = input_field
input_layout.addWidget(label, row, 0)
input_layout.addWidget(input_field, row, 1)
row += 1
# Add gender selection
gender_label = QLabel("Gender:")
self.gender_group = QButtonGroup()
male_radio = QRadioButton("Male")
female_radio = QRadioButton("Female")
male_radio.setChecked(True)
self.gender_group.addButton(male_radio, 1)
self.gender_group.addButton(female_radio, 2)
gender_layout = QHBoxLayout()
gender_layout.addWidget(male_radio)
gender_layout.addWidget(female_radio)
input_layout.addWidget(gender_label, row, 0)
input_layout.addLayout(gender_layout, row, 1)
row += 1
# Add trail type selection
trail_type_label = QLabel("Trail Type:")
self.trail_type_group = QButtonGroup()
self.one_way_radio = QRadioButton("One Way")
self.loop_radio = QRadioButton("Loop/Round Trip")
self.one_way_radio.setChecked(True)
self.trail_type_group.addButton(self.one_way_radio, 1)
self.trail_type_group.addButton(self.loop_radio, 2)
trail_type_layout = QHBoxLayout()
trail_type_layout.addWidget(self.one_way_radio)
trail_type_layout.addWidget(self.loop_radio)
input_layout.addWidget(trail_type_label, row, 0)
input_layout.addLayout(trail_type_layout, row, 1)
row += 1
layout.addWidget(input_frame)
# Add VO2 Calculator
self.vo2_calculator = VO2Calculator()
layout.addWidget(self.vo2_calculator)
# Add auto-calculate VO2 max checkbox
self.auto_vo2_checkbox = QCheckBox("Auto-calculate VO2 Max")
self.auto_vo2_checkbox.setChecked(True)
self.auto_vo2_checkbox.stateChanged.connect(self.toggle_vo2_input)
layout.addWidget(self.auto_vo2_checkbox)
# Create terrain slider frame
terrain_frame = QFrame()
terrain_frame.setFrameStyle(QFrame.StyledPanel)
terrain_layout = QVBoxLayout(terrain_frame)
terrain_label = QLabel("Terrain Difficulty:")
terrain_layout.addWidget(terrain_label)
self.terrain_slider = QSlider(Qt.Horizontal)
self.terrain_slider.setMinimum(1)
self.terrain_slider.setMaximum(10)
self.terrain_slider.setValue(5)
self.terrain_slider.setTickPosition(QSlider.TicksBelow)
self.terrain_slider.setTickInterval(1)
self.terrain_description = QLabel()
self.update_terrain_description(5)
self.terrain_slider.valueChanged.connect(self.update_terrain_description)
terrain_layout.addWidget(self.terrain_slider)
terrain_layout.addWidget(self.terrain_description)
layout.addWidget(terrain_frame)
# Create calculate button
calc_button = QPushButton("Calculate Difficulty")
calc_button.clicked.connect(self.calculate_difficulty)
calc_button.setStyleSheet("""
QPushButton {
background-color: #4CAF50;
color: white;
padding: 8px;
font-size: 14px;
border-radius: 4px;
}
QPushButton:hover {
background-color: #45a049;
}
""")
layout.addWidget(calc_button)
# Create result display
result_frame = QFrame()
result_frame.setFrameStyle(QFrame.StyledPanel)
result_layout = QVBoxLayout(result_frame)
self.score_label = QLabel("Difficulty Score: ")
self.score_label.setAlignment(Qt.AlignCenter)
self.score_label.setFont(QFont('Arial', 14, QFont.Bold))
self.rating_label = QLabel("Rating: ")
self.rating_label.setAlignment(Qt.AlignCenter)
self.rating_label.setFont(QFont('Arial', 12))
self.grade_label = QLabel("Grade: ")
self.grade_label.setAlignment(Qt.AlignCenter)
self.grade_label.setFont(QFont('Arial', 12))
self.vo2_adjusted_label = QLabel("VO2 Max Adjusted Rating: ")
self.vo2_adjusted_label.setAlignment(Qt.AlignCenter)
self.vo2_adjusted_label.setFont(QFont('Arial', 12))
result_layout.addWidget(self.grade_label)
result_layout.addWidget(self.score_label)
result_layout.addWidget(self.rating_label)
result_layout.addWidget(self.vo2_adjusted_label)
layout.addWidget(result_frame)
# Initially disable VO2 max input
self.inputs['vo2max'].setEnabled(False)
# Connect age input to auto-update VO2 max
self.inputs['age'].textChanged.connect(self.update_vo2_max)
def toggle_vo2_input(self, state):
self.inputs['vo2max'].setEnabled(not state)
if state:
self.update_vo2_max()
def update_terrain_description(self, value):
terrain_descriptions = {
1: "Paved road or smooth path",
2: "Well-maintained trail, packed dirt",
3: "Gravel path with some uneven spots",
4: "Natural trail with roots and small rocks",
5: "Mixed terrain with moderate obstacles",
6: "Rocky trail with frequent obstacles",
7: "Rough terrain with loose rocks and steep sections",
8: "Technical terrain with scrambling required",
9: "Very difficult terrain with constant obstacles",
10: "Extreme terrain requiring careful navigation"
}
self.terrain_description.setText(f"Level {value}: {terrain_descriptions[value]}")
def calculate_grade(self, distance_miles, elevation_gain_feet):
# Convert distance to feet
distance_feet = distance_miles * 5280
# If it's a loop/round trip, calculate grade based on half the distance
if self.loop_radio.isChecked():
distance_feet = distance_feet / 2
# Calculate grade as percentage
grade = (elevation_gain_feet / distance_feet) * 100
return grade
def calculate_vo2_adjustment(self, vo2max):
# Adjust difficulty based on VO2 max
if vo2max >= 50:
return 0.8 # Very fit
elif vo2max >= 40:
return 0.9 # Above average
elif vo2max >= 30:
return 1.0 # Average
elif vo2max >= 20:
return 1.2 # Below average
else:
return 1.4 # Poor
def calculate_hike_difficulty(self, distance_miles, elevation_gain_feet,
hiker_height_inches, hiker_weight_lbs,
terrain_difficulty, vo2max):
# Calculate grade
grade = self.calculate_grade(distance_miles, elevation_gain_feet)
# Base difficulty starts with distance
distance_factor = distance_miles / 5
# Elevation gain factor
elevation_factor = elevation_gain_feet / 1000
# Grade factor
grade_factor = (grade / 10) ** 1.5
# Terrain factor
terrain_factor = terrain_difficulty / 5
# BMI calculation
bmi = (hiker_weight_lbs * 703) / (hiker_height_inches ** 2)
# BMI factor
if bmi < 18.5:
bmi_factor = 1.3
elif bmi >= 18.5 and bmi < 25:
bmi_factor = 1.0
elif bmi >= 25 and bmi < 30:
bmi_factor = 1.2
else:
bmi_factor = 1.4
# Calculate base difficulty
raw_score = (distance_factor + elevation_factor + grade_factor) * bmi_factor * terrain_factor
base_score = min(10, max(1, round(raw_score, 1)))
# Calculate VO2 adjusted score
vo2_adjustment = self.calculate_vo2_adjustment(vo2max)
adjusted_score = min(10, max(1, round(raw_score * vo2_adjustment, 1)))
return base_score, adjusted_score, grade
def print_difficulty_rating(self, score):
if score <= 2:
return "Easy"
elif score <= 4:
return "Moderate"
elif score <= 6:
return "Challenging"
elif score <= 8:
return "Difficult"
else:
return "Very Difficult"
def update_vo2_max(self):
"""Calculate and update VO2 max when age, gender, or resting HR changes"""
if self.auto_vo2_checkbox.isChecked():
try:
age = int(self.inputs['age'].text())
gender = 'male' if self.gender_group.checkedId() == 1 else 'female'
weight = float(self.inputs['weight'].text())
# Check if there's a value in the resting HR field
resting_hr_text = self.vo2_calculator.inputs['resting_hr'].text()
if resting_hr_text: # Only calculate if resting HR is provided
vo2max = self.vo2_calculator.calculate_vo2max(age, gender, weight)
if vo2max is not None:
self.inputs['vo2max'].setText(str(vo2max))
except ValueError:
pass
def calculate_difficulty(self):
try:
distance = float(self.inputs['distance'].text())
elevation = float(self.inputs['elevation'].text())
height = float(self.inputs['height'].text())
weight = float(self.inputs['weight'].text())
vo2max = float(self.inputs['vo2max'].text())
terrain = self.terrain_slider.value()
difficulty, adjusted_difficulty, grade = self.calculate_hike_difficulty(
distance, elevation, height, weight, terrain, vo2max)
base_rating = self.print_difficulty_rating(difficulty)
adjusted_rating = self.print_difficulty_rating(adjusted_difficulty)
self.grade_label.setText(f"Grade: {grade:.1f}%")
self.score_label.setText(f"Base Difficulty Score: {difficulty}/10")
self.score_label.setStyleSheet(f"color: {'red' if difficulty > 7 else 'green'};")
self.rating_label.setText(f"Base Rating: {base_rating}")
self.vo2_adjusted_label.setText(
f"VO2 Adjusted: {adjusted_difficulty}/10 ({adjusted_rating})")
except ValueError:
self.score_label.setText("Please fill in all fields with valid numbers")
self.rating_label.setText("")
self.grade_label.setText("")
self.vo2_adjusted_label.setText("")
if __name__ == '__main__':
app = QApplication(sys.argv)
calculator = HikingCalculator()
calculator.show()
sys.exit(app.exec_())
Loading…
Cancel
Save