Workout Creator

Good day, trust all is well?
I am working on a Weekly Workout creator app based on Fitness and Fatigue. So basically the Fitness will determine the workout Type, while the fatigue will determine rest days, and workouts. The time on each workout will be controlled by Age, thus the younger, the less time the duration will be.
I need help with constructing the workouts, so that the workouts being added randomly to the weekly calender. There will be 5 Workout Types with 5 examples each.
How do I create my blocks so that each of the days from Sunday to Saturdays are populated randomly according to the criteria set
Any help will be appreciated

Could you show us a sample calendar or two, so we know what the end result should look like?

Also, please retire your rubber stamp and learn to use lists and tables if you haven't done so yet.

Thanks, Here is an example I made in HTML, but the reality is its needed in an app rather

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Week Planner</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      background-color: #f4f8ec; /* Light green tone */
      margin: 0;
      display: flex;
      align-items: center;
      justify-content: center;
      height: 100vh;
    }

    #plannerContainer {
      background-color: #fff;
      border-radius: 10px;
      box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
      padding: 20px;
      width: 80%;
    }

    h2 {
      text-align: center;
    }

    form {
      display: flex;
      flex-wrap: wrap;
      justify-content: space-around;
      margin-top: 20px;
    }

    label {
      width: calc(50% - 10px);
      margin-bottom: 5px;
      text-align: right;
    }

    input, select {
      width: calc(50% - 10px);
      margin-bottom: 10px;
      padding: 5px; /* Smaller padding */
      border: 1px solid #ddd;
      border-radius: 4px;
      box-sizing: border-box;
    }

    button {
      width: calc(100% - 16px);
      padding: 8px; /* Smaller padding */
      background-color: #4caf50; /* Green */
      color: white;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      margin-top: 10px;
    }

    button:hover {
      background-color: #45a049;
    }

    table {
      width: 100%;
      border-collapse: collapse;
      margin-top: 20px;
    }

    th, td {
      border: 1px solid #ddd;
      padding: 8px;
      text-align: center;
    }

    th {
      background-color: #f2f2f2;
    }
  </style>
</head>
<body>

  <div id="plannerContainer">
    <h2>Week Planner</h2>

    <form id="plannerForm">
      <label for="age">Age:</label>
      <input type="number" id="age" name="age" required min="10">

      <label for="fitness">Fitness:</label>
      <input type="number" id="fitness" name="fitness" required>

      <label for="fatigue">Fatigue:</label>
      <input type="number" id="fatigue" name="fatigue" required>
	  
	  <label for="nextRaceDate">Next Race Date:</label>
<input type="date" id="nextRaceDate" name="nextRaceDate" required>


 <label for="restDay">Rest Day:</label>
<select id="restDay" name="restDay">
  <option value="none">None</option>
  <option value="sunday" selected>Sunday</option>
  <option value="monday">Monday</option>
  <option value="tuesday">Tuesday</option>
  <option value="wednesday">Wednesday</option>
  <option value="thursday">Thursday</option>
  <option value="friday">Friday</option>
  <option value="saturday">Saturday</option>
</select>


      <button type="button" onclick="validateAndGenerate()">Generate</button>
    </form>

    <table>
      <thead>
        <tr>
          <th>Sunday</th>
          <th>Monday</th>
          <th>Tuesday</th>
          <th>Wednesday</th>
          <th>Thursday</th>
          <th>Friday</th>
          <th>Saturday</th>
        </tr>
      </thead>
      <tbody id="weekPlanner">
        <!-- Planner content goes here -->
      </tbody>
    </table>
  </div>

  <script>
  
function validateAndGenerate() {
  const ageInput = document.getElementById("age");
  const fitnessInput = document.getElementById("fitness");
  const fatigueInput = document.getElementById("fatigue");
  const nextRaceDateInput = document.getElementById("nextRaceDate");

  if (
    ageInput.checkValidity() &&
    fitnessInput.checkValidity() &&
    fatigueInput.checkValidity() &&
    nextRaceDateInput.checkValidity() &&
    ageInput.value >= 10
  ) {
    const age = parseInt(ageInput.value);
    const fitness = parseInt(fitnessInput.value);
    const fatigue = parseInt(fatigueInput.value);
    const nextRaceDate = new Date(nextRaceDateInput.value);

    // Check if the race date is within a week of the current date
    const currentDate = new Date();
    const oneWeekFromNow = new Date();
    oneWeekFromNow.setDate(currentDate.getDate() + 7);

    if (nextRaceDate >= currentDate && nextRaceDate <= oneWeekFromNow) {
      const restDaySelect = document.getElementById("restDay");
      const restDay = restDaySelect.value;

      generateCalendar(age, fitness, fatigue, restDay);
    } else {
      alert("Please select a race date within the next week.");
    }
  } else {
    alert("Please make sure the age is at least 10 and all fields are filled correctly.");
  }
}


function generateCalendar(age, fitness, fatigue, restDay) {
  const plannerContent = document.getElementById("weekPlanner");
  
   // Display the current week's dates at the top of the calendar
  const currentDate = new Date();
  const currentDay = currentDate.getDay(); // 0 for Sunday, 1 for Monday, etc.
  const startDate = new Date(currentDate);
  startDate.setDate(currentDate.getDate() - currentDay); // Move to the beginning of the week

  let headerRow = '<tr>';
  for (let i = 0; i < 7; i++) {
    const day = new Date(startDate);
    day.setDate(startDate.getDate() + i);
    const formattedDate = day.toLocaleDateString("en-US", { weekday: "short", day: "numeric" });
    headerRow += `<th>${formattedDate}</th>`;
  }
  headerRow += '</tr>';
  plannerContent.innerHTML = headerRow; 

  // Define workout types and time values
  let workoutTypes = [];
  const baseTimeValues = {
    TIME_A: 50,
    TIME_B: 5,
    TIME_C: 3,
    TIME_D: 8,
    TIME_E: 60,
  };

  // Set workout types based on fitness level
  if (fitness < 5) {
    workoutTypes = ['BASE', 'INTERVAL'];
  } else if (fitness >= 5 && fitness <= 10) {
    workoutTypes = ['INTERVAL', 'TEMPO'];
  } else if (fitness >= 10 && fitness <= 15) {
    workoutTypes = ['BASE', 'TEMPO'];
  } else if (fitness > 15) {
    workoutTypes = ['EASY', 'TEMPO'];
  }

  // Define workout examples
  const workouts = {
    BASE: ['BASE_1', 'BASE_2', 'BASE_3'],
    INTERVAL: ['INTERVAL_1', 'INTERVAL_2', 'INTERVAL_3'],
    TEMPO: ['TEMPO_1', 'TEMPO_2', 'TEMPO_3'],
    EASY: ['EASY_1', 'EASY_2', 'EASY_3', 'EASY_4'],
  };

  // Randomly select workouts for each day
  const daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];

  let calendarContent = '';
  let addedBaseWorkout = false; // Flag to track if a BASE workout has been added
  let usedWorkouts = [];

  for (let i = 0; i < daysOfWeek.length; i++) {
    const day = daysOfWeek[i];
    if (day.toLowerCase() === restDay.toLowerCase()) {
      // Skip rest day
      calendarContent += '<td>Rest Day</td>';
    } else {
      let selectedWorkoutType;
      let selectedWorkout;

      do {
        if (fitness < 5) {
          // Random mix of BASE and INTERVAL workouts
          selectedWorkoutType = workoutTypes[Math.floor(Math.random() * workoutTypes.length)];
        } else if (fitness >= 5 && fitness <= 10) {
          // Two days of INTERVAL or TEMPO, and one BASE day between Monday and Wednesday
          if (!addedBaseWorkout && i >= 1 && i <= 3) {
            selectedWorkoutType = 'BASE';
            addedBaseWorkout = true;
          } else {
            selectedWorkoutType = workoutTypes[Math.floor(Math.random() * workoutTypes.length)];
          }
        } else if (fitness > 10) {
          // One day of INTERVAL or TEMPO, and one BASE day between Monday and Wednesday
          if (!addedBaseWorkout && i >= 1 && i <= 3) {
            selectedWorkoutType = 'BASE';
            addedBaseWorkout = true;
          } else {
            selectedWorkoutType = workoutTypes[Math.floor(Math.random() * workoutTypes.length)];
          }
        }

        selectedWorkout = workouts[selectedWorkoutType][Math.floor(Math.random() * workouts[selectedWorkoutType].length)];
      } while (usedWorkouts.includes(selectedWorkout));

      usedWorkouts.push(selectedWorkout);

      // Adjust times based on the selected workout, age, fitness, and fatigue levels
      const adjustedTimes = adjustTimes(selectedWorkout, age, fitness, fatigue, baseTimeValues);

      calendarContent += `<td>${day}<br>${selectedWorkout}<br> ${adjustedTimes.join(', ')}</td>`;
    }
  }

  // Set the generated calendar content
  plannerContent.innerHTML = `<tr>${calendarContent}</tr>`;
}


    function adjustTimes(workout, age, fitness, fatigue, baseTimeValues) {
      const times = [];

      const workoutDetails = {
        'BASE_1': ['10 min Warm Up', `TIME_A min Zone 3 Ride`, '5 min Cool Down'],
        'BASE_2': ['10 min Warm Up', `TIME_A min Zone 3 Ride`, `5x TIME_B Tempo Ride with 5 min recovery in between`, '5 min Cool Down'],
        'BASE_3': ['5 min Warm Up', '10 min Zone 3 Ride', `2x TIME_C min Sprints Ride with 3 min recovery in between`, '10 min Zone 3 Ride', '5 min Cool Down'],
        'INTERVAL_1': ['10 min Warm Up', `5x TIME_C min Sprints Ride with 5 min recovery in between`, '5 min Cool Down'],
        'INTERVAL_2': ['10 min Warm Up', `2x TIME_C min Sprints Ride with 3 min recovery in between`, '10 min Zone 3 Ride', '5 min Cool Down'],
        'INTERVAL_3': ['10 min Warm Up', `2x TIME_C min Sprints Ride with 3 min recovery in between`, '10 min Zone 3 Ride', '5 min Cool Down'],
        'TEMPO_1': ['5 min Warm Up', `5x TIME_D min Tempo Rides, with TIME_C Sprints in between`, '10 min Zone 3 Ride', '5 min Cool Down'],
        'TEMPO_2': ['15 min Zone 3 Ride', '5 min Warm Up', `5x TIME_D min Tempo Rides, with TIME_C Sprints in between`, '5 min Cool Down'],
        'TEMPO_3': ['5 min Warm Up', `10x TIME_D min Tempo Rides, with TIME_C Sprints in between`, '10 min Zone 3 Ride', '5 min Cool Down'],
		'EASY_1': ['5 min Warm Up', `TIME_A min Zone 2 Ride`, '5 min Cool Down'],
		'EASY_2': ['5 min Warm Up', `TIME_E min Zone 2 Ride`],
		'EASY_3': ['10 min Warm Up', `TIME_E min Zone 2 Ride`],
		'EASY_4': ['10 min Warm Up', `TIME_E min Zone 2 Ride`],
      };

      if (workout in workoutDetails) {
        for (let step of workoutDetails[workout]) {
          if (step.includes('min') && step.includes('TIME_')) {
            const timeKey = step.match(/TIME_[A-D]/)[0];
            const timeValue = baseTimeValues[timeKey];
            const adjustedTime = Math.round(
              (timeValue * (age < 12 ? 0.7 : (age < 15 ? 0.8 : (age < 17 ? 0.9 : 1)))) +
              (fitness >= 75 ? 5 : 0) -
              (fatigue >= 75 ? 5 : 0)
            );
            times.push(step.replace(/TIME_[A-D]/, `${adjustedTime}`));
          } else {
            times.push(step);
          }
        }
      }

      return times;
    }
  </script>

</body>
</html>


You did so much work in HTML, so I tried it in a WebViewer.
blocks
html_workout.aia (4.6 KB)

Aside from fitting the WebViewer into the Screen, it looks great as is.

I belong to the Quit While You're Ahead school.

Yes, but the html version is a bit limited. Cannot save user info and sessions, and is is bit more difficult for me to create and add workout types. So I want to try as a proper App.
Thanks for the help

You're clearly not a noob.

What do you see on the back end?

I havent doen this in a while, so Im rusty.

I am working with a list system, this is what I have, but am stuck:(

Is this app intended to run only on a personal device, or is it intended to share information with a trainer on another device by way of a central database?

Just for personal use. The fitness and fatige values are obtained from a 3rd part website.

I took a shot at this, got past validation of input, but left off at the critical workout generation phase.

I see two TinyDB NameSpaces for this,

  • a profile NameSpace, and
  • a Schedule NameSpace, tag = yyyy.MM.dd

but have not settled on a daily work out structure yet for storage.

List of dictionaries, most likely.



workout_creator.aia (9.7 KB)

I'll look at it after more thought, and leave it bookmarked.

Thanks, this helps a lot. I havent even think about using a TinyDB. I am still in HTML mode in my brain.
Will work on this and give feedback.

Appreciate it really

1 Like