---
title: Workout
tags: workout,overview
---
> [TOC]
## Use cases
- [ ] Add Terms - https://app.termly.io/
- [x] Create account
- [x] Login
- [x] Logout
- [ ] Show loading before calendar is ready
- [ ] Allow reporting duplicate exercise
- [ ] Bodyparts overview - change "8 days ago" to "more then week ago"
- [ ] Create public version of workout
- [ ] Rename workoutlab-3fb75.firebaseapp.com from login screen
- [ ] Search exercise names in aliases table
- [x] Add map preview to strava workouts list
- [ ] Store gpx point encoded in google
- [x] Create readonly exercise table
- [ ] Allow search using aliases
- [x] Strengthlevel ID
- [x] Create readonly workout list
- [ ] Allow filtering
- [ ] With/Without strava
- [ ] With/Without sourceFile
- [ ] Strava
- [ ] Strava list
- [ ] Show matching day/duration/calories/time flags
- [ ] Show strava workouts which can be mapped to workouts
- [ ] Show workouts which can be mapped to strava workouts
- [ ] Google api
- [ ] add version, buildNumber, buildTime
- [ ] to google analytics
- [x] to sentry
- [ ] User detail
- [ ] Add strava id
- [ ] Add runkeeper id
- [ ] Exercise detail
- [ ] hide aliases metadata
- [ ] make category metadata standalone field
- [ ] Add button "show who practiced this"
- [ ] Strength level, stats, my stats > move to menu next to hearth
- [ ] Add workout visibility select
- [ ] Add workout with multi autocomplete to select users
- [ ] Workout detail
- [ ] Add export workout button
- [ ] Add share button
- [ ]
- [ ] Exercise record view
- [ ] change default textarea view height to be equal number of rows
- [ ] remove record overview on mobiles
- [ ] Set personal information
- [x] Birthday
- [x] Height
- [x] Weight
- [x] Gender
- [ ] TargetWeight
- [ ] TargetWeightDate
- [ ] Calculate BMI https://www.calculatorsoup.com/calculators/health/bmi-calculator.php
- [x] Allow to add file (GPX, Image) as attachment to workout
- [ ] Allow to create workout from KML format
- [ ] Create sidenav menu expandable - https://indepth.dev/tutorials/angular/angular-material-nested-menu-dynamic-data
- [ ] for non dev users
- [x] hide creation date
- [ ] GUI Improvements
- [ ] Create user chip
- [x] Image preview min-width: min(80vw, 100%);
- [ ] Created exercises must be approved by approvers
- [ ] Add color to bodyparts 
- [ ] Add functionality from https://www.runloop.com/heavyset
- [ ] personl records for reps for each exercise
- [ ] Routines with series and reps withoud weights
- [ ] Workout overview 
- [ ] Add functionality from https://play.google.com/store/apps/details?id=com.kg.app.sportdiary&hl=en_GB&gl=US
- [ ] Show received achievemnt after workout's record
- [ ] Add graph for number of practises for each month for each exercise
- [ ] Calendar driven workout overview
- [ ] Rename stats:
- [ ] Finished exercises/workouts
- [ ] Max weight and max reps 
- [ ] show max 1 rep weight graph 
- [ ] Create Routine
- [ ] Set name
- [ ] Add exercises
- [ ] Set Reminder
- [ ] Start date
- [ ] Until
- [ ] Number of weeks/days/months
- [ ] Specific date
- [ ] X repetations
- [ ] Send mail notification
- [x] Create workout
- [x] Add record to workout
- [x] Fill properties (start/end date and type, note, place, burned callories)
- [x] Save workout
- [ ] Create workout from routine
- [ ] Choose routine
- [ ] Fill values for each exercise record
- [ ] Fill missing properties
- [ ] Save workout
- [x] Show exercise records
- [ ] Clone workout into my workouts
- [ ] Improve diagrams
- 
- [ ] Create routine from workout
- [ ] Add musscle selector
- [x] https://codepen.io/tomaev/pen/dYrEbq
- document.querySelectorAll("[class=\"\"]")
- [ ] https://codepen.io/baublet/pen/PzjmpL
- [ ] https://codepen.io/MikeLouviere/pen/MyyWNe
- [ ] Create single record workout creationg page
- [ ] Create profile card

- [ ] Add bite flag for workout about exercises bodyparts
- [ ] add action row to expandable panel and wrap buttons inside of it
- [ ] Each workout has either calculated or user defined burned calories
- [ ] Add visibility option in workoute detail
- [ ] Handle empty detail actions menu
- [x] Show my workouts
- [ ] Export my workouts
- [x] Edit existing workout
- [x] Add record to workout
- [x] Remove record from workout
- [x] Edit record in workout
- [x] Edit workout properties
- [x] Save workout
- [x] Create exercise record
- [x] Choose exercise
- [x] Set attributes (weigth, repetations, note)
- [x] Save record
- [x] Exit exercise record
- [x] Change exercise
- [x] Edit attributes (weigth, repetations, note)
- [x] Save record
- [x] Remove existing workout
- [ ] Show map for outside activities
- [ ] Allow to import exercise record from external application
- [ ] Runkeeper
- [x] GPX file
- [ ] Strava
- [ ]
- [ ] Show recommendations
- [ ] Show which exercies should be practised
- [ ] Show week/month/year/total stats
- [x] Number of workouts
- [ ] Duration
- [ ] Information for each exercise
- [ ] Duration, count, percentage
- [ ] Show stats for exercise
- [ ] Maximal repetations/weight
- [ ] List of dates when it was practise
- [ ] Social
- [ ] Search users
- [x] Add/remove friend
- [ ] Friends are automatically fallowed
- [x] Show user detail page
### Create workout
#### online
```plantuml
start
:click on new workout button;
:select start date and time;
:optionally select end date and time;
repeat :click on save button;
:send request to server;
:wait for response;
backward :fix all issues;
repeat while (Error response) is (true)
-> no;
:show success notification;
:update store;
:redirect to detail of workout;
stop
```
#### offline
```plantuml
start
:click on new workout button;
:select start date and time;
:optionally select end date and time;
repeat :click on save button;
:validate workout locally;
backward :fix all issues;
repeat while (validation failed) is (true)
-> no;
:save request to localstorage;
:show success notification;
:update store and add flag that workout is not saved on server;
:redirect to detail of workout;
stop
```
## Endpoints
### Workout
- `[GET] /workout`
- minDate - minimal start date
- maxDate - maximal end date
- year - get workouts for specific year
- month - get workouts for specific month
- week - get workouts for specific week
- minDuration - minimal duration
- maxDuration - maximal duration
- bodyPart - filter for workout with exercises for specific body parts
- orderBy - creationDate, startDate, duration, callories
- orderDirection - asc, desc
- `[POST] /workout`
- Create new workout
- `[GET] workout/:workoutId`
- Get workout detail
- `[PUT] workout/:workoutId`
- Update workout
- `[DELETE] workout/:workoutId`
- Delete workout
- `[GET] workout/:workoutId/exercise_records`
- Same as as /exercise_record but for specific workout
- `[POST] workout/:workoutId/exercise_records`
- Same as as /exercise_record but for specific workout
### Exercises
- exercise
- Get
- Get list of exercises
- bodyPart - body part or list of body parts
- key - search by name
- orderBy - creationDate, name
- orderDirection - asc, desc
- Post
- Create new exercise
- exercise/{exerciseId}
- Get
- Get exercise detail
- Put
- Update exercise
- Delete
- Delete exercise
### Exercise records
- exercise_record/{exertciseRecordId}
- Get
- Get exercise record detail
- Put
- Update exercise record
- Delete
- Delete exercise record
- exercise_record
- Get
- Get exercise records
- Post
- Create exercise record
### User
- user
- Get
- Get list of users
- key - pattern for user name
- orderBy - creationDate, name, numberOfWorkouts, followers
- orderDirection - asc, desc
- Post
- Create new user
- user/{userId}/socials/addFriend/{targetUserId}
- Post
- Add friend to user
- user/{userId}/socials/removeFriend/{targetUserId}
- Post
- Remove friend from user
- user/{userId}/socials/follow/{targetUserId}
- Post
- Follow user
- user/{userId}/socials/unfollow/{targetUserId}
- Post
- Unfollow user
- user/{userId}/profile
- Get
- Get user profile
- user/{userId}/stats/{weekly,monthly,yearly}
- Get
- Get user weekly,monthly,yearly stats
- user/{userId}/workout
- Get
- Same as /workout but for specific user
## Models
### Workout
Single workout session. Include multiple exertice records
```plantuml
@startmindmap
* Workout
** ID
** CreationDate
** StartDate
** EndDate
** Comment
** ExerciseRecords
** BurnedCallories
@endmindmap
```
### Exercise
F - Forbitten
R - Required
O - Optional
| Parameter | Create | Update |
| -------- | -------- | -------- |
| ID | F | R |
|Name|R|O|
|type|R|O|
|bodypart|R|O|
|creationDate|F|F|
|description|O|O|
|createdBy|F|F|
|standing|O|O|
|outside|O|O|
|difficulty|O|O|
Exercise definition
```plantuml
@startmindmap
* Exercise
** Id
** Description
** CreatedBy
** CreationDate
** Image
** Visible for (me/friends/all)
** Bodypart string or bodyparts
** Required/Optional properties
*** Duration
*** Distance
*** Repetitions
*** Weight
*** Series
*** Equipment
*** GPX for running
*** Avg Heart Rate in BPM
@endmindmap
```
### App stats
Exercises: all/approved
Bodypart histogram
Category histogram
Workouts total number
Users total number
Graphs
- number/hours of workouts per months
### Exercise stats
#### BasicExerciseStatistics
| Name | Type | Description |
| -------- | -------- | -------- |
| totalNumberOfExercising | number | |
| maxRepetitions | number | |
| maxWeight | number | |
| maxLifted | number | |
| maxWeightForReps | `{ [reps in number]: number }` | |
| maxRepsForWeight | `{ [weight in number]: number }` | |
#### ExerciseStatistics extends [BasicExerciseStatistics](BasicExerciseStatistics)
| Name | Type | Description |
| -------- | -------- | -------- |
| uniqueUsersExercisingThisExercise | number | |
| id | ExerciseId | |
| updatedTime | string | |
| favouriteBy | `{date: string; value: number}` | |
### User stats
#### UserExerciseStats extends [BasicExerciseStatistics](BasicExerciseStatistics)
| Name | Type | Description |
| -------- | -------- | -------- |
| exerciseId | ExerciseId | |
| userId | UserId | |
| updatedTime | string | |
| yearStats | `{ [year in number]: BasicExerciseStatistics }` | |
| monthStats | `{ [month in number]: BasicExerciseStatistics }` | |
#### UserWorkoutStats
| Name | Type | Description |
| -------- | -------- | -------- |
| exerciseHistogram | `[key: string, value: number, exerciseId: ExerciseId][]` | |
| bodypartHistogram | `[key: BodyPart, value: number][]` | |
| totalExercises | number | |
| iniqueExercises | number | |
| totalWorkouts | number | |
| totalWorkoutDuration | number | |
| maxWorkoutDuration | number | |
| totalBurnedCalories | number | |
| maxBurnedCalories | number | |
### CustomExercise
Exercise created by user
```plantuml
@startmindmap
* CustomExercise
** Extends Exercise
** VerifiedBy
*** UserId
*** DateOfVerification
** Active/Inactive
** CreatedBy: UserId
** Type
*** Weight
**** Repetations
**** Series
**** Weight
*** Duration
**** Duration
*** Run
**** Distance
**** Duration
**** Average speed
*** Body weight
**** Repetations
**** Series
@endmindmap
```
### ExerciseRecord
```plantuml
@startmindmap
* ExerciseRecord
** ID
** Type:Exercise or CustomExercise
** Date
** Properties for Exercise
@endmindmap
```
### Routine
- Template for exercises for one workout
- Contains one or more exercises
- Cant have reminders
## Exercises
```plantuml
@startmindmap
* Exertices
** Pushup
*** Normal
*** Knee pushup
*** Pike pushups
*** One-legged pushup
** Abs
*** Bicycle crunch
*** Hollow hold to jackknife
*** Plank
**** One-leg Plank
**** One-arm Plank
**** Side plank
***** Side plank with hip abduction
** Squat
*** Overhead squat
** Lunge
*** Stationary lunge
*** Walking lunge
*** Jumping lunges
*** Elevated pike pushups
** Plank to Downward Dog
** Bird Dog
*** Advanced Bird Dog
** Dead bug
@endmindmap
```
Knee pushup
## Jobs
### CalculateExerciseStats
- cons MIN_RECALCULATION_OFFSET ~= 1 day
- find some exercise when metadata are missing
- if no one found then find metadat when now() - m.updatedTime > MIN_RECALCULATION_OFFSET
- find all workouts where creationDate > m.updateTime and has record with r.exerciseId === m.exerciseId
- generate new ExerciseMetadata from workouts
- updateDate is now
- merge previous and new (or set new if previous are missing) ExerciseMetadata
## Relations
### Database
```plantuml
struct WorkoutId
struct ExerciseId
struct ExerciseRecordId
struct UserId
class Workout {
+id: WorkoutId
+createdBy: UserId
+ownerId: UserId
+member: UserId
+participants?: UserId[]
+creationDate: DateTime
+updateDate: DateTime
+deletionDate?: DateTime
+sourceFileName?: string
+startDate: DateTime
+records: WorkoutRecord[]
+visibility: PUBLIC | PRIVATE
+endDate?: DateTime
+burendCalories: number
+userDefinedBurnedCalories?: number
+averageHeartBear: nubmber;
+note: string
}
enum BodyPart {
ABDOMINAL
BACK
BICEPS
CHEST
SHOULDERS
CALVES
LEGS
TRICEPS
}
enum ExerciseType {
WEIGHT
BODY_WEIGHT
DISTANCE
DURATION
}
class Exercise {
+id: ExerciseId
+description: string
+bodypars: BodyPart[]
+secondaryBodypars: BodyPart[]
+creationDate: DateTime
+deletionDate?: DateTime
+image?: "storage"
+createdBy: UserId
+standing?: boolean
+outside?: boolean
+MET?: number
+type: ExerciseType
+image: string
+variationOf?: ExerciseId
+similar?: ExerciseId[]
}
class ExerciseRecord {
+id: ExerciseRecordId
+workoutId: WorkoutId
+creationDate: DateTime
+deletionDate?: DateTime
+note?: string
+items: ExerciseRecordItem[]
}
class ExerciseRecordItem {
+repetitions?: number
+weight?: number
+duration?: number
+series?: number
+distance?: number
}
ExerciseId <-- ExerciseRecord
WorkoutId <-- ExerciseRecord
Workout --> WorkoutId
Workout --> UserId
Workout --> ExerciseId
ExerciseId <-- Exercise
UserId <-- Exercise
Exercise --> ExerciseType
Exercise --> BodyPart
ExerciseRecordItem <-- ExerciseRecord
ExerciseRecordId <-- ExerciseRecord
note left of Workout::participants
Additional participants
(Owner is participant by default)
end note
note left of Exercise::variationOf
Some exercises could be
extendsion for another exercises
end note
hide empty members
```
### Database - sets
```plantuml
enum SetEntryType {
+BREAK
+DURATION
+REPETITIONS
}
interface SetEntry {
+exerciseId: ExerciseId
+type: SetEntryType
+series?: number
+duration?: number
+repetitions?: number
}
class Set {
+id: SetId
+creationDate: DateTime
+createdBy: UserId
+entries: SetEntry[]
}
SetEntryType <-- SetEntry
SetEntry <-- Set
note right of SetEntry::series
If this is omited then value 1 is used
end note
hide empty members
```
### Database - metadata
```plantuml
interface RepSetEntry {
reps: number
sets: number
count: number
}
interface DateTimeValue {
date: DateTime
value: number
}
interface BasicExerciseMetadata {
mostRepsAndSets?: RepSetEntry[]
maxRepetitions: number
maxWeight: number
maxLifted: number
maxWeightForReps: Map<number, number>
recordsForReps: Map<number, number>
}
class ExerciseMetadata implements BasicExerciseMetadata {
id: ExerciseId
updateDate: DateTime
favouriteBy: DateTimeValue[]
}
interface BasicUserExerciseMetadata extends BasicExerciseMetadata {
maxWeightOverTime: DateTimeValue[]
maxLiftedOverTime: DateTimeValue[]
}
class UserMetadata {
id: UserId
updateDate: DateTime
longestWorkout: number
longestDistance: number
fastestPace: number
mostBurnedCalories: number
minAverageHeartBeat: number
maxAverageHeartBeat: number
createdExercises: number
finishedExercise: number
finishedWorkouts: number
exerciseRecords: Map<ExerciseId, BasicUserExerciseMetadata>
}
RepSetEntry <-- BasicExerciseMetadata
BasicUserExerciseMetadata <-- UserMetadata
note right of BasicExerciseMetadata::mostRepsAndSets
Only for exercise of type WEIGHT
end note
note right of BasicExerciseMetadata::maxRepetitions
Maximal repetitions in one series
end note
note right of BasicExerciseMetadata::maxWeight
value from recordsForReps[1]
end note
note right of BasicExerciseMetadata::maxLifted
Series * Reps * Weight
end note
hide empty members
```
### API
```plantuml
interface NamedUserDTO {
+id: UserId
+name: string
}
interface UserDTO extends NamedUserDTO {
+avatar: string
}
interface UserDetailDTO extends UserDTO {
+properties: any
+preferences: any
}
interface WorkoutDTO {
+id: WorkoutId
+startDate: DateTime
'Duration in MS
+endDate: DateTime
+owner: NamedUserDTO
}
interface WorkoutDetailDTO extends WorkoutDTO{
+records: ExerciseRecord[]
}
interface ExerciseDTO {
+id: ExerciseId
+description: string
+bodypars: BodyPart[]
+image: string
}
interface ExerciseDetailDTO extends ExerciseDTO {
+creationDate: DateTime
}
interface ExerciseRecordDTO {
+id: ExerciseRecordId
+exercise: Exercise
+properties: JsonObject
+items: WorkoutItemDTO[]
}
interface WorkoutItemDTO {
+weight?: number
+repetitions?: number
+series?: number
+points?: WorkoutItemPointDTO[]
}
interface WorkoutItemPointDTO {
+lat: number
+long: number
+altitude: number
+timeOffset: number
}
WorkoutItemDTO *-- WorkoutItemPointDTO
ExerciseRecordDTO *-- WorkoutItemDTO
WorkoutDTO *-- NamedUserDTO
WorkoutDetailDTO *-- ExerciseRecordDTO
ExerciseRecordDTO *-- ExerciseDTO
hide empty members
```
#### API examples
##### Workout detail
###### bench press
```plantuml
@startjson
{
"id": "0caf6d5b-6b45-452b-ac11-bac6181c9578",
"startDate": "2023-04-17T07:09:29.780Z",
"endDate": "2023-04-17T07:58:29.780Z",
"owner": {
"id": "fc0a9b94-ef77-46b5-b7aa-31b469608912",
"name": "Some User "
},
"records": [
{
"id":"51e31ea1-d453-416b-a580-61327400a150",
"exercise": {
"id":"f0d5091b-a7d6-4e58-bc82-134b9b511c22",
"name": "Bench press",
"bodyparts": ["CHEST"],
"image":"https://workout-lab/exercises/f0d5091b-a7d6-4e58-bc82-134b9b511c22/image/320.png"
},
"items": [
{
"weight": 80,
"series": 2,
"repetitions": 12
},
{
"weight": 80,
"series": 2,
"repetitions": 10
}
]
}
]
}
@endjson
```
###### Running
```plantuml
@startjson
{
"id": "0caf6d5b-6b45-452b-ac11-bac6181c9578",
"startDate": "2023-04-17T07:09:29.780Z",
"endDate": "2023-04-17T07:58:29.780Z",
"owner": {
"id": "fc0a9b94-ef77-46b5-b7aa-31b469608912",
"name": "Some User "
},
"records": [
{
"id":"51e31ea1-d453-416b-a580-61327400a150",
"exercise": {
"id":"f0d5091b-a7d6-4e58-bc82-134b9b511c22",
"name": "Running",
"bodyparts": ["EGS"],
"image":"https://workout-lab/exercises/f0d5091b-a7d6-4e58-bc82-134b9b511c22/image/320.png"
},
"items": [
{
"lat": 80,
"long": 2,
"altitude": 12,
"timeOffset": 0
},
{
"lat": 80,
"long": 2,
"altitude": 13,
"timeOffset": 2000
}
]
}
]
}
@endjson
```
## Operations
### Create template
```plantuml
interface Template {
+creationDate: string
}
interface ParsedTemplate {
+creationDate: string
}
interface WorkoutCreation {
}
hide empty members
```
```plantuml
@startuml
box Client
participant User
participant TemplateComponent
participant TemplateService
end box
box Server
participant WorkoutController
participant WorkoutService
participant ExerciseService
end box
User -> TemplateComponent: paste text
TemplateComponent -> TemplateService: parseTemplate(template)
TemplateComponent <- TemplateService: ParsedTemplate
User -> TemplateComponent: submit form
TemplateComponent -> TemplateService: submitTemplate()
TemplateService --> WorkoutController: [POST]
WorkoutController -> WorkoutService: processTemplate(template)
WorkoutService -> ExerciseService: findExercisesByNames(names)
WorkoutService <-- ExerciseService
WorkoutController <-- WorkoutService
TemplateService <-- WorkoutController: response
==Try to get exercise names==
TemplateService -> TemplateComponent: showExercisesTable()
opt Some exercises are incorrect
loop For each incorect exrcise
alt Exercise is second options in reasult
User -> TemplateComponent: click context menu
User -> TemplateComponent: choose correct exercise
else Exercise wasn't found or is missing in found exercises
User -> TemplateComponent: click context menu
User -> TemplateComponent: click on search button
TemplateComponent -> TemplateComponent: showExerciseLookupDialog()
User -> TemplateComponent: search and select correct exercise
end
end
end
User -> TemplateComponent: confirm exercises
TemplateComponent -> TemplateService: WorkoutCreation()
TemplateService --> WorkoutController: [POST]
WorkoutController -> WorkoutService: createWorkoutCreation(parsedTemplate)
WorkoutController <-- WorkoutService
TemplateService <-- WorkoutController
==Show creating workout overview==
TemplateService -> TemplateComponent: Show overview()
opt Some fields are incorrect
User -> TemplateComponent: update field
end
User -> TemplateComponent: click create button
TemplateComponent -> TemplateService: createWorkoutFromTemplate(WorkoutCreation, ParsedTemplate)
TemplateService --> WorkoutController: [POST]
WorkoutController --> WorkoutService: createWorkoutFromTemplate(WorkoutCreation, ParsedTemplate)
WorkoutService -> ExerciseService: update searched exercise names and choosen ones inside tabase
@enduml
```
### Import external file
```plantuml
@startuml
Client --> WorkoutController: processFile(File, ProcessFileOptions)
WorkoutController --> WorkoutService: processFile(File, ProcessFileOptions)
WorkoutService --> WorkoutService: validateUserPermission(User, PROCESS)
WorkoutService --> FileImporter: processFile(File, ProcessFileOptions)
FileImporter --> FileImporter: processFileLocaly(File, ProcessFileOptions)
FileImporter --> FileProcessor: processFile(File, ProcessFileOptions)
FileImporter <- FileProcessor: CompactWorkoutCreateDto
FileImporter --> WorkoutBurnedCaloriesCalculator: getCaloriesFor()
FileImporter <- WorkoutBurnedCaloriesCalculator: number
WorkoutService<- FileImporter: CompactWorkoutCreateDto
WorkoutController <- WorkoutService: CompactWorkoutCreateDto
Client <-- WorkoutController: CompactWorkoutCreateDto
@enduml
```
### Get workout detail/list
```plantuml
@startuml
Client --> WorkoutController: getWorkout(WorkoutId)
WorkoutController --> WorkoutService: getWorkout(WorkoutId)
WorkoutService --> WorkoutService: validateUserPermission(USER, READ)
WorkoutService --> WorkoutDatabase: getWorkout(WorkoutId)
WorkoutDatabase --> DB
WorkoutDatabase <- DB: WorkoutModel
WorkoutDatabase -> WorkoutDatabase: WorkoutModelToWorkout(WorkoutModel)
WorkoutService <- WorkoutDatabase: SimpleWorkout
WorkoutService -> WorkoutService: getWorkoutUsers(SimpleWorkout)
WorkoutService -> WorkoutService: getWorkoutExercises(SimpleWorkout)
WorkoutService --> ExerciseService:getExercisesByIds(Array<ExerciseId>)
WorkoutService --> UserService:getUsersByIds(Array<UserId>)
WorkoutService -> WorkoutService: createWorkout(WorkoutModel, Array<Exercise>, Array<User>)
WorkoutController <- WorkoutService : Workout
Client <- WorkoutController : Workout
@enduml
```
### Create workout
```plantuml
@startuml
Client --> WorkoutController: createWorkout(WorkoutCreateDto)
WorkoutController --> WorkoutService: createWorkout(WorkoutCreateDto, User)
WorkoutService --> WorkoutService: validateUserPermission(USER, READ)
group is has missing burned calories
WorkoutService -> CaloriesCalculator: calculateCalories(WorkoutCreateDto)
WorkoutService <- CaloriesCalculator: BurnedCalories
WorkoutService -> WorkoutService: setCaloriesToWorkout()
end
WorkoutService --> WorkoutDatabase: createWorkout(WorkoutCreateDto, User)
WorkoutDatabase --> DB
WorkoutDatabase <- DB: WorkoutModel
WorkoutDatabase -> WorkoutDatabase: WorkoutModelToSimpleWorkout()
WorkoutService <- WorkoutDatabase: SimpleWorkout
WorkoutService--> WorkoutService: SimpleWorkoutToWorkout()
WorkoutController <- WorkoutService : Workout
Client <- WorkoutController : Workout
@enduml
```
### Calculate exercise statistics
### Calculate exercise user statistics
### Calculate user statistics
## Screens
### Home screen
{%figma https://www.figma.com/file/SigS4MEFM8u1BUT9j8YZhX/WorkoutLogger?node-id=201%3A4
%}
### List of exercises
{%figma https://www.figma.com/file/SigS4MEFM8u1BUT9j8YZhX/WorkoutLogger?node-id=204%3A90
%}
### Exercise detail
{%figma https://www.figma.com/file/SigS4MEFM8u1BUT9j8YZhX/WorkoutLogger?node-id=203%3A16
%}
### Add exercise
### Add Workout
#### Empty workout preview
```plantuml
@startsalt
{^"Create workout"
[Add exercise<&plus>]
[<color:#9a9a9a>Save] | [Cancel]
}
@endsalt
```
#### Add exercise dialog
```plantuml
@startsalt
{^"Add Exercise record dialog"
^Choose exercise record^
}
@endsalt
```
```plantuml
@startsalt
{^"Add Exercise record dialog"
^Choose Exercise record^^ Normal pushup^^ Knee pushup^
}
@endsalt
```
```plantuml
@startsalt
{^"Add Exercise record dialog"
^Normal Pushup^
Repetitions | "12"
Series | "5"
Duration | " "
}
@endsalt
```
{%figma https://www.figma.com/file/SigS4MEFM8u1BUT9j8YZhX/WorkoutLogger?node-id=284%3A667
%}
#### Save workout
```plantuml
@startsalt
{^"Create workout"
Normal pushups (12 * 5) | ["Remove"<&trash>]
[Add exercise<&plus>]
[Save] | [Cancel]
}
@endsalt
```
### Workout detail
### List of workouts
{%figma https://www.figma.com/file/SigS4MEFM8u1BUT9j8YZhX/WorkoutLogger?node-id=204%3A125
%}
## TODO
- [ ] Optimize user activities logging to bulk update
- [ ] Add 'create workout' icon to toolbar
- [ ] Exercise lookup component
- [ ] Add favourite exercises to the menu
- [ ] Multi workout overview
- [ ] Allow to create empty workout
- [ ] Simple way to add exerciseRecord
- [x] Hide error notification after logout
- [x] Add default permissions for new users
- [ ] Create separate endpoint for update exercise records
- [ ] Statistics https://codepen.io/marcnewport/pen/xEKWpz
- [ ] Realtime recording https://codepen.io/echodean/details/EEyGzZ
- [ ] Add more bodyparts
- rear_deltoids
- soleus_muscle
- triceps
- traps
- lats
- mid_back
- low_back
- glutes
- hamstring
- calves
- quads
- abs
- forearms
- biceps
- chest
- shoulders
- neck
## Features
#### Simple overview of your workouts

##### Workout statistics

#### Exercise detail

##### Exercise statistics

##### My exercise statistics

#### User profile
##### User properties

##### Customizable

##### User statistics

#### Workout detail

##### With simple exercise recods preview

#### Dark/Light theme

#### Workout statistics

## Future features
#### Offline support
#### Advanced workout recommendations
#### Realtime mode for breaks timeout, voice commands
#### File import (GPX, KML)
#### Allow exporting to different formats
#### Add share options (facebook, twitter)
#### Realtime running tracker (Like Runkeeper)
#### Advanced graphs and statistics
#### Integration with external apps (Runkeeper, Strava, Huawei Health, Zepp life)