Test Failed
Push — master ( c56ed7...f22d86 )
by Valentyn
19:09
created

getCurrentWorkoutIndex(InUser,List)   B

Complexity

Conditions 6

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 10
c 1
b 0
f 0
dl 0
loc 13
rs 8.6666
cc 6
1
package com.osomapps.pt.admin.program;
2
3
import com.osomapps.pt.dictionary.DictionaryName;
4
import com.osomapps.pt.dictionary.DictionaryService;
5
import com.osomapps.pt.exercises.Exercise;
6
import com.osomapps.pt.exercises.ExerciseRepository;
7
import com.osomapps.pt.programs.InProgram;
8
import com.osomapps.pt.programs.InProgramRepository;
9
import com.osomapps.pt.programs.InWarmupWorkoutItem;
10
import com.osomapps.pt.programs.InWarmupWorkoutItemRepository;
11
import com.osomapps.pt.programs.InWorkout;
12
import com.osomapps.pt.programs.InWorkoutItem;
13
import com.osomapps.pt.programs.InWorkoutItemSet;
14
import com.osomapps.pt.programs.InWorkoutRepository;
15
import com.osomapps.pt.programs.ParseExercise;
16
import com.osomapps.pt.programs.ParseWorkout;
17
import com.osomapps.pt.reportworkout.InWorkoutItemRepository;
18
import com.osomapps.pt.token.InUser;
19
import java.util.Arrays;
20
import java.util.List;
21
import java.util.Optional;
22
import java.util.stream.Collectors;
23
import org.springframework.stereotype.Service;
24
import com.osomapps.pt.programs.ParseProgram;
25
import com.osomapps.pt.programs.ParseProgramRepository;
26
import com.osomapps.pt.programs.ParseRound;
27
import com.osomapps.pt.programs.ParseWorkoutItem;
28
import com.osomapps.pt.programs.ParseWorkoutItemSet;
29
import com.osomapps.pt.reportworkout.InWorkoutItemSetRepository;
30
import com.osomapps.pt.token.InUserGoal;
31
import java.util.ArrayList;
32
import java.util.Collections;
33
import java.util.Objects;
34
import java.util.concurrent.atomic.AtomicInteger;
35
import lombok.extern.slf4j.Slf4j;
36
import org.springframework.data.domain.Sort;
37
38
@Slf4j
39
@Service
40
public class AdminProgramAssignService {
41
42
    private final InProgramRepository inProgramRepository;
43
    private final InWorkoutRepository inWorkoutRepository;
44
    private final InWorkoutItemRepository inWorkoutItemRepository;
45
    private final InWorkoutItemSetRepository inWorkoutItemSetRepository;
46
    private final ParseProgramRepository parseProgramRepository;
47
    private final InWarmupWorkoutItemRepository inWarmupWorkoutItemRepository;
48
    private final DictionaryService dictionaryService;
49
    private final ExerciseRepository exerciseRepository;
50
51
    AdminProgramAssignService(InProgramRepository inProgramRepository,
0 ignored issues
show
Comprehensibility introduced by
Constructor has 8 parameters, which is greater than 7 authorized.
Loading history...
52
            InWorkoutRepository inWorkoutRepository,
53
            InWorkoutItemRepository inWorkoutItemRepository,
54
            InWorkoutItemSetRepository inWorkoutItemSetRepository,
55
            ParseProgramRepository parseProgramRepository,
56
            InWarmupWorkoutItemRepository inWarmupWorkoutItemRepository,
57
            DictionaryService dictionaryService,
58
            ExerciseRepository exerciseRepository) {
59
        this.inProgramRepository = inProgramRepository;
60
        this.inWorkoutRepository = inWorkoutRepository;
61
        this.inWorkoutItemRepository = inWorkoutItemRepository;
62
        this.inWorkoutItemSetRepository = inWorkoutItemSetRepository;
63
        this.parseProgramRepository = parseProgramRepository;
64
        this.inWarmupWorkoutItemRepository = inWarmupWorkoutItemRepository;
65
        this.dictionaryService = dictionaryService;
66
        this.exerciseRepository = exerciseRepository;
67
    }
68
69
    public InUser assign(InUser inUser) {
70
71
        List<ParseProgram> parsePrograms = parseProgramRepository.findAll(new Sort(Sort.Direction.DESC, "id"));
72
        if (parsePrograms.isEmpty()) {
73
            log.warn("There are no programs found.");
74
            return inUser;
75
        }
76
        Optional<Integer> userGroup = getUserGroup(inUser);
77
        if (!userGroup.isPresent()) {
78
            log.warn("User group not recognized, level - {}, gender - {}", inUser.getD_level(), inUser.getD_sex());
79
            return inUser;
80
        }
81
        if (inUser.getInUserGoals().isEmpty()) {
82
            log.warn("Cannot create program for user without goals.");
83
            return inUser;
84
        }
85
        List<ParseRound> parseRounds = getRoundsGorGoalAndUserGroup(parsePrograms,
86
                inUser.getInUserGoals().get(0), userGroup.get());
87
        final List<ParseWorkout> parseWorkouts;
88
        final List<String> goalNames = new ArrayList<>();
89
        if (!parseRounds.isEmpty()) {
90
            goalNames.add(parseRounds.get(0).getParseUserGroup().getParseGoal().getName());
91
        }
92
        if (inUser.getInUserGoals().size() > 1) {
93
            List<ParseRound> parseRounds2 = getRoundsGorGoalAndUserGroup(parsePrograms,
94
                    inUser.getInUserGoals().get(1), userGroup.get());
95
            parseWorkouts = mergeLists(getParseWorkouts(parseRounds), getParseWorkouts(parseRounds2));
96
            if (!parseRounds2.isEmpty()) {
97
                goalNames.add(parseRounds2.get(0).getParseUserGroup().getParseGoal().getName());
98
            }
99
        } else {
100
            parseWorkouts = getParseWorkouts(parseRounds);
101
        }
102
        final AtomicInteger index = new AtomicInteger(-1);
103
        final InProgram inProgram = new InProgram()
104
                .setName("Test program for user " + inUser.getId())
105
                .setCurrent_workout_index(getCurrentWorkoutIndex(inUser, parseWorkouts))
106
                .setInWorkouts(parseWorkouts.stream().map(parseWorkout
107
                        -> new InWorkout()
108
                        .setD_workout_name(parseWorkout.getName())
109
                        .setWorkout_index(index.incrementAndGet())
110
                        .setPart_name(parseWorkout.getParsePart().getName())
111
                        .setGoal_index(goalNames.indexOf(parseWorkout.getParsePart().getParseRound().getParseUserGroup().getParseGoal().getName()))
112
                        .setInWarmupWorkoutItems(parseWorkout.getParseWarmupWorkoutItems().stream().map(parseWarmupWorkoutItem
113
                                -> new InWarmupWorkoutItem()
114
                                .setD_exercise_name(parseWarmupWorkoutItem.getName())
115
                                .setExercise_id(parseWarmupWorkoutItem.getExercise_id())
116
                                .setTime_in_sec(minToSec(parseWarmupWorkoutItem.getTime_in_min()))
117
                                .setSpeed(parseWarmupWorkoutItem.getSpeed())
118
                                .setIncline(parseWarmupWorkoutItem.getIncline())
119
                        ).collect(Collectors.toList()))
120
                        .setInWorkoutItems(parseWorkout.getParseWorkoutItems().stream().map(parseWorkoutItem
121
                                -> new InWorkoutItem()
122
                                .setD_exercise_name(parseWorkoutItem.getName())
123
                                .setExercise_id(parseWorkoutItem.getExercise_id())
124
                                .setD_exercise_type(getExerciseType(parseWorkoutItem))
125
                                .setInWorkoutItemSets(parseWorkoutItem.getParseWorkoutItemSets().stream().map(parseWorkoutItemSet
126
                                        -> generateInWorkoutItemSet(parseWorkoutItemSet,
127
                                                parsePrograms.get(0).getParseExercises(), inUser.getWeight())
128
                                ).collect(Collectors.toList()))
129
                                .setInWorkoutItemReports(Collections.emptyList())
130
                        ).collect(Collectors.toList()))).collect(Collectors.toList()));
131
        inProgram.setInUser(inUser);
132
        inProgram.getInWorkouts().forEach(inWorkout -> {
133
            inWorkout.setInProgram(inProgram);
134
            inWorkout.getInWarmupWorkoutItems().forEach(inWarmupWorkoutItem -> {
0 ignored issues
show
Java 8 introduced by
Remove useless curly braces around statement
Loading history...
135
                inWarmupWorkoutItem.setInWorkout(inWorkout);
136
            });
137
            inWorkout.getInWorkoutItems().forEach(inWorkoutItem -> {
138
                inWorkoutItem.setInWorkout(inWorkout);
139
                inWorkoutItem.getInWorkoutItemSets().forEach(inWorkoutItemSet -> {
0 ignored issues
show
Java 8 introduced by
Remove useless curly braces around statement
Loading history...
140
                    inWorkoutItemSet.setInWorkoutItem(inWorkoutItem);
141
                });
142
            });
143
        });
144
        inProgramRepository.save(inProgram);
145
        inWorkoutRepository.save(inProgram.getInWorkouts());
146
        inProgram.getInWorkouts().forEach(inWorkout -> {
147
            inWarmupWorkoutItemRepository.save(inWorkout.getInWarmupWorkoutItems());
148
            inWorkoutItemRepository.save(inWorkout.getInWorkoutItems());
149
            inWorkout.getInWorkoutItems().forEach(inWorkoutItem -> {
0 ignored issues
show
Java 8 introduced by
Remove useless curly braces around statement
Loading history...
150
                inWorkoutItemSetRepository.save(inWorkoutItem.getInWorkoutItemSets());
151
            });
152
        });
153
        return inUser;
154
    }
155
156
    int getCurrentWorkoutIndex(InUser inUser, List<ParseWorkout> parseWorkouts) {
157
        if (inUser.getInPrograms() == null || inUser.getInPrograms().isEmpty()) {
158
            return 0;
159
        }
160
        final InProgram inProgram = inUser.getInPrograms().get(inUser.getInPrograms().size() - 1);
161
        if (parseWorkouts.isEmpty() || parseWorkouts.size() != inProgram.getInWorkouts().size()) {
162
            return 0;
163
        }
164
        final InWorkout inWorkout = inProgram.getInWorkouts().get(inProgram.getCurrent_workout_index());
165
        if (parseWorkouts.get(inProgram.getCurrent_workout_index()).getName().equals(inWorkout.getD_workout_name())) {
166
            return inProgram.getCurrent_workout_index();
167
        }
168
        return 0;
169
    }
170
171
    private InWorkoutItemSet generateInWorkoutItemSet(ParseWorkoutItemSet parseWorkoutItemSet,
172
            List<ParseExercise> parseExercises, Float userWeight) {
173
        Optional<ParseExercise> parseExercise = parseExercises.stream().filter(
174
                exercise -> Objects.equals(exercise.getExercise_id(),
175
                parseWorkoutItemSet.getParseWorkoutItem().getExercise_id())).findFirst();
176
        Integer userGroup = Integer.parseInt(parseWorkoutItemSet.getParseWorkoutItem()
177
                .getParseWorkout().getParsePart().getParseRound().getParseUserGroup().getName());
178
        final Integer exercise_percent;
179
        if (parseExercise.isPresent()) {
180
            switch (userGroup) {
181
                case 1:
182
                    exercise_percent = parseExercise.get().getUser_group_1_percent();
183
                    break;
184
                case 2:
185
                    exercise_percent = parseExercise.get().getUser_group_2_percent();
186
                    break;
187
                case 3:
188
                    exercise_percent = parseExercise.get().getUser_group_3_percent();
189
                    break;
190
                case 4:
191
                    exercise_percent = parseExercise.get().getUser_group_4_percent();
192
                    break;
193
                default:
194
                    exercise_percent = 100;
195
            }
196
        } else {
197
            exercise_percent = 100;
198
        }
199
        final String exerciseBasis = parseExercise.isPresent()
200
                ? parseExercise.get().getBasis_for_calculations() : "";
201
        final Integer exerciseWeightPercent = Arrays.asList("Weight", "Time").contains(exerciseBasis)
202
                ? exercise_percent : null;
203
        final Integer exerciseRepetitionsPercent = "Reps".equals(exerciseBasis) ? exercise_percent : null;
204
        final Integer exerciseTimePercent = "Time".equals(exerciseBasis) ? exercise_percent : null;
205
        final Integer exerciseSpeedPercent = "Speed".equals(exerciseBasis) ? exercise_percent : null;
206
        return new InWorkoutItemSet()
207
                .setExercise_repetitions_percent(exerciseRepetitionsPercent)
208
                .setGoal_repetitions(parseWorkoutItemSet.getRepetitions())
209
                .setRepetitions(parseWorkoutItemSet.getRepetitions())
210
                .setRepetitions_to_failure(parseWorkoutItemSet.getRepetitions_to_failure())
211
                .setExercise_weight_percent(exerciseWeightPercent)
212
                .setGoal_weight_coef(parseWorkoutItemSet.getWeight())
213
                .setWeight(generateWeight(parseWorkoutItemSet.getWeight(),
214
                        exerciseWeightPercent, getWeightForUserGroup(userWeight, userGroup)))
215
                .setBodyweight(parseWorkoutItemSet.getBodyweight())
216
                .setExercise_time_percent(exerciseTimePercent)
217
                .setGoal_time_in_sec(minToSec(parseWorkoutItemSet.getTime_in_min()))
218
                .setTime_in_sec(minToSec(parseWorkoutItemSet.getTime_in_min()))
219
                .setExercise_speed_percent(exerciseSpeedPercent)
220
                .setGoal_speed(parseWorkoutItemSet.getSpeed())
221
                .setSpeed(parseWorkoutItemSet.getSpeed())
222
                .setIncline(parseWorkoutItemSet.getIncline())
223
                .setResistance(parseWorkoutItemSet.getResistance())
224
                .setExercise_basis(exerciseBasis);
225
    }
226
227
    Float getWeightForUserGroup(Float userWeight, Integer userGroup) {
228
        if (userWeight == null) {
229
            return null;
230
        }
231
        switch (userGroup) {
232
            case 1:
233
            case 2:
234
                return Math.min(100F, userWeight);
235
            case 3:
236
            case 4:
237
                return Math.min(75F, userWeight);
238
            default:
239
                return userWeight;
240
        }
241
    }
242
243
    private Float generateWeight(Float goalWeightCoef, Integer exerciseWeightPercent, Float userWeight) {
244
        if (goalWeightCoef != null && exerciseWeightPercent != null && userWeight != null) {
245
            return roundToEven(userWeight * goalWeightCoef * exerciseWeightPercent / 100);
246
        }
247
        return goalWeightCoef;
248
    }
249
250
    float roundToEven(Float floatValue) {
251
        return floatValue.intValue() - floatValue.intValue() % 2;
0 ignored issues
show
Bug introduced by
Math operands should be cast to prevent unwanted loss of precision when mixing types. Consider casting one of the operands of this subtraction to float.
Loading history...
252
    }
253
254
    private Integer minToSec(Float value) {
255
        return value == null ? null : Float.valueOf(value * 60).intValue();
0 ignored issues
show
Best Practice introduced by
Immediately boxing and unboxing a primitive has no effect. Consider removing the boxing to Float

For an explanation of boxing and autoboxing, please refer to the Java tutorial.

Loading history...
256
    }
257
258
    private List<ParseRound> getRoundsGorGoalAndUserGroup(List<ParseProgram> parsePrograms,
259
            InUserGoal inUserGoal, Integer userGroup) {
260
        final List<ParseRound> parseRounds = new ArrayList<>();
261
        parsePrograms.get(0).getParseGoals().forEach(parseGoal -> {
262
            boolean nameFound = getOnlySymbols(parseGoal.getName()).equalsIgnoreCase(
263
                    getOnlySymbols(getGoalName(inUserGoal)));
264
            if (nameFound) {
265
                final List<ParseRound> localParseRounds = new ArrayList<>();
266
                parseGoal.getParseUserGroups().forEach(parseUserGroup -> {
267
                    if (parseUserGroup.getName().equals("" + userGroup)) {
268
                        localParseRounds.addAll(parseUserGroup.getParseRounds());
269
                    }
270
                });
271
                parseRounds.addAll(multiplyLists(localParseRounds, parseGoal.getLoops()));
272
            }
273
        });
274
        return parseRounds;
275
    }
276
277
    List<ParseRound> multiplyLists(List<ParseRound> parseRounds, Integer loops) {
278
        if (loops == null) {
279
            return parseRounds;
280
        }
281
        final List<ParseRound> result = new ArrayList<>(parseRounds.size() * loops);
282
        parseRounds.forEach(parseRound -> {
283
            for (int index = 0; index < loops; index += 1) {
284
                result.add(parseRound);
285
            }
286
        });
287
        return result;
288
    }
289
290
    private String getOnlySymbols(String value) {
291
        return value.replace("on a specific distance", "").replace("ednurance", "endurance")
292
                .replaceAll("[\\s\\.\\,]+", "");
293
    }
294
295
    private String getGoalName(InUserGoal inUserGoal) {
296
        return Arrays.asList(dictionaryService.getEnValue(DictionaryName.goal_title,
297
                inUserGoal.getD_goal_title(), null),
298
                dictionaryService.getEnValue(DictionaryName.goal_title_2,
299
                        inUserGoal.getD_goal_title_2(), null)).stream().filter(Objects::nonNull).collect(Collectors.joining(", "));
300
    }
301
302
    private List<ParseWorkout> getParseWorkouts(List<ParseRound> parseRounds) {
303
        final List<ParseWorkout> parseWorkouts = new ArrayList<>();
304
        parseRounds.forEach(parseRound -> {
0 ignored issues
show
Java 8 introduced by
Remove useless curly braces around statement
Loading history...
305
            parseRound.getParseParts().forEach(parsePart -> {
0 ignored issues
show
Java 8 introduced by
Remove useless curly braces around statement
Loading history...
306
                parsePart.getParseWorkouts().forEach(parseWorkout -> {
0 ignored issues
show
Java 8 introduced by
Remove useless curly braces around statement
Loading history...
307
                    parseWorkouts.add(parseWorkout);
308
                });
309
            });
310
        });
311
        return parseWorkouts;
312
    }
313
314
    List<ParseWorkout> mergeLists(List<ParseWorkout> parseWorkouts1, List<ParseWorkout> parseWorkouts2) {
315
        final List<ParseWorkout> parseWorkouts = new ArrayList<>();
316
        for (int index = 0; index < Math.max(parseWorkouts1.size(), parseWorkouts2.size()); index += 1) {
317
            if (!parseWorkouts1.isEmpty()) {
318
                parseWorkouts.add(parseWorkouts1.get(index % parseWorkouts1.size()));
319
            }
320
            if (!parseWorkouts2.isEmpty()) {
321
                parseWorkouts.add(parseWorkouts2.get(index % parseWorkouts2.size()));
322
            }
323
        }
324
        return parseWorkouts;
325
    }
326
327
    String getExerciseType(ParseWorkoutItem parseWorkoutItem) {
328
        List<Exercise> exercises = exerciseRepository.findByExerciseId(parseWorkoutItem.getExercise_id());
329
        if (exercises.isEmpty()) {
330
            return "OnRepetitions";
331
        }
332
        if (exercises.get(0).getExerciseTypes().size() == 1) {
333
            return exercises.get(0).getExerciseTypes().get(0).getName();
334
        }
335
        if (parseWorkoutItem.getParseWorkoutItemSets().stream()
336
                .filter(set -> set.getTime_in_min() != null).findFirst().isPresent()) {
0 ignored issues
show
introduced by
Replace this "filter().findFirst().isPresent()" chain with "anyMatch()"
Loading history...
337
            return "OnTime";
338
        }
339
        return "OnRepetitions";
340
    }
341
342
    enum Gender {
343
        male, female, unknown
344
    }
345
346
    enum Level {
347
        unexperienced, experienced, unknown
348
    }
349
350
    private Optional<Integer> getUserGroup(InUser inUser) {
351
        final Gender gender;
352
        if ("male".equalsIgnoreCase(inUser.getD_sex())) {
353
            gender = Gender.male;
354
        } else if ("female".equalsIgnoreCase(inUser.getD_sex())) {
355
            gender = Gender.female;
356
        } else {
357
            gender = Gender.unknown;
358
        }
359
        final Level level;
360
        if ("1".equalsIgnoreCase(inUser.getD_level())) {
361
            level = Level.unexperienced;
362
        } else if ("2".equalsIgnoreCase(inUser.getD_level())) {
363
            level = Level.experienced;
364
        } else {
365
            level = Level.unknown;
366
        }
367
        final Integer userGroup;
368
        if (gender == Gender.male && level == Level.experienced) {
369
            userGroup = 1;
370
        } else if (gender == Gender.male && level == Level.unexperienced) {
371
            userGroup = 2;
372
        } else if (gender == Gender.female && level == Level.experienced) {
373
            userGroup = 3;
374
        } else if (gender == Gender.female && level == Level.unexperienced) {
375
            userGroup = 4;
376
        } else {
377
            userGroup = null;
378
        }
379
        return Optional.ofNullable(userGroup);
380
    }
381
}
382