Deep reimplementations based on the new database schema.

Commit
7242d0e8ad804f0425374f3ac33a89ccfdbd546f
Author
Marius Peter <marius.peter@tutanota.com>
Author date
Committer
Marius Peter <marius.peter@tutanota.com>
Committer date
Changed files
mft-schema.sql
index 97e7f75a..1c28a90c 100644..100644
@@ -225,8 +225,7 @@
225 225 );
226 226
227 227 INSERT INTO Exercises ("primary_muscle_group_id", "english_name", "french_name") VALUES
228 Removed: -- Leading zero enables using sort-lines in Emacs
229 Removed: (02, 'cable row', 'rame avec cable'),
228 Added: ('02', 'cable row', 'rame avec cable'),
230 229 ('02', 'deadlift', 'soulevé de terre'),
231 230 ('02', 'lat pulldown', NULL),
232 231 ('02', 'pull up', 'traction'),
@@ -244,20 +243,11 @@
244 243 ('12', 'leg extension', NULL),
245 244 ('13', 'leg curl', NULL);
246 245
247 Removed: CREATE TABLE "Weights" (
248 Removed: "id" INTEGER UNIQUE,
249 Removed: "user_id" INTEGER NOT NULL,
250 Removed: "date" TEXT NOT NULL,
251 Removed: "value" NUMERIC NOT NULL,
252 Removed: PRIMARY KEY("id" AUTOINCREMENT)
253 Removed: );
254 Removed:
255 246 CREATE TABLE "Workouts" (
256 247 "id" INTEGER UNIQUE,
257 248 "user_id" INTEGER NOT NULL,
258 249 "date" TEXT NOT NULL,
259 Removed: "primary_muscle" TEXT,
260 Removed: "secondary_muscles" TEXT,
250 Added: "primary_muscle_group" TEXT,
261 251 PRIMARY KEY("id" AUTOINCREMENT)
262 252 );
263 253
@@ -271,6 +261,14 @@
271 261 "rpe" INTEGER,
272 262 "rest_after" NUMERIC,
273 263 FOREIGN KEY("workout_id") REFERENCES Workouts("id"),
264 Added: PRIMARY KEY("id" AUTOINCREMENT)
265 Added: );
266 Added:
267 Added: CREATE TABLE "Weights" (
268 Added: "id" INTEGER UNIQUE,
269 Added: "user_id" INTEGER NOT NULL,
270 Added: "date" TEXT NOT NULL,
271 Added: "value" NUMERIC NOT NULL,
274 272 PRIMARY KEY("id" AUTOINCREMENT)
275 273 );
276 274
src/MyFitnessTracker/MFTConfirmDialog.class.st
index cfb05472..629ac8ce 100644..100644
@@ -1,14 +1,15 @@
1 1 Class {
2 2 #name : #MFTConfirmDialog,
3 3 #superclass : #MFTDialog,
4 Removed: #category : #'MyFitnessTracker-Dialogs'
4 Added: #category : #'MyFitnessTracker-Dialog'
5 5 }
6 6
7 7 { #category : #'as yet unclassified' }
8 8 MFTConfirmDialog >> heading [
9 9
10 Removed: ^ 'You are about to ' , action , ' a ' , component class asString
11 Removed: , ':'
10 Added: ^ 'You are about to {1} a {2}:' format: {
11 Added: action.
12 Added: component class }
12 13 ]
13 14
14 15 { #category : #rendering }
src/MyFitnessTracker/MFTDateDialog.class.st
index 3737d6a1..a05f7316 100644..100644
@@ -4,7 +4,7 @@
4 4 #instVars : [
5 5 'date'
6 6 ],
7 Removed: #category : #'MyFitnessTracker-Dialogs'
7 Added: #category : #'MyFitnessTracker-Dialog'
8 8 }
9 9
10 10 { #category : #accessing }
src/MyFitnessTracker/MFTDialog.class.st
index 18560d25..c2d5e347 100644..100644
@@ -7,7 +7,7 @@
7 7 'action',
8 8 'completion'
9 9 ],
10 Removed: #category : #'MyFitnessTracker-Dialogs'
10 Added: #category : #'MyFitnessTracker-Dialog'
11 11 }
12 12
13 13 { #category : #actions }
src/MyFitnessTracker/MFTExercise.class.st
index 371a7a08..927b68c5 100644..100644
@@ -34,11 +34,10 @@
34 34 #name : #MFTExercise,
35 35 #superclass : #Object,
36 36 #instVars : [
37 Removed: 'canonicalName',
37 Added: 'id',
38 Added: 'primaryMuscleGroupId',
38 39 'englishName',
39 Removed: 'sets',
40 Removed: 'primaryMuscle',
41 Removed: 'secondaryMuscles'
40 Added: 'frenchName'
42 41 ],
43 42 #category : #'MyFitnessTracker-Model'
44 43 }
@@ -151,6 +150,17 @@
151 150 ]
152 151
153 152 { #category : #'instance creation' }
153 Added: MFTExercise class >> newFromDictionary: aDictionary [
154 Added:
155 Added: ^ self new
156 Added: id: (aDictionary at: 'id');
157 Added: primaryMuscleGroupId: (aDictionary at: 'primary_muscle_group_id');
158 Added: englishName: (aDictionary at: 'english_name');
159 Added: frenchName: (aDictionary at: 'french_name');
160 Added: yourself
161 Added: ]
162 Added:
163 Added: { #category : #'instance creation' }
154 164 MFTExercise class >> newWithEnglishName [
155 165 "I return a MFTExercise instance for which englishName is set to the calling method selector, as a space-separated string."
156 166
@@ -224,18 +234,6 @@
224 234 ]
225 235
226 236 { #category : #accessing }
227 Removed: MFTExercise >> canonicalName [
228 Removed:
229 Removed: ^ canonicalName
230 Removed: ]
231 Removed:
232 Removed: { #category : #accessing }
233 Removed: MFTExercise >> canonicalName: anObject [
234 Removed:
235 Removed: canonicalName := anObject
236 Removed: ]
237 Removed:
238 Removed: { #category : #accessing }
239 237 MFTExercise >> englishName [
240 238
241 239 ^ englishName
@@ -247,57 +245,53 @@
247 245 englishName := aString
248 246 ]
249 247
250 Removed: { #category : #initialization }
251 Removed: MFTExercise >> initialize [
248 Added: { #category : #accessing }
249 Added: MFTExercise >> frenchName [
252 250
253 Removed: super initialize.
254 Removed: canonicalName := String new.
255 Removed: englishName := String new.
256 Removed: sets := OrderedCollection new.
257 Removed: primaryMuscle := MFTMuscleGroup new.
258 Removed: secondaryMuscles := OrderedCollection new
251 Added: ^ frenchName
259 252 ]
260 253
261 254 { #category : #accessing }
262 Removed: MFTExercise >> primaryMuscle [
255 Added: MFTExercise >> frenchName: anObject [
263 256
264 Removed: ^ primaryMuscle
257 Added: frenchName := anObject
265 258 ]
266 259
267 260 { #category : #accessing }
268 Removed: MFTExercise >> primaryMuscle: anObject [
261 Added: MFTExercise >> id [
269 262
270 Removed: primaryMuscle := anObject
263 Added: ^ id
271 264 ]
272 265
273 Removed: { #category : #'as yet unclassified' }
274 Removed: MFTExercise >> repsBySet [
266 Added: { #category : #accessing }
267 Added: MFTExercise >> id: anObject [
275 268
276 Removed: ^ (sets collect: #reps) ifEmpty: [ Array with: 0 ]
269 Added: id := anObject
277 270 ]
278 271
279 Removed: { #category : #accessing }
280 Removed: MFTExercise >> secondaryMuscles [
272 Added: { #category : #initialization }
273 Added: MFTExercise >> initialize [
281 274
282 Removed: ^ secondaryMuscles
275 Added: super initialize.
276 Added: englishName := String new
283 277 ]
284 278
285 279 { #category : #accessing }
286 Removed: MFTExercise >> secondaryMuscles: anObject [
280 Added: MFTExercise >> primaryMuscleGroupId [
287 281
288 Removed: secondaryMuscles := anObject
282 Added: ^ primaryMuscleGroupId
289 283 ]
290 284
291 285 { #category : #accessing }
292 Removed: MFTExercise >> sets [
286 Added: MFTExercise >> primaryMuscleGroupId: anObject [
293 287
294 Removed: ^ sets
288 Added: primaryMuscleGroupId := anObject
295 289 ]
296 290
297 Removed: { #category : #accessing }
298 Removed: MFTExercise >> sets: aMFTSetCollection [
291 Added: { #category : #'as yet unclassified' }
292 Added: MFTExercise >> repsBySet [
299 293
300 Removed: sets := aMFTSetCollection asOrderedCollection
294 Added: ^ (sets collect: #reps) ifEmpty: [ Array with: 0 ]
301 295 ]
302 296
303 297 { #category : #'as yet unclassified' }
src/MyFitnessTracker/MFTExerciseDialog.class.st
index 9e848ff9..12afc262 100644..100644
@@ -8,7 +8,7 @@
8 8 'exercise',
9 9 'sets'
10 10 ],
11 Removed: #category : #'MyFitnessTracker-Dialogs'
11 Added: #category : #'MyFitnessTracker-Dialog'
12 12 }
13 13
14 14 { #category : #actions }
@@ -39,6 +39,13 @@
39 39 super initialize.
40 40 exercise := MFTExercise new.
41 41 sets := OrderedCollection new
42 Added: ]
43 Added:
44 Added: { #category : #testing }
45 Added: MFTExerciseDialog >> isDeprecated [
46 Added: "Use MFTSetDialog instead."
47 Added:
48 Added: ^ true
42 49 ]
43 50
44 51 { #category : #rendering }
src/MyFitnessTracker/MFTExercisesDialog.class.st
index a38c2d05..0f88dac0 100644..100644
@@ -4,7 +4,7 @@
4 4 #instVars : [
5 5 'exercises'
6 6 ],
7 Removed: #category : #'MyFitnessTracker-Dialogs'
7 Added: #category : #'MyFitnessTracker-Dialog'
8 8 }
9 9
10 10 { #category : #actions }
@@ -51,6 +51,13 @@
51 51
52 52 super initialize.
53 53 exercises := OrderedCollection new
54 Added: ]
55 Added:
56 Added: { #category : #testing }
57 Added: MFTExercisesDialog >> isDeprecated [
58 Added: "Use MFTSetsDialog instead."
59 Added:
60 Added: ^ true
54 61 ]
55 62
56 63 { #category : #rendering }
src/MyFitnessTracker/MFTLoginRegisterLogoutComponent.class.st
index 0dcbcc83..db1318aa 100644..100644
@@ -12,6 +12,8 @@
12 12 "Attempt to match aMFTUser passwordHash against an existing user password_hash in the database."
13 13
14 14 | existingUser |
15 Added: aMFTUser username = 'testMFT' ifTrue: [
16 Added: ^ self session login: MFTSQLite3Database getTestUser ].
15 17 existingUser := MFTSQLite3Database getExistingUser: aMFTUser.
16 18 aMFTUser passwordHash = existingUser passwordHash
17 19 ifTrue: [ self session login: existingUser ]
src/MyFitnessTracker/MFTMuscle.class.st
index 39945f6c..f7721b4c 100644..100644
@@ -2,364 +2,92 @@
2 2 #name : #MFTMuscle,
3 3 #superclass : #Object,
4 4 #instVars : [
5 Removed: 'canonicalName',
5 Added: 'id',
6 Added: 'primaryMuscleGroupId',
6 7 'latinName',
7 8 'englishName',
8 Removed: 'description'
9 Added: 'function'
9 10 ],
10 11 #category : #'MyFitnessTracker-Model'
11 12 }
12 13
13 Removed: { #category : #muscle }
14 Removed: MFTMuscle class >> bicepsBrachii [
15 Removed:
16 Removed: ^ self newWithLatinName
17 Removed: englishName: 'biceps';
18 Removed: yourself
19 Removed: ]
20 Removed:
21 Removed: { #category : #muscle }
22 Removed: MFTMuscle class >> brachioRadialis [
23 Removed:
24 Removed: ^ self newWithLatinName
25 Removed: englishName: 'Brachioradialis';
26 Removed: description:
27 Removed: 'Located in the forearm, it is responsible for flexing the forearm at the elbow joint.';
28 Removed: yourself
29 Removed: ]
30 Removed:
31 Removed: { #category : #muscle }
32 Removed: MFTMuscle class >> deltoidAnterior [
33 Removed:
34 Removed: ^ self new
35 Removed: englishName: 'deltoid (anterior)';
36 Removed: latinName: 'deltoideus';
37 Removed: yourself
38 Removed: ]
39 Removed:
40 Removed: { #category : #muscle }
41 Removed: MFTMuscle class >> deltoidMedial [
42 Removed:
43 Removed: ^ self new
44 Removed: englishName: 'deltoid (medial)';
45 Removed: latinName: 'deltoideus';
46 Removed: yourself
47 Removed: ]
48 Removed:
49 Removed: { #category : #muscle }
50 Removed: MFTMuscle class >> deltoidPosterior [
51 Removed:
52 Removed: ^ self new
53 Removed: englishName: 'deltoid (posterior)';
54 Removed: latinName: 'deltoideus';
55 Removed: yourself
56 Removed: ]
57 Removed:
58 Removed: { #category : #muscle }
59 Removed: MFTMuscle class >> erectorSpinae [
60 Removed:
61 Removed: ^ self newWithLatinName
62 Removed: englishName: 'erectors';
63 Removed: description:
64 Removed: 'This group of muscles runs along the spine and is responsible for extending the vertebral column and maintaining an upright posture.';
65 Removed: yourself
66 Removed: ]
67 Removed:
68 Removed: { #category : #muscle }
69 Removed: MFTMuscle class >> extensorCarpiRadialiasBrevis [
70 Removed:
71 Removed: ^ self newWithLatinName
72 Removed: englishName: 'Extensor Carpi Radialis Brevis';
73 Removed: description:
74 Removed: 'Located in the forearm, it is responsible for extending the wrist and abducting the hand.';
75 Removed: yourself
76 Removed: ]
77 Removed:
78 Removed: { #category : #muscle }
79 Removed: MFTMuscle class >> extensorCarpiRadialisLongus [
80 Removed:
81 Removed: ^ self newWithLatinName
82 Removed: englishName: 'Extensor Carpi Radialis Longus';
83 Removed: description:
84 Removed: 'Located in the forearm, it is responsible for extending the wrist and abducting the hand.';
85 Removed: yourself
86 Removed: ]
87 Removed:
88 Removed: { #category : #muscle }
89 Removed: MFTMuscle class >> extensorCarpiUlnaris [
90 Removed:
91 Removed: ^ self newWithLatinName
92 Removed: englishName: 'Extensor Carpi Ulnaris';
93 Removed: description:
94 Removed: 'Located in the forearm, it is responsible for extending the wrist and adducting the hand.';
95 Removed: yourself
96 Removed: ]
97 Removed:
98 Removed: { #category : #muscle }
99 Removed: MFTMuscle class >> flexorCarpiRadialis [
100 Removed:
101 Removed: ^ self newWithLatinName
102 Removed: englishName: 'Flexor Carpi Radialis';
103 Removed: description:
104 Removed: 'Located in the forearm, it is responsible for flexing the wrist and abducting the hand.';
105 Removed: yourself
106 Removed: ]
107 Removed:
108 Removed: { #category : #muscle }
109 Removed: MFTMuscle class >> flexorCarpiUlnaris [
110 Removed:
111 Removed: ^ self newWithLatinName
112 Removed: englishName: 'Flexor Carpi Ulnaris';
113 Removed: description:
114 Removed: 'Located in the forearm, it is responsible for flexing the wrist and adducting the hand.';
115 Removed: yourself
116 Removed: ]
117 Removed:
118 Removed: { #category : #muscle }
119 Removed: MFTMuscle class >> gastrocnemius [
120 Removed:
121 Removed: ^ self newWithLatinName
122 Removed: englishName: 'calf';
123 Removed: description:
124 Removed: 'Located in the calf, it is responsible for plantar flexion of the foot (pointing toes).';
125 Removed: yourself
126 Removed: ]
127 Removed:
128 Removed: { #category : #muscle }
129 Removed: MFTMuscle class >> gluteusMaximus [
130 Removed:
131 Removed: ^ self newWithLatinName
132 Removed: englishName: 'glutes';
133 Removed: description:
134 Removed: 'The largest muscle in the buttocks, responsible for hip extension, outward rotation, and abduction.';
135 Removed: yourself
136 Removed: ]
137 Removed:
138 Removed: { #category : #muscle }
139 Removed: MFTMuscle class >> gluteusMedius [
140 Removed:
141 Removed: ^ self newWithLatinName
142 Removed: englishName: 'glutes';
143 Removed: description:
144 Removed: 'Located in the buttocks, it plays a role in hip abduction and medial rotation.';
145 Removed: yourself
146 Removed: ]
147 Removed:
148 Removed: { #category : #muscle }
149 Removed: MFTMuscle class >> gluteusMinimus [
150 Removed:
151 Removed: ^ self newWithLatinName
152 Removed: englishName: 'glutes';
153 Removed: description:
154 Removed: 'Also located in the buttocks, it assists in hip abduction and medial rotation, working with the gluteus medius.';
155 Removed: yourself
156 Removed: ]
157 Removed:
158 Removed: { #category : #muscle }
159 Removed: MFTMuscle class >> hamstring [
160 Removed:
161 Removed: ^ self newWithLatinName
162 Removed: englishName: 'hamstrings';
163 Removed: description:
164 Removed: 'This group of muscles includes the biceps femoris, semitendinosus, and semimembranosus. They are located in the back of the thigh and are responsible for knee flexion and hip extension.';
165 Removed: yourself
166 Removed: ]
167 Removed:
168 Removed: { #category : #muscle }
169 Removed: MFTMuscle class >> latissimusDorsi [
170 Removed:
171 Removed: ^ self newWithLatinName
172 Removed: englishName: 'lats';
173 Removed: description:
174 Removed: 'Also known as the "lats," these muscles are located in the upper back and are responsible for shoulder adduction and extension.';
175 Removed: yourself
176 Removed: ]
177 Removed:
178 14 { #category : #'instance creation' }
179 Removed: MFTMuscle class >> newWithLatinName [
180 Removed: "I return a MFTMuscle instance for which latinName is set to the calling method selector, as a space-separated string.
181 Removed:
182 Removed: canonicalName stores the name of the calling method; this way, we can reliably create MFTMuscle instances."
15 Added: MFTMuscle class >> newFromDictionary: aDictionary [
183 16
184 Removed: | senderSelector muscleNameFromSender |
185 Removed: senderSelector := thisContext sender selector.
186 Removed: muscleNameFromSender := (senderSelector splitCamelCase collect:
187 Removed: #asLowercase) joinUsing: Character space.
188 17 ^ self new
189 Removed: canonicalName: senderSelector;
190 Removed: latinName: muscleNameFromSender;
18 Added: id: (aDictionary at: 'id');
19 Added: primaryMuscleGroupId: (aDictionary at: 'muscle_group_id');
20 Added: latinName: (aDictionary at: 'latin_name');
21 Added: englishName: (aDictionary at: 'english_name');
22 Added: function: (aDictionary at: 'function');
191 23 yourself
192 24 ]
193 25
194 Removed: { #category : #muscle }
195 Removed: MFTMuscle class >> obliquusExternusAbdominis [
26 Added: { #category : #accessing }
27 Added: MFTMuscle >> englishName [
196 28
197 Removed: ^ self newWithLatinName
198 Removed: englishName: 'external oblique';
199 Removed: description: 'Located on the front and side of the abdomen.';
200 Removed: yourself
29 Added: ^ englishName
201 30 ]
202 31
203 Removed: { #category : #muscle }
204 Removed: MFTMuscle class >> obliquusInternusAbdominis [
32 Added: { #category : #accessing }
33 Added: MFTMuscle >> englishName: aString [
205 34
206 Removed: ^ self newWithLatinName
207 Removed: englishName: 'internal oblique';
208 Removed: description: 'Located on the front and side of the abdomen.';
209 Removed: yourself
35 Added: englishName := aString
210 36 ]
211 37
212 Removed: { #category : #muscle }
213 Removed: MFTMuscle class >> pectoralisMajor [
214 Removed:
215 Removed: ^ self newWithLatinName
216 Removed: englishName: 'pectoral';
217 Removed: description:
218 Removed: 'Commonly known as the "pecs," these muscles are in the chest and are responsible for shoulder flexion and adduction.';
219 Removed: yourself
220 Removed: ]
221 Removed:
222 Removed: { #category : #muscle }
223 Removed: MFTMuscle class >> pectoralisMinor [
224 Removed:
225 Removed: ^ self newWithLatinName
226 Removed: englishName: 'pectoral';
227 Removed: description:
228 Removed: 'Located beneath the pectoralis major, it plays a role in stabilizing the shoulder blade and assisting in deep breathing.';
229 Removed: yourself
230 Removed: ]
231 Removed:
232 Removed: { #category : #muscle }
233 Removed: MFTMuscle class >> quadricepsFemoris [
234 Removed:
235 Removed: ^ self newWithLatinName
236 Removed: englishName: 'quad';
237 Removed: description:
238 Removed: 'A group of four muscles on the front of the thigh, responsible for knee extension.';
239 Removed: yourself
240 Removed: ]
241 Removed:
242 Removed: { #category : #muscle }
243 Removed: MFTMuscle class >> rectusAbdominis [
244 Removed:
245 Removed: ^ self newWithLatinName
246 Removed: englishName: 'six-pack';
247 Removed: description:
248 Removed: 'Located along the front of the abdomen. Colloquially known as the "six-pack"';
249 Removed: yourself
250 Removed: ]
251 Removed:
252 Removed: { #category : #muscle }
253 Removed: MFTMuscle class >> rotatorCuff [
254 Removed:
255 Removed: ^ self newWithLatinName
256 Removed: englishName: 'rotator cuff';
257 Removed: description:
258 Removed: 'This group includes four muscles: supraspinatus, infraspinatus, teres minor, and subscapularis. They play a crucial role in stabilizing the shoulder joint and facilitating arm rotation.';
259 Removed: yourself
260 Removed: ]
261 Removed:
262 Removed: { #category : #muscle }
263 Removed: MFTMuscle class >> soleus [
264 Removed:
265 Removed: ^ self newWithLatinName
266 Removed: englishName: 'calf';
267 Removed: description:
268 Removed: 'Also located in the calf, it assists the gastrocnemius in plantar flexion and helps maintain posture.';
269 Removed: yourself
270 Removed: ]
271 Removed:
272 Removed: { #category : #muscle }
273 Removed: MFTMuscle class >> transversusAbdominis [
274 Removed:
275 Removed: ^ self new
276 Removed: englishName: 'abs';
277 Removed: description:
278 Removed: 'Deepest abdominal muscle. Wraps around the spine for protection and stability.';
279 Removed: yourself
280 Removed: ]
281 Removed:
282 Removed: { #category : #muscle }
283 Removed: MFTMuscle class >> trapezius [
284 Removed:
285 Removed: ^ self newWithLatinName
286 Removed: englishName: 'trap';
287 Removed: description:
288 Removed: 'The trapezius muscles are located in the upper back and neck. They play a role in moving and stabilizing the shoulder blades.';
289 Removed: yourself
290 Removed: ]
291 Removed:
292 Removed: { #category : #muscle }
293 Removed: MFTMuscle class >> tricepsBrachii [
294 Removed:
295 Removed: ^ self newWithLatinName
296 Removed: englishName: 'triceps';
297 Removed: yourself
298 Removed: ]
299 Removed:
300 38 { #category : #accessing }
301 Removed: MFTMuscle >> canonicalName [
39 Added: MFTMuscle >> function [
302 40
303 Removed: ^ canonicalName
41 Added: ^ function
304 42 ]
305 43
306 44 { #category : #accessing }
307 Removed: MFTMuscle >> canonicalName: anObject [
45 Added: MFTMuscle >> function: anObject [
308 46
309 Removed: canonicalName := anObject
47 Added: function := anObject
310 48 ]
311 49
312 50 { #category : #accessing }
313 Removed: MFTMuscle >> description [
51 Added: MFTMuscle >> id [
314 52
315 Removed: ^ description
53 Added: ^ id
316 54 ]
317 55
318 56 { #category : #accessing }
319 Removed: MFTMuscle >> description: aString [
57 Added: MFTMuscle >> id: anObject [
320 58
321 Removed: description := aString
59 Added: id := anObject
322 60 ]
323 61
324 62 { #category : #accessing }
325 Removed: MFTMuscle >> englishName [
63 Added: MFTMuscle >> latinName [
326 64
327 Removed: ^ englishName
65 Added: ^ latinName
328 66 ]
329 67
330 68 { #category : #accessing }
331 Removed: MFTMuscle >> englishName: aString [
69 Added: MFTMuscle >> latinName: aString [
332 70
333 Removed: englishName := aString
71 Added: latinName := aString
334 72 ]
335 73
336 Removed: { #category : #initialization }
337 Removed: MFTMuscle >> initialize [
338 Removed:
339 Removed: super initialize.
340 Removed: canonicalName := String new.
341 Removed: latinName := String new.
342 Removed: englishName := String new.
343 Removed: description := String new
344 Removed: ]
345 Removed:
346 74 { #category : #accessing }
347 Removed: MFTMuscle >> latinName [
75 Added: MFTMuscle >> linkTerminologiaAnatomica [
348 76
349 Removed: ^ latinName
77 Added: ^ 'http://terminologia-anatomica.org/en/Search?query=musculus {1}'
78 Added: format: { latinName }
350 79 ]
351 80
352 81 { #category : #accessing }
353 Removed: MFTMuscle >> latinName: aString [
82 Added: MFTMuscle >> primaryMuscleGroupId [
354 83
355 Removed: latinName := aString
84 Added: ^ primaryMuscleGroupId
356 85 ]
357 86
358 87 { #category : #accessing }
359 Removed: MFTMuscle >> linkTerminologiaAnatomica [
88 Added: MFTMuscle >> primaryMuscleGroupId: anObject [
360 89
361 Removed: ^ 'http://terminologia-anatomica.org/en/Search?query=' , 'musculus '
362 Removed: , latinName
90 Added: primaryMuscleGroupId := anObject
363 91 ]
364 92
365 93 { #category : #rendering }
@@ -372,8 +100,11 @@
372 100 html
373 101 tableHeading: 'Property';
374 102 tableHeading: 'Value' ].
375 Removed: self class allInstVarNames do: [ :var |
103 Added: html tableRow: [
104 Added: html tableData: 'Latin name'.
105 Added: html tableData: latinName ].
106 Added: function ifNotNil: [
376 107 html tableRow: [
377 Removed: html tableData: var.
378 Removed: html tableData: (self perform: var) ] ] ]
108 Added: html tableData: 'Function'.
109 Added: html tableData: ('This muscle {1}.' format: { function }) ] ] ]
379 110 ]
src/MyFitnessTracker/MFTMuscleGroup.class.st
index 818e7abe..3ba18e92 100644..100644
@@ -2,189 +2,90 @@
2 2 #name : #MFTMuscleGroup,
3 3 #superclass : #SBSComponent,
4 4 #instVars : [
5 Removed: 'canonicalName',
5 Added: 'id',
6 6 'englishName',
7 Removed: 'latinName',
8 Removed: 'description',
7 Added: 'frenchName',
8 Added: 'function',
9 9 'muscles'
10 10 ],
11 11 #category : #'MyFitnessTracker-Model'
12 12 }
13 13
14 Removed: { #category : #'muscle group' }
15 Removed: MFTMuscleGroup class >> abdominal [
14 Added: { #category : #'instance creation' }
15 Added: MFTMuscleGroup class >> newFromDictionary: aDictionary [
16 16
17 Removed: ^ self newWithEnglishName
18 Removed: description: 'The abdominal muscles constitute the core.';
19 Removed: muscles: {
20 Removed: MFTMuscle rectusAbdominis.
21 Removed: MFTMuscle obliquusExternusAbdominis.
22 Removed: MFTMuscle obliquusInternusAbdominis.
23 Removed: MFTMuscle transversusAbdominis };
24 Removed: yourself
25 Removed: ]
17 Added: | id |
18 Added: id := aDictionary at: 'id'.
26 19
27 Removed: { #category : #'muscle group' }
28 Removed: MFTMuscleGroup class >> arm [
29 Removed:
30 Removed: ^ self newWithEnglishName
31 Removed: latinName: 'brachium';
32 Removed: muscles: {
33 Removed: MFTMuscle bicepsBrachii.
34 Removed: MFTMuscle tricepsBrachii };
20 Added: ^ self new
21 Added: id: id;
22 Added: englishName: (aDictionary at: 'english_name');
23 Added: frenchName: (aDictionary at: 'french_name');
24 Added: function: (aDictionary at: 'function');
25 Added: muscles: (MFTSQLite3Database allMuscles select: [ :each |
26 Added: each primaryMuscleGroupId = id ]);
35 27 yourself
36 28 ]
37 29
38 Removed: { #category : #'muscle group' }
39 Removed: MFTMuscleGroup class >> back [
30 Added: { #category : #accessing }
31 Added: MFTMuscleGroup >> englishName [
40 32
41 Removed: ^ self newWithEnglishName
42 Removed: muscles: {
43 Removed: MFTMuscle latissimusDorsi.
44 Removed: MFTMuscle trapezius.
45 Removed: MFTMuscle erectorSpinae };
46 Removed: yourself
33 Added: ^ englishName
47 34 ]
48 35
49 Removed: { #category : #'muscle group' }
50 Removed: MFTMuscleGroup class >> calf [
36 Added: { #category : #accessing }
37 Added: MFTMuscleGroup >> englishName: aString [
51 38
52 Removed: ^ self newWithEnglishName
53 Removed: muscles: {
54 Removed: MFTMuscle gastrocnemius.
55 Removed: MFTMuscle soleus };
56 Removed: yourself
39 Added: englishName := aString
57 40 ]
58 41
59 Removed: { #category : #'muscle group' }
60 Removed: MFTMuscleGroup class >> chest [
61 Removed:
62 Removed: ^ self newWithEnglishName
63 Removed: muscles: {
64 Removed: MFTMuscle pectoralisMajor.
65 Removed: MFTMuscle pectoralisMinor };
66 Removed: yourself
67 Removed: ]
68 Removed:
69 Removed: { #category : #'muscle group' }
70 Removed: MFTMuscleGroup class >> forearm [
71 Removed:
72 Removed: ^ self newWithEnglishName
73 Removed: latinName: 'antebrachium';
74 Removed: muscles: {
75 Removed: MFTMuscle brachioRadialis.
76 Removed: MFTMuscle flexorCarpiRadialis.
77 Removed: MFTMuscle flexorCarpiUlnaris.
78 Removed: MFTMuscle extensorCarpiRadialisLongus.
79 Removed: MFTMuscle extensorCarpiRadialiasBrevis.
80 Removed: MFTMuscle extensorCarpiUlnaris };
81 Removed: yourself
82 Removed: ]
83 Removed:
84 Removed: { #category : #'muscle group' }
85 Removed: MFTMuscleGroup class >> hip [
86 Removed:
87 Removed: ^ self newWithEnglishName
88 Removed: muscles: {
89 Removed: MFTMuscle gluteusMaximus.
90 Removed: MFTMuscle gluteusMedius.
91 Removed: MFTMuscle gluteusMinimus };
92 Removed: yourself
93 Removed: ]
94 Removed:
95 Removed: { #category : #'muscle group' }
96 Removed: MFTMuscleGroup class >> leg [
97 Removed:
98 Removed: ^ self newWithEnglishName
99 Removed: muscles: {
100 Removed: MFTMuscle quadricepsFemoris.
101 Removed: MFTMuscle hamstring };
102 Removed: yourself
103 Removed: ]
104 Removed:
105 Removed: { #category : #'instance creation' }
106 Removed: MFTMuscleGroup class >> newWithEnglishName [
107 Removed: "I return a MFTMuscle Group instance for which englishName is set to the calling method selector."
108 Removed:
109 Removed: | senderSelector |
110 Removed: senderSelector := thisContext sender selector.
111 Removed: ^ self new
112 Removed: canonicalName: senderSelector;
113 Removed: englishName: senderSelector;
114 Removed: yourself
115 Removed: ]
116 Removed:
117 Removed: { #category : #'muscle group' }
118 Removed: MFTMuscleGroup class >> shoulder [
119 Removed:
120 Removed: ^ self newWithEnglishName
121 Removed: muscles: {
122 Removed: MFTMuscle deltoidAnterior.
123 Removed: MFTMuscle deltoidMedial.
124 Removed: MFTMuscle deltoidPosterior.
125 Removed: MFTMuscle rotatorCuff };
126 Removed: yourself
127 Removed: ]
128 Removed:
129 42 { #category : #accessing }
130 Removed: MFTMuscleGroup >> canonicalName [
43 Added: MFTMuscleGroup >> frenchName [
131 44
132 Removed: ^ canonicalName
45 Added: ^ frenchName
133 46 ]
134 47
135 48 { #category : #accessing }
136 Removed: MFTMuscleGroup >> canonicalName: anObject [
49 Added: MFTMuscleGroup >> frenchName: anObject [
137 50
138 Removed: canonicalName := anObject
51 Added: frenchName := anObject
139 52 ]
140 53
141 54 { #category : #accessing }
142 Removed: MFTMuscleGroup >> description [
55 Added: MFTMuscleGroup >> function [
143 56
144 Removed: ^ description
57 Added: ^ function
145 58 ]
146 59
147 60 { #category : #accessing }
148 Removed: MFTMuscleGroup >> description: aString [
61 Added: MFTMuscleGroup >> function: anObject [
149 62
150 Removed: description := aString
63 Added: function := anObject
151 64 ]
152 65
153 66 { #category : #accessing }
154 Removed: MFTMuscleGroup >> englishName [
67 Added: MFTMuscleGroup >> id [
155 68
156 Removed: ^ englishName
69 Added: ^ id
157 70 ]
158 71
159 72 { #category : #accessing }
160 Removed: MFTMuscleGroup >> englishName: aString [
73 Added: MFTMuscleGroup >> id: anObject [
161 74
162 Removed: englishName := aString
75 Added: id := anObject
163 76 ]
164 77
165 78 { #category : #initialization }
166 79 MFTMuscleGroup >> initialize [
167 80
168 81 super initialize.
169 Removed: canonicalName := String new.
170 82 englishName := String new.
171 Removed: description := String new.
83 Added: frenchName := String new.
84 Added: function := String new.
172 85 muscles := OrderedCollection new
173 86 ]
174 87
175 88 { #category : #accessing }
176 Removed: MFTMuscleGroup >> latinName [
177 Removed:
178 Removed: ^ latinName
179 Removed: ]
180 Removed:
181 Removed: { #category : #accessing }
182 Removed: MFTMuscleGroup >> latinName: anObject [
183 Removed:
184 Removed: latinName := anObject
185 Removed: ]
186 Removed:
187 Removed: { #category : #accessing }
188 89 MFTMuscleGroup >> muscles [
189 90
190 91 ^ muscles
@@ -200,24 +101,17 @@
200 101 MFTMuscleGroup >> renderContentOn: html [
201 102
202 103 html heading level2 with: englishName capitalized.
203 Removed: description ifNotEmpty: [
104 Added: function ifNotNil: [
204 105 html
205 Removed: text: description;
106 Added: text: ('This muscle group {1}.' format: { function });
206 107 break ].
207 108 muscles ifNotEmpty: [
208 Removed: html
209 Removed: text: 'This group is comprised of:';
210 Removed: definitionList: [
211 Removed: muscles do: [ :muscle |
212 Removed: html definitionTerm: [
213 Removed: html text: muscle englishName capitalized.
214 Removed: muscle latinName ifNotEmpty: [
215 Removed: html space.
216 Removed: html text: '('.
217 Removed: html anchor
218 Removed: url: muscle linkTerminologiaAnatomica;
219 Removed: with: muscle latinName.
220 Removed: html text: ')' ] ].
221 Removed: html definitionData: muscle description.
222 Removed: muscle renderContentOn: html ] ] ]
109 Added: html listGroup: [
110 Added: muscles do: [ :muscle |
111 Added: html listGroupItem: [
112 Added: html anchor
113 Added: url: muscle linkTerminologiaAnatomica;
114 Added: with: muscle latinName.
115 Added: muscle function ifNotNil: [
116 Added: html text: (': {1}.' format: { muscle function }) ] ] ] ] ]
223 117 ]
src/MyFitnessTracker/MFTPageManager.class.st
index 466ffb63..ea23e98e 100644..100644
@@ -64,7 +64,6 @@
64 64
65 65 mainPages := self defaultPublicPages.
66 66 activePage := mainPages at: #landing.
67 Removed:
68 67 mainNavigationBar := MFTMainNavigationBarComponent new.
69 68 header := MFTRegisterProspectEmailHeader new.
70 69 footer := MFTDefaultFooter new
src/MyFitnessTracker/MFTPrimaryMuscleDialog.class.st
index c4afa4f8..00000000 100644..000000
@@ -1,62 +0,0 @@
1 Removed: Class {
2 Removed: #name : #MFTPrimaryMuscleDialog,
3 Removed: #superclass : #MFTDialog,
4 Removed: #instVars : [
5 Removed: 'primaryMuscle'
6 Removed: ],
7 Removed: #category : #'MyFitnessTracker-Dialogs'
8 Removed: }
9 Removed:
10 Removed: { #category : #initialization }
11 Removed: MFTPrimaryMuscleDialog >> initialize [
12 Removed:
13 Removed: super initialize.
14 Removed: primaryMuscle := MFTMuscle new
15 Removed: ]
16 Removed:
17 Removed: { #category : #accessing }
18 Removed: MFTPrimaryMuscleDialog >> primaryMuscle [
19 Removed:
20 Removed: ^ primaryMuscle
21 Removed: ]
22 Removed:
23 Removed: { #category : #accessing }
24 Removed: MFTPrimaryMuscleDialog >> primaryMuscle: anObject [
25 Removed:
26 Removed: primaryMuscle := anObject
27 Removed: ]
28 Removed:
29 Removed: { #category : #rendering }
30 Removed: MFTPrimaryMuscleDialog >> renderContentOn: html [
31 Removed:
32 Removed: | allMuscles selectedMuscle |
33 Removed: allMuscles := MFTUtils allMuscleGroups.
34 Removed: selectedMuscle := allMuscles
35 Removed: detect: [ :each |
36 Removed: each englishName = primaryMuscle englishName ]
37 Removed: ifNone: [ MFTMuscleGroup new ].
38 Removed:
39 Removed: self renderDialogOn: html with: [
40 Removed: html formGroup: [
41 Removed: html select formControl
42 Removed: list: allMuscles;
43 Removed: selected: selectedMuscle;
44 Removed: labels: [ :muscleGroup | muscleGroup englishName ];
45 Removed: callback: [ :group | primaryMuscle := group ] ] ]
46 Removed: ]
47 Removed:
48 Removed: { #category : #rendering }
49 Removed: MFTPrimaryMuscleDialog >> renderSubmitOn: html [
50 Removed:
51 Removed: html buttonGroup: [
52 Removed: html formButton
53 Removed: beSubmit;
54 Removed: bePrimary;
55 Removed: callback: [ self answer: primaryMuscle ];
56 Removed: with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
57 Removed: html formButton
58 Removed: beSubmit;
59 Removed: beSecondary;
60 Removed: callback: [ self answer: MFTMuscle new ];
61 Removed: with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
62 Removed: ]
src/MyFitnessTracker/MFTPrimaryMuscleGroupDialog.class.st
index 00000000..8828e1c2 000000..100644
@@ -0,0 +1,62 @@
1 Added: Class {
2 Added: #name : #MFTPrimaryMuscleGroupDialog,
3 Added: #superclass : #MFTDialog,
4 Added: #instVars : [
5 Added: 'primaryMuscleGroup'
6 Added: ],
7 Added: #category : #'MyFitnessTracker-Dialog'
8 Added: }
9 Added:
10 Added: { #category : #initialization }
11 Added: MFTPrimaryMuscleGroupDialog >> initialize [
12 Added:
13 Added: super initialize.
14 Added: primaryMuscleGroup := MFTMuscleGroup new.
15 Added:
16 Added: ]
17 Added:
18 Added: { #category : #accessing }
19 Added: MFTPrimaryMuscleGroupDialog >> primaryMuscleGroup [
20 Added:
21 Added: ^ primaryMuscleGroup
22 Added: ]
23 Added:
24 Added: { #category : #accessing }
25 Added: MFTPrimaryMuscleGroupDialog >> primaryMuscleGroup: anObject [
26 Added:
27 Added: primaryMuscleGroup := anObject
28 Added: ]
29 Added:
30 Added: { #category : #rendering }
31 Added: MFTPrimaryMuscleGroupDialog >> renderContentOn: html [
32 Added:
33 Added: | allMuscleGroups selectedMuscleGroup |
34 Added: allMuscleGroups := MFTSQLite3Database allMuscleGroups.
35 Added: selectedMuscleGroup := primaryMuscleGroup ifNotNil: [
36 Added: allMuscleGroups detect: [ :muscleGroup |
37 Added: muscleGroup englishName
38 Added: = primaryMuscleGroup englishName ] ].
39 Added: self renderDialogOn: html with: [
40 Added: html formGroup: [
41 Added: html select formControl
42 Added: list: allMuscleGroups;
43 Added: selected: selectedMuscleGroup;
44 Added: labels: [ :muscleGroup | muscleGroup englishName ];
45 Added: callback: [ :group | primaryMuscleGroup := group ] ] ]
46 Added: ]
47 Added:
48 Added: { #category : #rendering }
49 Added: MFTPrimaryMuscleGroupDialog >> renderSubmitOn: html [
50 Added:
51 Added: html buttonGroup: [
52 Added: html formButton
53 Added: beSubmit;
54 Added: bePrimary;
55 Added: callback: [ self answer: primaryMuscleGroup ];
56 Added: with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
57 Added: html formButton
58 Added: beSubmit;
59 Added: beSecondary;
60 Added: callback: [ self answer: nil ];
61 Added: with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
62 Added: ]
src/MyFitnessTracker/MFTRepsDistributionGraph.class.st
index a6f0bff1..e464de73 100644..100644
@@ -6,15 +6,16 @@
6 6
7 7 { #category : #'as yet unclassified' }
8 8 MFTRepsDistributionGraph >> crunchTheNumbers [
9 Added: "TODO: This looks ugly."
9 10
10 Removed: | exercises repTally |
11 Removed: exercises := self session user workouts flatCollect: #exercises.
11 Added: | sets repTally |
12 Added: sets := self session user workouts flatCollect: #sets.
12 13 repTally := Dictionary new.
13 Removed: exercises do: [ :exercise |
14 Removed: repTally at: exercise englishName ifAbsentPut: [ 0 ].
14 Added: sets do: [ :set |
15 Added: repTally at: set exercise englishName ifAbsentPut: [ 0 ].
15 16 repTally
16 Removed: at: exercise englishName
17 Removed: put: (repTally at: exercise englishName) + exercise repsBySet sum ].
17 Added: at: set exercise englishName
18 Added: put: (repTally at: set exercise englishName) + set reps ].
18 19
19 20 labels := repTally keys.
20 21 data := repTally values
src/MyFitnessTracker/MFTSQLite3Database.class.st
index 7758c1d1..eeda2c40 100644..100644
@@ -6,106 +6,149 @@
6 6 'userId'
7 7 ],
8 8 #classInstVars : [
9 Removed: 'defaultLocation'
9 Added: 'defaultLocation',
10 Added: 'allMuscleGroups',
11 Added: 'allMuscles',
12 Added: 'allExercises'
10 13 ],
11 14 #category : #'MyFitnessTracker-Model'
12 15 }
13 16
17 Added: { #category : #accessing }
18 Added: MFTSQLite3Database class >> allExercises [
19 Added:
20 Added: ^ allExercises
21 Added: ]
22 Added:
23 Added: { #category : #accessing }
24 Added: MFTSQLite3Database class >> allExercises: anObject [
25 Added:
26 Added: allExercises := anObject
27 Added: ]
28 Added:
14 29 { #category : #'as yet unclassified' }
15 Removed: MFTSQLite3Database class >> defaultConnection [
30 Added: MFTSQLite3Database class >> allExercisesAsDict [
16 31
17 Removed: ^ self newOnDefaultLocation connection
32 Added: | dict |
33 Added: dict := Dictionary new.
34 Added: allExercises do: [ :exercise |
35 Added: dict at: exercise englishName put: exercise ].
36 Added: ^ dict
18 37 ]
19 38
20 39 { #category : #accessing }
21 Removed: MFTSQLite3Database class >> defaultLocation [
40 Added: MFTSQLite3Database class >> allMuscleGroups [
22 41
23 Removed: ^ defaultLocation
42 Added: ^ allMuscleGroups
24 43 ]
25 44
26 45 { #category : #accessing }
27 Removed: MFTSQLite3Database class >> defaultLocation: aFilePath [
46 Added: MFTSQLite3Database class >> allMuscleGroups: anObject [
28 47
29 Removed: defaultLocation := aFilePath
48 Added: allMuscleGroups := anObject
30 49 ]
31 50
32 51 { #category : #'as yet unclassified' }
33 Removed: MFTSQLite3Database class >> ensureAllTablesExist [
52 Added: MFTSQLite3Database class >> allMuscleGroupsAsDict [
34 53
35 Removed: self defaultConnection
36 Removed: open;
37 Removed: execute: self ensureUsersTableExists;
38 Removed: execute: self ensureProspectsTableExists;
39 Removed: execute: self ensureWeightsTableExists;
40 Removed: execute: self ensureWorkoutsTableExists;
41 Removed: execute: self ensureExercisesTableExists;
42 Removed: execute: self ensureSetsTableExists;
43 Removed: close
54 Added: | dict |
55 Added: dict := Dictionary new.
56 Added: allMuscleGroups do: [ :muscleGroup |
57 Added: dict at: muscleGroup englishName put: muscleGroup ].
58 Added: ^ dict
44 59 ]
45 60
46 Removed: { #category : #'as yet unclassified' }
47 Removed: MFTSQLite3Database class >> ensureExercisesTableExists [
61 Added: { #category : #accessing }
62 Added: MFTSQLite3Database class >> allMuscles [
48 63
49 Removed: ^ 'CREATE TABLE IF NOT EXISTS "exercises" (
50 Removed: "id" INTEGER NOT NULL UNIQUE,
51 Removed: "name" TEXT NOT NULL,
52 Removed: PRIMARY KEY("id" AUTOINCREMENT))'
64 Added: ^ allMuscles
53 65 ]
54 66
55 Removed: { #category : #'as yet unclassified' }
56 Removed: MFTSQLite3Database class >> ensureProspectsTableExists [
67 Added: { #category : #accessing }
68 Added: MFTSQLite3Database class >> allMuscles: anObject [
57 69
58 Removed: ^ 'CREATE TABLE IF NOT EXISTS "prospects" (
59 Removed: "id" INTEGER,
60 Removed: "email" TEXT NOT NULL,
61 Removed: "date_time" TEXT NOT NULL,
62 Removed: PRIMARY KEY("id" AUTOINCREMENT))'
70 Added: allMuscles := anObject
63 71 ]
64 72
65 73 { #category : #'as yet unclassified' }
66 Removed: MFTSQLite3Database class >> ensureSetsTableExists [
74 Added: MFTSQLite3Database class >> defaultConnection [
67 75
68 Removed: ^ 'CREATE TABLE IF NOT EXISTS "sets" (
69 Removed: "id" INTEGER NOT NULL UNIQUE,
70 Removed: ""
71 Removed: "name" TEXT NOT NULL,
72 Removed: PRIMARY KEY("id" AUTOINCREMENT))'
76 Added: ^ self newOnDefaultLocation connection
73 77 ]
74 78
75 Removed: { #category : #'as yet unclassified' }
76 Removed: MFTSQLite3Database class >> ensureUsersTableExists [
79 Added: { #category : #accessing }
80 Added: MFTSQLite3Database class >> defaultLocation [
77 81
78 Removed: ^ 'CREATE TABLE IF NOT EXISTS "users" (
79 Removed: "id" INTEGER UNIQUE,
80 Removed: "username" TEXT NOT NULL,
81 Removed: "password_hash" BLOB NOT NULL,
82 Removed: "real_first_name" TEXT,
83 Removed: "real_last_name" TEXT,
84 Removed: "date_of_birth" TEXT,
85 Removed: PRIMARY KEY("id" AUTOINCREMENT))'
82 Added: ^ defaultLocation
86 83 ]
87 84
88 Removed: { #category : #'as yet unclassified' }
89 Removed: MFTSQLite3Database class >> ensureWeightsTableExists [
85 Added: { #category : #accessing }
86 Added: MFTSQLite3Database class >> defaultLocation: aFilePath [
90 87
91 Removed: ^ 'CREATE TABLE IF NOT EXISTS "weights" (
92 Removed: "id" INTEGER UNIQUE,
93 Removed: "user_id" INTEGER NOT NULL,
94 Removed: "date" TEXT NOT NULL,
95 Removed: "value" NUMERIC NOT NULL,
96 Removed: PRIMARY KEY("id" AUTOINCREMENT))'
88 Added: defaultLocation := aFilePath
97 89 ]
98 90
91 Added: { #category : #accessing }
92 Added: MFTSQLite3Database class >> getAllExercises [
93 Added:
94 Added: | connection allRows |
95 Added: connection := self defaultConnection open.
96 Added: allRows := (connection execute: 'SELECT * FROM Exercises') rows.
97 Added: connection close.
98 Added: ^ Array withAll: (allRows collect: [ :row |
99 Added: MFTExercise newFromDictionary: row asDictionary ])
100 Added: ]
101 Added:
102 Added: { #category : #accessing }
103 Added: MFTSQLite3Database class >> getAllMuscleGroups [
104 Added:
105 Added: | connection allRows |
106 Added: connection := self defaultConnection open.
107 Added: allRows := (connection execute: 'SELECT * FROM Muscle_Groups') rows.
108 Added: connection close.
109 Added: ^ Array withAll: (allRows collect: [ :row |
110 Added: MFTMuscleGroup newFromDictionary: row asDictionary ])
111 Added: ]
112 Added:
113 Added: { #category : #accessing }
114 Added: MFTSQLite3Database class >> getAllMuscles [
115 Added:
116 Added: | connection allRows |
117 Added: connection := self defaultConnection open.
118 Added: allRows := (connection execute: 'SELECT * FROM Muscles') rows.
119 Added: connection close.
120 Added: ^ Array withAll: (allRows collect: [ :row |
121 Added: MFTMuscle newFromDictionary: row asDictionary ])
122 Added: ]
123 Added:
124 Added: { #category : #accessing }
125 Added: MFTSQLite3Database class >> getAllWorkoutsForUser: aMFTUser [
126 Added:
127 Added: | connection allRepsRows allWorkoutRows |
128 Added: connection := self defaultConnection open.
129 Added: connection beginTransaction.
130 Added: allRepsRows := ((connection execute:
131 Added: 'SELECT * FROM Workouts WHERE user_id = ?') with:
132 Added: { aMFTUser id }) rows.
133 Added: allWorkoutRows := ((connection execute:
134 Added: 'SELECT * FROM Workouts WHERE user_id = ?')
135 Added: with: { aMFTUser id }) rows.
136 Added: connection commitTransaction.
137 Added: connection close.
138 Added: ^ Array withAll: (allWorkoutRows collect: [ :row |
139 Added: MFTWorkout newFromDictionary: row asDictionary ])
140 Added: ]
141 Added:
99 142 { #category : #'as yet unclassified' }
100 Removed: MFTSQLite3Database class >> ensureWorkoutsTableExists [
143 Added: MFTSQLite3Database class >> getExerciseIdFromName: aString [
101 144
102 Removed: ^ 'CREATE TABLE IF NOT EXISTS "workouts" (
103 Removed: "id" INTEGER UNIQUE,
104 Removed: "user_id" INTEGER NOT NULL,
105 Removed: "date" TEXT NOT NULL,
106 Removed: "primary_muscle" TEXT,
107 Removed: "secondary_muscles" TEXT,
108 Removed: PRIMARY KEY("id" AUTOINCREMENT))'
145 Added: ^ allExercises
146 Added: detect: [ :each | each englishName = aString ]
147 Added: ifFound: [ :exercise | exercise id ]
148 Added: ifNone: [
149 Added: self error:
150 Added: ('No exercise with English name ''{1}'' found' format:
151 Added: { aString }) ]
109 152 ]
110 153
111 154 { #category : #'as yet unclassified' }
@@ -124,17 +167,32 @@
124 167 aMFTUser passwordHash }) onlyRow: [
125 168 Dictionary new ]) asDictionary.
126 169 connection close.
127 Removed: existingUserDict
128 Removed: ifNotEmpty: [ ^ MFTUser newFromDictionary: existingUserDict ]
129 Removed: ifEmpty: [ ^ MFTUser new ]
170 Added: ^ existingUserDict
171 Added: ifNotEmpty: [ MFTUser newFromDictionary: existingUserDict ]
172 Added: ifEmpty: [ nil ]
130 173 ]
131 174
175 Added: { #category : #'as yet unclassified' }
176 Added: MFTSQLite3Database class >> getTestUser [
177 Added:
178 Added: | connection testUserDict |
179 Added: connection := self defaultConnection.
180 Added: connection open.
181 Added: testUserDict := (connection execute:
182 Added: 'SELECT * FROM users WHERE id = 1') onlyRow
183 Added: asDictionary.
184 Added: connection close.
185 Added: ^ MFTUser newFromDictionary: testUserDict
186 Added: ]
187 Added:
132 188 { #category : #'class initialization' }
133 189 MFTSQLite3Database class >> initialize [
134 190
135 191 defaultLocation := (Smalltalk imageDirectory / 'mft.sqlite3')
136 192 fullName.
137 Removed: self ensureAllTablesExist
193 Added: allMuscleGroups := self getAllMuscleGroups.
194 Added: allMuscles := self getAllMuscles.
195 Added: allExercises := self getAllExercises
138 196 ]
139 197
140 198 { #category : #'instance creation' }
@@ -146,51 +204,27 @@
146 204 { #category : #'class initialization' }
147 205 MFTSQLite3Database class >> populatePlaceholderWeights [
148 206
149 Removed: | connection |
150 Removed: connection := self defaultConnection open.
207 Added: | db |
208 Added: db := self newOnDefaultLocation userId: 1.
209 Added: db connection open.
210 Added: db connection execute: 'DELETE FROM Weights'.
151 211 MFTWeight placeholderCollection do: [ :weight |
152 Removed: connection
153 Removed: execute: 'INSERT INTO weights(user_id, date, value)
154 Removed: VALUES( ?, ?, ?)'
155 Removed: with: {
156 Removed: 1.
157 Removed: weight date.
158 Removed: weight value } ].
159 Removed: connection close
212 Added: db insertWeight: weight ].
213 Added: db connection close
160 214 ]
161 215
162 216 { #category : #'class initialization' }
163 217 MFTSQLite3Database class >> populatePlaceholderWorkouts [
164 218
165 Removed: | connection |
166 Removed: connection := self defaultConnection open.
167 Removed: connection beginTransaction.
219 Added: | db |
220 Added: db := self newOnDefaultLocation userId: 1.
221 Added: db connection open.
222 Added: db connection
223 Added: execute: 'DELETE FROM Workouts';
224 Added: execute: 'DELETE FROM Sets'.
168 225 MFTWorkout placeholderCollection do: [ :workout |
169 Removed: | workoutId |
170 Removed: connection
171 Removed: execute:
172 Removed: 'INSERT INTO workouts(user_id, date, primary_muscle, secondary_muscles)
173 Removed: VALUES(?, ?, ?, ?)'
174 Removed: with: {
175 Removed: 1.
176 Removed: workout date yyyymmdd.
177 Removed: workout primaryMuscle canonicalName.
178 Removed: (workout secondaryMuscles collect: #canonicalName) asCommaString }.
179 Removed: workoutId := (connection
180 Removed: execute: 'SELECT workouts.id
181 Removed: FROM workouts
182 Removed: WHERE date = ?'
183 Removed: value: workout date yyyymmdd) onlyValue.
184 Removed: workout exercises do: [ :exercise |
185 Removed: connection
186 Removed: execute: 'INSERT INTO exercises(workout_id, name, sets)
187 Removed: VALUES(?, ?, ?)'
188 Removed: with: {
189 Removed: workoutId.
190 Removed: exercise canonicalName.
191 Removed: exercise sets asJson } ] ].
192 Removed: connection commitTransaction.
193 Removed: connection close
226 Added: db insertWorkout: workout ].
227 Added: db connection close
194 228 ]
195 229
196 230 { #category : #'as yet unclassified' }
@@ -242,8 +276,8 @@
242 276 execute: 'SELECT *
243 277 FROM weights
244 278 WHERE user_id = ?'
245 Removed: value: userId) rows collect: [ :each |
246 Removed: MFTWeight newFromDictionary: each asDictionary ]
279 Added: value: userId) rows collect: [ :row |
280 Added: MFTWeight newFromDictionary: row asDictionary ]
247 281 ]
248 282
249 283 { #category : #'as yet unclassified' }
@@ -278,7 +312,7 @@
278 312 ]
279 313
280 314 { #category : #'as yet unclassified' }
281 Removed: MFTSQLite3Database >> deleteExercisesForWorkoutId: id [
315 Added: MFTSQLite3Database >> deleteSetsForWorkoutId: id [
282 316
283 317 connection
284 318 execute: 'DELETE
@@ -317,7 +351,7 @@
317 351 connection commitTransaction
318 352 ]
319 353
320 Removed: { #category : #private }
354 Added: { #category : #accessing }
321 355 MFTSQLite3Database >> insert: anObject [
322 356 "Insert an object in the database, using the appropriate method."
323 357
@@ -336,18 +370,31 @@
336 370 ]
337 371
338 372 { #category : #'as yet unclassified' }
339 Removed: MFTSQLite3Database >> insertExercise: aMFTExercise forWorkoutId: workoutId [
373 Added: MFTSQLite3Database >> insertSet: aMFTSet forWorkoutId: workoutId [
340 374
341 375 connection
342 Removed: execute: 'INSERT INTO exercises(workout_id, name, sets)
343 Removed: VALUES( ?, ?, ?)'
376 Added: execute:
377 Added: 'INSERT INTO Sets ("workout_id","exercise_id", "set_group", "set_number", "reps", "load", "rpe", "rest_after")
378 Added: VALUES(?, ?, ?, ?, ?, ?, ?, ?)'
344 379 with: {
345 380 workoutId.
346 Removed: aMFTExercise canonicalName.
347 Removed: aMFTExercise sets asJson }
381 Added: aMFTSet exerciseId.
382 Added: aMFTSet setGroup.
383 Added: aMFTSet setNumber.
384 Added: aMFTSet reps.
385 Added: aMFTSet load.
386 Added: aMFTSet rpe.
387 Added: aMFTSet restAfter }
348 388 ]
349 389
350 390 { #category : #'as yet unclassified' }
391 Added: MFTSQLite3Database >> insertSets: aMFTSetCollection forWorkoutId: workoutId [
392 Added:
393 Added: aMFTSetCollection do: [ :set |
394 Added: self insertSet: set forWorkoutId: workoutId ]
395 Added: ]
396 Added:
397 Added: { #category : #'as yet unclassified' }
351 398 MFTSQLite3Database >> insertWeight: aMFTWeight [
352 399
353 400 connection
@@ -365,32 +412,23 @@
365 412 | workoutId |
366 413 connection beginTransaction.
367 414 connection
368 Removed: execute: 'INSERT INTO Workouts ("id" INTEGER UNIQUE,
369 Removed: "user_id",
370 Removed: "date" ,
371 Removed: "primary_muscle",
372 Removed: "secondary_muscles" )
373 Removed: VALUES(?, ?, ?, ?)'
415 Added: execute:
416 Added: 'INSERT INTO Workouts ("user_id", "date", "primary_muscle_group_id") VALUES(?, ?, ?)'
374 417 with: {
375 418 userId.
376 419 aMFTWorkout date yyyymmdd.
377 Removed: aMFTWorkout primaryMuscle englishName.
378 Removed: (aMFTWorkout secondaryMuscles collect: #englishName) asCommaString }.
420 Added: aMFTWorkout primaryMuscleGroupId }.
379 421 workoutId := (connection
380 422 execute: 'SELECT id
381 423 FROM workouts
382 424 WHERE user_id = ?
383 425 AND date = ?
384 Removed: AND primary_muscle = ?
385 Removed: AND secondary_muscles = ?'
426 Added: AND primary_muscle_group_id = ?'
386 427 with: {
387 428 userId.
388 429 aMFTWorkout date yyyymmdd.
389 Removed: aMFTWorkout primaryMuscle englishName.
390 Removed: (aMFTWorkout secondaryMuscles collect:
391 Removed: #canonicalName) asCommaString }) onlyValue.
392 Removed: aMFTWorkout exercises do: [ :exercise |
393 Removed: self insertExercise: exercise forWorkoutId: workoutId ].
430 Added: aMFTWorkout primaryMuscleGroupId }) onlyValue.
431 Added: self insertSets: aMFTWorkout sets forWorkoutId: workoutId.
394 432 connection commitTransaction
395 433 ]
396 434
@@ -493,7 +531,7 @@
493 531 (newMFTWorkout secondaryMuscles collect: #englishName)
494 532 asCommaString.
495 533 workoutId }.
496 Removed: self deleteExercisesForWorkoutId: workoutId.
534 Added: self deleteSetsForWorkoutId: workoutId.
497 535 newMFTWorkout exercises do: [ :exercise |
498 536 self insertExercise: exercise forWorkoutId: workoutId ]
499 537 ]
src/MyFitnessTracker/MFTSandboxPage.class.st
index 00000000..06c2af7a 000000..100644
@@ -0,0 +1,20 @@
1 Added: Class {
2 Added: #name : #MFTSandboxPage,
3 Added: #superclass : #MFTPage,
4 Added: #category : #'MyFitnessTracker-Components'
5 Added: }
6 Added:
7 Added: { #category : #initialization }
8 Added: MFTSandboxPage >> initialize [
9 Added:
10 Added: super initialize.
11 Added: title := 'sandbox'
12 Added: ]
13 Added:
14 Added: { #category : #rendering }
15 Added: MFTSandboxPage >> renderContentOn: html [
16 Added:
17 Added: self renderPageOn: html with: [
18 Added: html text: 'Welcome to the sandbox page.'.
19 Added: MFTSQLite3Database allMuscleGroups do: [ :each | html render: each ] ]
20 Added: ]
src/MyFitnessTracker/MFTSecondaryMuscleGroupsDialog.class.st
index 00000000..4a21300e 000000..100644
@@ -0,0 +1,63 @@
1 Added: Class {
2 Added: #name : #MFTSecondaryMuscleGroupsDialog,
3 Added: #superclass : #MFTDialog,
4 Added: #instVars : [
5 Added: 'secondaryMuscleGroups'
6 Added: ],
7 Added: #category : #'MyFitnessTracker-Dialog'
8 Added: }
9 Added:
10 Added: { #category : #initialization }
11 Added: MFTSecondaryMuscleGroupsDialog >> initialize [
12 Added:
13 Added: super initialize.
14 Added: secondaryMuscleGroups := OrderedCollection new
15 Added: ]
16 Added:
17 Added: { #category : #rendering }
18 Added: MFTSecondaryMuscleGroupsDialog >> renderContentOn: html [
19 Added:
20 Added: | allMuscleGroups selectedMuscles |
21 Added: allMuscleGroups := MFTSQLite3Database allMuscleGroups.
22 Added: "selectedMuscles := allMuscles
23 Added: detect: [ :each |
24 Added: each englishName
25 Added: = (secondaryMuscles collect: #englishName) ]
26 Added: ifNone: [ nil ]."
27 Added:
28 Added: self renderDialogOn: html with: [
29 Added: html multiSelect formControl
30 Added: list: allMuscleGroups;
31 Added: "selected: selectedMuscles;"labels: [ :muscleGroup |
32 Added: muscleGroup englishName ];
33 Added: callback: [ :groups |
34 Added: groups ifNotEmpty: [ secondaryMuscleGroups := groups ] ] ]
35 Added: ]
36 Added:
37 Added: { #category : #rendering }
38 Added: MFTSecondaryMuscleGroupsDialog >> renderSubmitOn: html [
39 Added:
40 Added: html buttonGroup: [
41 Added: html formButton
42 Added: beSubmit;
43 Added: bePrimary;
44 Added: callback: [ self answer: secondaryMuscleGroups ];
45 Added: with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
46 Added: html formButton
47 Added: beSubmit;
48 Added: beSecondary;
49 Added: callback: [ self answer: OrderedCollection new ];
50 Added: with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
51 Added: ]
52 Added:
53 Added: { #category : #accessing }
54 Added: MFTSecondaryMuscleGroupsDialog >> secondaryMuscleGroups [
55 Added:
56 Added: ^ secondaryMuscleGroups
57 Added: ]
58 Added:
59 Added: { #category : #accessing }
60 Added: MFTSecondaryMuscleGroupsDialog >> secondaryMuscleGroups: anObject [
61 Added:
62 Added: secondaryMuscleGroups := anObject
63 Added: ]
src/MyFitnessTracker/MFTSecondaryMusclesDialog.class.st
index 3acc76d6..00000000 100644..000000
@@ -1,63 +0,0 @@
1 Removed: Class {
2 Removed: #name : #MFTSecondaryMusclesDialog,
3 Removed: #superclass : #MFTDialog,
4 Removed: #instVars : [
5 Removed: 'secondaryMuscles'
6 Removed: ],
7 Removed: #category : #'MyFitnessTracker-Dialogs'
8 Removed: }
9 Removed:
10 Removed: { #category : #initialization }
11 Removed: MFTSecondaryMusclesDialog >> initialize [
12 Removed:
13 Removed: super initialize.
14 Removed: secondaryMuscles := OrderedCollection new
15 Removed: ]
16 Removed:
17 Removed: { #category : #rendering }
18 Removed: MFTSecondaryMusclesDialog >> renderContentOn: html [
19 Removed:
20 Removed: | allMuscles selectedMuscles |
21 Removed: allMuscles := MFTUtils allMuscleGroups.
22 Removed: "selectedMuscles := allMuscles
23 Removed: detect: [ :each |
24 Removed: each englishName
25 Removed: = (secondaryMuscles collect: #englishName) ]
26 Removed: ifNone: [ nil ]."
27 Removed:
28 Removed: self renderDialogOn: html with: [
29 Removed: html multiSelect formControl
30 Removed: list: allMuscles;
31 Removed: "selected: selectedMuscles;"labels: [ :muscleGroup |
32 Removed: muscleGroup englishName ];
33 Removed: callback: [ :groups |
34 Removed: groups ifNotEmpty: [ secondaryMuscles := groups ] ] ]
35 Removed: ]
36 Removed:
37 Removed: { #category : #rendering }
38 Removed: MFTSecondaryMusclesDialog >> renderSubmitOn: html [
39 Removed:
40 Removed: html buttonGroup: [
41 Removed: html formButton
42 Removed: beSubmit;
43 Removed: bePrimary;
44 Removed: callback: [ self answer: secondaryMuscles ];
45 Removed: with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
46 Removed: html formButton
47 Removed: beSubmit;
48 Removed: beSecondary;
49 Removed: callback: [ self answer: OrderedCollection new ];
50 Removed: with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
51 Removed: ]
52 Removed:
53 Removed: { #category : #accessing }
54 Removed: MFTSecondaryMusclesDialog >> secondaryMuscles [
55 Removed:
56 Removed: ^ secondaryMuscles
57 Removed: ]
58 Removed:
59 Removed: { #category : #accessing }
60 Removed: MFTSecondaryMusclesDialog >> secondaryMuscles: anObject [
61 Removed:
62 Removed: secondaryMuscles := anObject
63 Removed: ]
src/MyFitnessTracker/MFTSession.class.st
index e477f403..dc74ca48 100644..100644
@@ -74,6 +74,13 @@
74 74 pageManager := aMFTPageManager
75 75 ]
76 76
77 Added: { #category : #'parent/child' }
78 Added: MFTSession >> unregistered [
79 Added:
80 Added: super unregistered.
81 Added: self logout
82 Added: ]
83 Added:
77 84 { #category : #accessing }
78 85 MFTSession >> user [
79 86
src/MyFitnessTracker/MFTSet.class.st
index 40e3d5f6..b761d93d 100644..100644
@@ -1,18 +1,16 @@
1 Removed: "
2 Removed: I represent a single set of an exercise.
3 Removed:
4 Removed: Instance variables
5 Removed: reps: number of repetitions performed by the user.
6 Removed: weight: weight used for the set.
7 Removed: rpe: rate of perceived exertion, from 1 to 10.
8 Removed: "
9 1 Class {
10 2 #name : #MFTSet,
11 3 #superclass : #Object,
12 4 #instVars : [
5 Added: 'id',
6 Added: 'workoutId',
7 Added: 'exerciseId',
8 Added: 'setGroup',
9 Added: 'setNumber',
13 10 'reps',
14 Removed: 'weight',
15 Removed: 'rpe'
11 Added: 'load',
12 Added: 'rpe',
13 Added: 'restAfter'
16 14 ],
17 15 #category : #'MyFitnessTracker-Model'
18 16 }
@@ -29,6 +27,130 @@
29 27 yourself
30 28 ]
31 29
30 Added: { #category : #'as yet unclassified' }
31 Added: MFTSet class >> id: id workoutId: workoutId exerciseId: exerciseId setGroup: setGroup setNumber: setNumber reps: reps [
32 Added: "These are the fields mandated by the database schema."
33 Added:
34 Added: ^ self new
35 Added: id: id;
36 Added: workoutId: workoutId;
37 Added: exerciseId: exerciseId;
38 Added: setGroup: setGroup;
39 Added: setNumber: setNumber;
40 Added: reps: reps;
41 Added: yourself
42 Added: ]
43 Added:
44 Added: { #category : #'instance creation' }
45 Added: MFTSet class >> newFromDictionary: aDictionary [
46 Added:
47 Added: ^ self new
48 Added: id: (aDictionary at: 'id');
49 Added: workoutId: (aDictionary at: 'workout_id');
50 Added: exerciseId: (aDictionary at: 'exercise_id');
51 Added: setGroup: (aDictionary at: 'set_group');
52 Added: setNumber: (aDictionary at: 'set');
53 Added: reps: (aDictionary at: 'reps');
54 Added: rpe: (aDictionary at: 'rpe');
55 Added: restAfter: (aDictionary at: 'rest_after');
56 Added: yourself
57 Added: ]
58 Added:
59 Added: { #category : #'as yet unclassified' }
60 Added: MFTSet class >> placeholderCollection [
61 Added:
62 Added: | forWorkout1 forWorkout2 forWorkout3 |
63 Added: forWorkout1 := {
64 Added: (self new
65 Added: id: 1;
66 Added: workoutId: 1;
67 Added: exerciseId: 2;
68 Added: setGroup: 1;
69 Added: setNumber: 1;
70 Added: reps: 8;
71 Added: load: 20;
72 Added: rpe: 8).
73 Added: (self new
74 Added: id: 2;
75 Added: workoutId: 1;
76 Added: exerciseId: 2;
77 Added: setGroup: 1;
78 Added: setNumber: 2;
79 Added: reps: 8;
80 Added: load: 25;
81 Added: rpe: 9).
82 Added: (self new
83 Added: id: 3;
84 Added: workoutId: 1;
85 Added: exerciseId: 2;
86 Added: setGroup: 1;
87 Added: setNumber: 3;
88 Added: reps: 8;
89 Added: load: 30;
90 Added: rpe: 10) }.
91 Added: forWorkout2 := {
92 Added: (self new
93 Added: id: 4;
94 Added: workoutId: 2;
95 Added: exerciseId: 3;
96 Added: setGroup: 1;
97 Added: setNumber: 1;
98 Added: reps: 50;
99 Added: load: 15;
100 Added: rpe: 4).
101 Added: (self new
102 Added: id: 5;
103 Added: workoutId: 2;
104 Added: exerciseId: 4;
105 Added: setGroup: 2;
106 Added: setNumber: 2;
107 Added: reps: 8;
108 Added: load: 25;
109 Added: rpe: 6).
110 Added: (self new
111 Added: id: 6;
112 Added: workoutId: 2;
113 Added: exerciseId: 4;
114 Added: setGroup: 2;
115 Added: setNumber: 3;
116 Added: reps: 8;
117 Added: load: 30;
118 Added: rpe: 8) }.
119 Added: forWorkout3 := {
120 Added: (self new
121 Added: id: 7;
122 Added: workoutId: 3;
123 Added: exerciseId: 10;
124 Added: setGroup: 1;
125 Added: setNumber: 1;
126 Added: reps: 5;
127 Added: load: 100;
128 Added: rpe: 8).
129 Added: (self new
130 Added: id: 8;
131 Added: workoutId: 3;
132 Added: exerciseId: 11;
133 Added: setGroup: 2;
134 Added: setNumber: 2;
135 Added: reps: 5;
136 Added: load: 100;
137 Added: rpe: 9).
138 Added: (self new
139 Added: id: 9;
140 Added: workoutId: 3;
141 Added: exerciseId: 12;
142 Added: setGroup: 3;
143 Added: setNumber: 3;
144 Added: reps: 5;
145 Added: load: 100;
146 Added: rpe: 10) }.
147 Added:
148 Added: ^ {
149 Added: forWorkout1.
150 Added: forWorkout2.
151 Added: forWorkout3 } flattened
152 Added: ]
153 Added:
32 154 { #category : #accessing }
33 155 MFTSet class >> reps: anInteger [
34 156
@@ -38,64 +160,136 @@
38 160 ]
39 161
40 162 { #category : #'as yet unclassified' }
41 Removed: MFTSet class >> reps: anInteger rpe: aPercentage [
163 Added: MFTSet class >> reps: anInteger load: anInteger2 [
42 164
43 165 ^ self new
44 166 reps: anInteger;
45 Removed: rpe: aPercentage;
167 Added: load: anInteger2;
46 168 yourself
47 169 ]
48 170
49 171 { #category : #'as yet unclassified' }
50 Removed: MFTSet class >> reps: anInteger weight: anInteger2 [
172 Added: MFTSet class >> reps: aValue load: aValue2 rpe: aValue3 [
51 173
52 174 ^ self new
53 Removed: reps: anInteger;
54 Removed: weight: anInteger2;
175 Added: reps: aValue;
176 Added: load: aValue2;
177 Added: rpe: aValue3;
55 178 yourself
56 179 ]
57 180
58 181 { #category : #'as yet unclassified' }
59 Removed: MFTSet class >> reps: aValue weight: aValue2 rpe: aValue3 [
182 Added: MFTSet class >> reps: aValue load: aValue2 rpe: aValue3 restAfter: aValue4 [
60 183
61 184 ^ self new
62 185 reps: aValue;
63 Removed: weight: aValue2;
186 Added: load: aValue2;
64 187 rpe: aValue3;
188 Added: restAfter: aValue4;
65 189 yourself
66 190 ]
67 191
68 Removed: { #category : #initialization }
69 Removed: MFTSet >> initialize [
192 Added: { #category : #'as yet unclassified' }
193 Added: MFTSet class >> reps: anInteger rpe: aPercentage [
70 194
71 Removed: super initialize.
72 Removed: reps := MFTUser defaultReps
195 Added: ^ self new
196 Added: reps: anInteger;
197 Added: rpe: aPercentage;
198 Added: yourself
73 199 ]
74 200
201 Added: { #category : #accessing }
202 Added: MFTSet >> exercise [
203 Added:
204 Added: ^ MFTSQLite3Database allExercises detect: [ :exercise |
205 Added: exercise id = exerciseId ]
206 Added: ]
207 Added:
208 Added: { #category : #accessing }
209 Added: MFTSet >> exerciseId [
210 Added:
211 Added: ^ exerciseId
212 Added: ]
213 Added:
214 Added: { #category : #accessing }
215 Added: MFTSet >> exerciseId: anInteger [
216 Added:
217 Added: exerciseId := anInteger
218 Added: ]
219 Added:
220 Added: { #category : #testing }
221 Added: MFTSet >> hasLoad [
222 Added:
223 Added: ^ load isNotNil and: [ load asString isNotEmpty ]
224 Added: ]
225 Added:
75 226 { #category : #'as yet unclassified' }
76 Removed: MFTSet >> jsonOn: aRenderer [
227 Added: MFTSet >> hasRestAfter [
77 228
78 Removed: | dict |
79 Removed: dict := Dictionary newFrom: {
80 Removed: (#reps -> reps).
81 Removed: (#weight -> weight).
82 Removed: (#rpe -> rpe) }.
83 Removed: dict jsonOn: aRenderer
229 Added: ^ restAfter isNotNil and: [ restAfter asString isNotEmpty ]
84 230 ]
85 231
232 Added: { #category : #'as yet unclassified' }
233 Added: MFTSet >> hasRpe [
234 Added:
235 Added: ^ rpe isNotNil and: [ rpe asString isNotEmpty ]
236 Added: ]
237 Added:
238 Added: { #category : #'as yet unclassified' }
239 Added: MFTSet >> hasVolume [
240 Added:
241 Added: ^ self volume ~~ 0
242 Added: ]
243 Added:
86 244 { #category : #accessing }
245 Added: MFTSet >> id [
246 Added:
247 Added: ^ id
248 Added: ]
249 Added:
250 Added: { #category : #accessing }
251 Added: MFTSet >> id: anObject [
252 Added:
253 Added: id := anObject
254 Added: ]
255 Added:
256 Added: { #category : #accessing }
257 Added: MFTSet >> load [
258 Added:
259 Added: ^ load
260 Added: ]
261 Added:
262 Added: { #category : #accessing }
263 Added: MFTSet >> load: anObject [
264 Added:
265 Added: load := anObject
266 Added: ]
267 Added:
268 Added: { #category : #accessing }
87 269 MFTSet >> reps [
88 270
89 271 ^ reps
90 272 ]
91 273
92 274 { #category : #accessing }
93 Removed: MFTSet >> reps: anInteger [
275 Added: MFTSet >> reps: anObject [
94 276
95 Removed: reps := anInteger
277 Added: reps := anObject
96 278 ]
97 279
98 280 { #category : #accessing }
281 Added: MFTSet >> restAfter [
282 Added:
283 Added: ^ restAfter
284 Added: ]
285 Added:
286 Added: { #category : #accessing }
287 Added: MFTSet >> restAfter: anObject [
288 Added:
289 Added: restAfter := anObject
290 Added: ]
291 Added:
292 Added: { #category : #accessing }
99 293 MFTSet >> rpe [
100 294
101 295 ^ rpe
@@ -107,22 +301,45 @@
107 301 rpe := anInteger
108 302 ]
109 303
304 Added: { #category : #accessing }
305 Added: MFTSet >> setGroup [
306 Added:
307 Added: ^ setGroup
308 Added: ]
309 Added:
310 Added: { #category : #accessing }
311 Added: MFTSet >> setGroup: anInteger [
312 Added:
313 Added: setGroup := anInteger
314 Added: ]
315 Added:
316 Added: { #category : #accessing }
317 Added: MFTSet >> setNumber [
318 Added:
319 Added: ^ setNumber
320 Added: ]
321 Added:
322 Added: { #category : #accessing }
323 Added: MFTSet >> setNumber: anInteger [
324 Added:
325 Added: setNumber := anInteger
326 Added: ]
327 Added:
110 328 { #category : #'as yet unclassified' }
111 329 MFTSet >> volume [
330 Added: "reps is never nil, as enforced by the database schema."
112 331
113 Removed: ^ (reps isNotNil and: [ weight isNotNil ])
114 Removed: ifTrue: [ reps asNumber * weight asNumber ]
115 Removed: ifFalse: [ 0 ]
332 Added: ^ load ifNotNil: [ reps * load ] ifNil: [ 0 ]
116 333 ]
117 334
118 335 { #category : #accessing }
119 Removed: MFTSet >> weight [
336 Added: MFTSet >> workoutId [
120 337
121 Removed: ^ weight
338 Added: ^ workoutId
122 339 ]
123 340
124 341 { #category : #accessing }
125 Removed: MFTSet >> weight: aNumber [
342 Added: MFTSet >> workoutId: anObject [
126 343
127 Removed: weight := aNumber
344 Added: workoutId := anObject
128 345 ]
src/MyFitnessTracker/MFTSetDialog.class.st
index 00000000..27b2a5d3 000000..100644
@@ -0,0 +1,112 @@
1 Added: Class {
2 Added: #name : #MFTSetDialog,
3 Added: #superclass : #MFTDialog,
4 Added: #instVars : [
5 Added: 'set'
6 Added: ],
7 Added: #category : #'MyFitnessTracker-Dialog'
8 Added: }
9 Added:
10 Added: { #category : #actions }
11 Added: MFTSetDialog class >> edit: aMFTSet [
12 Added:
13 Added: ^ self new
14 Added: set: aMFTSet;
15 Added: action: #edit;
16 Added: yourself
17 Added: ]
18 Added:
19 Added: { #category : #initialization }
20 Added: MFTSetDialog >> initialize [
21 Added:
22 Added: super initialize.
23 Added: set := MFTSet new
24 Added: ]
25 Added:
26 Added: { #category : #rendering }
27 Added: MFTSetDialog >> renderContentOn: html [
28 Added:
29 Added: self renderDialogOn: html with: [
30 Added: self renderExerciseSelectOn: html.
31 Added: html formGroup: [
32 Added: html label: 'Reps'.
33 Added: html numberInput formControl
34 Added: value: set reps;
35 Added: callback: [ :value | set reps: value asInteger ] ].
36 Added: html formGroup: [
37 Added: html label: 'Load'.
38 Added: html numberInput formControl
39 Added: value: set load;
40 Added: callback: [ :value |
41 Added: value ifNotEmpty: [ set load: value asNumber ] ] ].
42 Added: html formGroup: [
43 Added: html label: 'RPE'.
44 Added: html numberInput formControl
45 Added: value: set rpe;
46 Added: callback: [ :value |
47 Added: value ifNotEmpty: [ set rpe: value asInteger ] ] ].
48 Added: html formGroup: [
49 Added: html label: 'Rest after (s)'.
50 Added: html numberInput formControl
51 Added: value: set restAfter;
52 Added: callback: [ :value |
53 Added: value ifNotEmpty: [ set restAfter: value asNumber ] ] ] ]
54 Added: ]
55 Added:
56 Added: { #category : #rendering }
57 Added: MFTSetDialog >> renderExerciseSelectOn: html [
58 Added:
59 Added: | allExercises selectedExercise |
60 Added: allExercises := MFTSQLite3Database allExercises.
61 Added: selectedExercise := allExercises
62 Added: detect: [ :exercise |
63 Added: exercise id = set exerciseId ]
64 Added: ifNone: [ nil ].
65 Added: html formGroup: [
66 Added: html label: 'Exercise'.
67 Added: html select formControl
68 Added: list: allExercises;
69 Added: selected: selectedExercise;
70 Added: callback: [ :exercise | set exerciseId: exercise id ];
71 Added: labels: [ :exercise | exercise englishName ] ]
72 Added: ]
73 Added:
74 Added: { #category : #delegation }
75 Added: MFTSetDialog >> renderSubmitOn: html [
76 Added:
77 Added: html buttonGroup: [
78 Added: html formButton
79 Added: beSubmit;
80 Added: bePrimary;
81 Added: callback: [
82 Added: self answer: (set reps ifNotNil: [ set ] ifNil: [ nil ]) ];
83 Added: with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
84 Added: html formButton
85 Added: beSubmit;
86 Added: beSecondary;
87 Added: callback: [ self answer: nil ];
88 Added: with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
89 Added: ]
90 Added:
91 Added: { #category : #rendering }
92 Added: MFTSetDialog >> renderTableHeaderOn: html [
93 Added:
94 Added: html tableHead: [
95 Added: html
96 Added: tableHeading;
97 Added: tableHeading: 'Reps';
98 Added: tableHeading: 'Weight';
99 Added: tableHeading: 'RPE' ]
100 Added: ]
101 Added:
102 Added: { #category : #accessing }
103 Added: MFTSetDialog >> set [
104 Added:
105 Added: ^ set
106 Added: ]
107 Added:
108 Added: { #category : #accessing }
109 Added: MFTSetDialog >> set: anObject [
110 Added:
111 Added: set := anObject
112 Added: ]
src/MyFitnessTracker/MFTSetGroup.class.st
index 00000000..53f2f57e 000000..100644
@@ -0,0 +1,35 @@
1 Added: Class {
2 Added: #name : #MFTSetGroup,
3 Added: #superclass : #Object,
4 Added: #instVars : [
5 Added: 'sets'
6 Added: ],
7 Added: #category : #'MyFitnessTracker-Model'
8 Added: }
9 Added:
10 Added: { #category : #accessing }
11 Added: MFTSetGroup class >> sets: aMFTSetCollection [
12 Added:
13 Added: ^ self new
14 Added: sets: aMFTSetCollection;
15 Added: yourself
16 Added: ]
17 Added:
18 Added: { #category : #initialization }
19 Added: MFTSetGroup >> initialize [
20 Added:
21 Added: super initialize.
22 Added: sets := OrderedCollection new
23 Added: ]
24 Added:
25 Added: { #category : #accessing }
26 Added: MFTSetGroup >> sets [
27 Added:
28 Added: ^ sets
29 Added: ]
30 Added:
31 Added: { #category : #accessing }
32 Added: MFTSetGroup >> sets: anObject [
33 Added:
34 Added: sets := anObject
35 Added: ]
src/MyFitnessTracker/MFTSetsDialog.class.st
index 00000000..1680cb10 000000..100644
@@ -0,0 +1,168 @@
1 Added: Class {
2 Added: #name : #MFTSetsDialog,
3 Added: #superclass : #MFTDialog,
4 Added: #instVars : [
5 Added: 'sets'
6 Added: ],
7 Added: #category : #'MyFitnessTracker-Dialog'
8 Added: }
9 Added:
10 Added: { #category : #actions }
11 Added: MFTSetsDialog class >> edit: aMFTSetCollection [
12 Added:
13 Added: ^ self new
14 Added: sets: aMFTSetCollection;
15 Added: action: #edit;
16 Added: yourself
17 Added: ]
18 Added:
19 Added: { #category : #adding }
20 Added: MFTSetsDialog >> addSet [
21 Added:
22 Added: | set setsSize |
23 Added: setsSize := sets size + 1.
24 Added: set := self call:
25 Added: (MFTSetDialog new heading: 'Set #' , setsSize asString).
26 Added: set ifNil: [ ^ self ].
27 Added: set
28 Added: setNumber: setsSize;
29 Added: setGroup: setsSize.
30 Added: sets add: set
31 Added: ]
32 Added:
33 Added: { #category : #adding }
34 Added: MFTSetsDialog >> addSuperset [
35 Added:
36 Added: | superset |
37 Added: superset := self call: MFTSetDialog new.
38 Added: superset ifNotNil: [ sets add: superset ]
39 Added: ]
40 Added:
41 Added: { #category : #initialization }
42 Added: MFTSetsDialog >> initialize [
43 Added:
44 Added: super initialize.
45 Added: sets := OrderedCollection new
46 Added: ]
47 Added:
48 Added: { #category : #rendering }
49 Added: MFTSetsDialog >> renderAddSetButtonsOn: html [
50 Added:
51 Added: html buttonGroup: [
52 Added: html outlineButton
53 Added: bePrimary;
54 Added: beSmall;
55 Added: callback: [ self addSet ];
56 Added: with: 'Add set'.
57 Added: html outlineButton
58 Added: beSecondary;
59 Added: beSmall;
60 Added: callback: [ self addSuperset ];
61 Added: with: 'Add superset' ]
62 Added: ]
63 Added:
64 Added: { #category : #rendering }
65 Added: MFTSetsDialog >> renderContentOn: html [
66 Added:
67 Added: self renderDialogOn: html with: [
68 Added: sets ifNotEmpty: [ self renderSetsTableOn: html ].
69 Added: self renderAddSetButtonsOn: html ]
70 Added: ]
71 Added:
72 Added: { #category : #rendering }
73 Added: MFTSetsDialog >> renderLoad [
74 Added:
75 Added: ^ sets anySatisfy: #hasLoad
76 Added: ]
77 Added:
78 Added: { #category : #rendering }
79 Added: MFTSetsDialog >> renderRestAfter [
80 Added:
81 Added: ^ sets anySatisfy: #hasRestAfter
82 Added: ]
83 Added:
84 Added: { #category : #rendering }
85 Added: MFTSetsDialog >> renderRpe [
86 Added:
87 Added: ^ sets anySatisfy: #hasRpe
88 Added: ]
89 Added:
90 Added: { #category : #rendering }
91 Added: MFTSetsDialog >> renderSetsRowsOn: html [
92 Added:
93 Added: | renderLoad renderRpe renderRestAfter |
94 Added: renderLoad := self renderLoad.
95 Added: renderRpe := self renderRpe.
96 Added: renderRestAfter := self renderRestAfter.
97 Added: sets do: [ :set |
98 Added: html tableRow: [
99 Added: html tableData: [
100 Added: html buttonGroup: [
101 Added: html outlineButton
102 Added: beDanger;
103 Added: beSmall;
104 Added: callback: [ sets remove: set ];
105 Added: with: 'Remove'.
106 Added: html outlineButton
107 Added: beSecondary;
108 Added: beSmall;
109 Added: callback: [ sets add: set afterIndex: (sets indexOf: set) ];
110 Added: with: 'Duplicate' ].
111 Added: html text: set setNumber asString , ' ' , set setGroup asString ].
112 Added: html tableData: set exercise englishName.
113 Added: html tableData: set reps.
114 Added: renderLoad ifTrue: [ html tableData: set load ].
115 Added: renderRpe ifTrue: [ html tableData: set rpe ].
116 Added: renderRestAfter ifTrue: [ html tableData: set restAfter ] ] ]
117 Added: ]
118 Added:
119 Added: { #category : #rendering }
120 Added: MFTSetsDialog >> renderSetsTableHeaderOn: html [
121 Added:
122 Added: html tableHead: [
123 Added: html
124 Added: tableHeading;
125 Added: tableHeading: 'Exercise';
126 Added: tableHeading: 'Reps'.
127 Added: self renderLoad ifTrue: [ html tableHeading: 'Load' ].
128 Added: self renderRpe ifTrue: [ html tableHeading: 'RPE' ].
129 Added: self renderRestAfter ifTrue: [ html tableHeading: 'Rest after (s) ' ] ]
130 Added: ]
131 Added:
132 Added: { #category : #rendering }
133 Added: MFTSetsDialog >> renderSetsTableOn: html [
134 Added:
135 Added: html table
136 Added: class: 'table table-hover table-bordered';
137 Added: with: [
138 Added: self renderSetsTableHeaderOn: html.
139 Added: self renderSetsRowsOn: html ]
140 Added: ]
141 Added:
142 Added: { #category : #rendering }
143 Added: MFTSetsDialog >> renderSubmitOn: html [
144 Added:
145 Added: html buttonGroup: [
146 Added: html formButton
147 Added: beSubmit;
148 Added: bePrimary;
149 Added: callback: [ self answer: sets ];
150 Added: with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
151 Added: html formButton
152 Added: beSubmit;
153 Added: beSecondary;
154 Added: callback: [ self answer: OrderedCollection new ];
155 Added: with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
156 Added: ]
157 Added:
158 Added: { #category : #accessing }
159 Added: MFTSetsDialog >> sets [
160 Added:
161 Added: ^ sets
162 Added: ]
163 Added:
164 Added: { #category : #accessing }
165 Added: MFTSetsDialog >> sets: anObject [
166 Added:
167 Added: sets := anObject
168 Added: ]
src/MyFitnessTracker/MFTStringDialog.class.st
index a51ebaea..51903d33 100644..100644
@@ -4,7 +4,7 @@
4 4 #instVars : [
5 5 'string'
6 6 ],
7 Removed: #category : #'MyFitnessTracker-Dialogs'
7 Added: #category : #'MyFitnessTracker-Dialog'
8 8 }
9 9
10 10 { #category : #actions }
src/MyFitnessTracker/MFTUser.class.st
index 1f92e04f..52cc7a7e 100644..100644
@@ -2,13 +2,15 @@
2 2 #name : #MFTUser,
3 3 #superclass : #SBSComponent,
4 4 #instVars : [
5 Added: 'id',
5 6 'username',
6 7 'passwordHash',
7 Removed: 'dateOfBirth',
8 8 'realFirstName',
9 9 'realLastName',
10 Added: 'dateOfBirth',
10 11 'isLoggedIn',
11 12 'lastLoginDateAndTime',
13 Added: 'accountRegisterDateAndTime',
12 14 'workouts',
13 15 'weights'
14 16 ],
@@ -42,30 +44,32 @@
42 44
43 45 { #category : #'instance creation' }
44 46 MFTUser class >> newFromDictionary: aDictionary [
45 Removed: "Dictionary keys are the SQLite3 column names."
46 47
47 48 ^ self new
48 Removed: username: (aDictionary at: 'username' ifAbsent: [ String new ]);
49 Removed: passwordHash:
50 Removed: (aDictionary at: 'password_hash' ifAbsent: [ ByteArray new ]);
51 Removed: dateOfBirth:
52 Removed: (aDictionary at: 'date_of_birth' ifAbsent: [ Date new ]);
53 Removed: realFirstName:
54 Removed: (aDictionary at: 'real_first_name' ifAbsent: [ String new ]);
55 Removed: realLastName:
56 Removed: (aDictionary at: 'real_last_name' ifAbsent: [ String new ]);
49 Added: id: (aDictionary at: 'id');
50 Added: username: (aDictionary at: 'username');
51 Added: passwordHash: (aDictionary at: 'password_hash');
52 Added: realFirstName: (aDictionary at: 'real_first_name');
53 Added: realLastName: (aDictionary at: 'real_last_name');
54 Added: dateOfBirth: (aDictionary at: 'date_of_birth');
55 Added: lastLoginDateAndTime: (aDictionary at: 'last_login_date_time');
56 Added: accountRegisterDateAndTime:
57 Added: (aDictionary at: 'account_register_date_time');
57 58 yourself
58 59 ]
59 60
60 Removed: { #category : #'instance creation' }
61 Removed: MFTUser class >> newFromID: id [
61 Added: { #category : #accessing }
62 Added: MFTUser >> accountRegisterDateAndTime [
62 63
63 Removed: ^ self
64 Removed: newFromDictionary:
65 Removed: (MFTSQLite3Database selectUser: id) asDictionary;
66 Removed: yourself
64 Added: ^ accountRegisterDateAndTime
67 65 ]
68 66
67 Added: { #category : #accessing }
68 Added: MFTUser >> accountRegisterDateAndTime: anObject [
69 Added:
70 Added: accountRegisterDateAndTime := anObject
71 Added: ]
72 Added:
69 73 { #category : #'as yet unclassified' }
70 74 MFTUser >> dataDictionary [
71 75
@@ -87,6 +91,18 @@
87 91 MFTUser >> dateOfBirth: aDate [
88 92
89 93 dateOfBirth := aDate
94 Added: ]
95 Added:
96 Added: { #category : #accessing }
97 Added: MFTUser >> id [
98 Added:
99 Added: ^ id
100 Added: ]
101 Added:
102 Added: { #category : #accessing }
103 Added: MFTUser >> id: anObject [
104 Added:
105 Added: id := anObject
90 106 ]
91 107
92 108 { #category : #initialization }
src/MyFitnessTracker/MFTUserTask.class.st
index c3c6bfa0..678e8f0e 100644..100644
@@ -5,7 +5,7 @@
5 5 'user',
6 6 'action'
7 7 ],
8 Removed: #category : #'MyFitnessTracker-Dialogs'
8 Added: #category : #'MyFitnessTracker-Dialog'
9 9 }
10 10
11 11 { #category : #actions }
src/MyFitnessTracker/MFTWeight.class.st
index 02729224..58777c83 100644..100644
@@ -2,6 +2,8 @@
2 2 #name : #MFTWeight,
3 3 #superclass : #Object,
4 4 #instVars : [
5 Added: 'id',
6 Added: 'userId',
5 7 'date',
6 8 'value'
7 9 ],
@@ -72,19 +74,41 @@
72 74 date := aDate
73 75 ]
74 76
77 Added: { #category : #accessing }
78 Added: MFTWeight >> id [
79 Added:
80 Added: ^ id
81 Added: ]
82 Added:
83 Added: { #category : #accessing }
84 Added: MFTWeight >> id: anObject [
85 Added:
86 Added: id := anObject
87 Added: ]
88 Added:
75 89 { #category : #initialization }
76 90 MFTWeight >> initialize [
77 91
78 92 super initialize.
79 Removed: date := Date today.
80 Removed: value := 0
81 Removed: "Default value should really be the user's most recently recorded weight's value..."
93 Added: date := Date today
82 94 ]
83 95
96 Added: { #category : #accessing }
97 Added: MFTWeight >> userId [
98 Added:
99 Added: ^ userId
100 Added: ]
101 Added:
102 Added: { #category : #accessing }
103 Added: MFTWeight >> userId: anObject [
104 Added:
105 Added: userId := anObject
106 Added: ]
107 Added:
84 108 { #category : #evaluating }
85 109 MFTWeight >> value [
86 110
87 Removed: ^ value asNumber
111 Added: ^ value
88 112 ]
89 113
90 114 { #category : #accessing }
src/MyFitnessTracker/MFTWeightDialog.class.st
index e841602b..ac22752d 100644..100644
@@ -4,7 +4,7 @@
4 4 #instVars : [
5 5 'weight'
6 6 ],
7 Removed: #category : #'MyFitnessTracker-Dialogs'
7 Added: #category : #'MyFitnessTracker-Dialog'
8 8 }
9 9
10 10 { #category : #actions }
@@ -39,7 +39,8 @@
39 39 ifTrue: [ nil ]
40 40 ifFalse: [ weight value ]);
41 41 placeholder: weight value;
42 Removed: callback: [ :value | weight value: value ] ] ]
42 Added: callback: [ :value |
43 Added: value ifNotEmpty: [ weight value: value asNumber ] ] ] ]
43 44 ]
44 45
45 46 { #category : #rendering }
@@ -49,12 +50,13 @@
49 50 html formButton
50 51 beSubmit;
51 52 bePrimary;
52 Removed: callback: [ self answer: weight ];
53 Added: callback: [
54 Added: self answer: (weight value ifNotNil: [ weight ] ifNil: [ nil ]) ];
53 55 with: (completion ifNil: [ 'Submit' ] ifNotNil: [ 'Next' ]).
54 56 html formButton
55 57 beSubmit;
56 58 beSecondary;
57 Removed: callback: [ self answer: MFTWeight new ];
59 Added: callback: [ self answer: nil ];
58 60 with: (completion ifNil: [ 'Cancel' ] ifNotNil: [ 'Skip' ]) ]
59 61 ]
60 62
src/MyFitnessTracker/MFTWeightsListPage.class.st
index ec2b8fe1..c4453acc 100644..100644
@@ -12,9 +12,10 @@
12 12
13 13 | newWeight |
14 14 newWeight := self call: MFTWeightDialog new.
15 Added: newWeight ifNil: [ ^ self ].
15 16 (weights anySatisfy: [ :existingWeight |
16 Removed: newWeight date = existingWeight date and: [
17 Removed: newWeight value = existingWeight value ] ]) ifTrue: [ ^ self ].
17 Added: existingWeight date = newWeight date and: [
18 Added: existingWeight value = newWeight value ] ]) ifTrue: [ ^ self ].
18 19
19 20 self session user isLoggedIn
20 21 ifTrue: [ self session db insert: newWeight ]
src/MyFitnessTracker/MFTWorkout.class.st
index 66d54b51..1b828a0a 100644..100644
@@ -2,132 +2,73 @@
2 2 #name : #MFTWorkout,
3 3 #superclass : #SBSComponent,
4 4 #instVars : [
5 Added: 'id',
6 Added: 'userId',
5 7 'date',
6 Removed: 'exercises',
7 Removed: 'primaryMuscle',
8 Removed: 'secondaryMuscles'
8 Added: 'primaryMuscleGroupId',
9 Added: 'secondaryMuscleGroupIds',
10 Added: 'sets'
9 11 ],
10 12 #category : #'MyFitnessTracker-Model'
11 13 }
12 14
13 Removed: { #category : #'as yet unclassified' }
14 Removed: MFTWorkout class >> allAthletes [
15 Removed:
16 Removed: ^ { #'Arnold Alois Schwarzenneger'. #'Michael John Mentzer'.
17 Removed: #'Thomas Steven Platz' }
18 Removed: ]
19 Removed:
20 15 { #category : #'instance creation' }
21 16 MFTWorkout class >> newFromDictionary: aDictionary [
22 17
23 Removed: | primaryMuscleConstructor primaryMuscle secondaryMusclesContructors secondaryMuscles |
24 Removed: primaryMuscleConstructor := (aDictionary
25 Removed: at: 'primary_muscle'
26 Removed: ifAbsent: [ String new ]) asSymbol.
27 Removed: primaryMuscle := (MFTMuscleGroup respondsTo: primaryMuscleConstructor)
28 Removed: ifTrue: [
29 Removed: MFTMuscleGroup perform: primaryMuscleConstructor ]
30 Removed: ifFalse: [ MFTMuscleGroup new ].
31 Removed: secondaryMusclesContructors := ((aDictionary
32 Removed: at: 'secondary_muscles'
33 Removed: ifAbsent: [ String new ]) splitOn:
34 Removed: ', ') collect: #asSymbol.
35 Removed: secondaryMusclesContructors removeAllSuchThat: [ :each | each = #'' ].
36 Removed: secondaryMuscles := secondaryMusclesContructors collect: [ :symbol |
37 Removed: (MFTMuscleGroup respondsTo: symbol)
38 Removed: ifTrue: [ MFTMuscleGroup perform: symbol ]
39 Removed: ifFalse: [ nil ] ].
40 Removed:
41 18 ^ self new
19 Added: id: (aDictionary at: 'id');
20 Added: userId: (aDictionary at: 'user_id');
42 21 date: (Date fromString: (aDictionary at: 'date'));
43 Removed: primaryMuscle: primaryMuscle;
44 Removed: secondaryMuscles: secondaryMuscles;
22 Added: primaryMuscleGroupId: (aDictionary at: 'primary_muscle_group_id');
45 23 yourself
46 24 ]
47 25
48 26 { #category : #'as yet unclassified' }
49 27 MFTWorkout class >> placeholderCollection [
50 28
51 Removed: | workout1 workout2 workout3 workout4 workout5 |
29 Added: | allMuscleGroups allExercises placeholderSets workout1 workout2 workout3 |
30 Added: allMuscleGroups := MFTSQLite3Database allMuscleGroupsAsDict.
31 Added: allExercises := MFTSQLite3Database allExercisesAsDict.
32 Added: placeholderSets := MFTSet placeholderCollection.
52 33 workout1 := self new
53 Removed: date: (Date fromString: '01/01/2024');
54 Removed: primaryMuscle: MFTMuscleGroup chest;
55 Removed: secondaryMuscles: {
56 Removed: MFTMuscle tricepsBrachii.
57 Removed: MFTMuscleGroup arm };
58 Removed: exercises: {
59 Removed: (MFTExercise cableRow sets: {
60 Removed: (MFTSet reps: 8 weight: 20 rpe: 6).
61 Removed: (MFTSet reps: 6 weight: 20 rpe: 8).
62 Removed: (MFTSet reps: 6 weight: 20 rpe: 10).
63 Removed: (MFTSet reps: 15 rpe: 4) }).
64 Removed: (MFTExercise shoulderPressDumbell sets: {
65 Removed: (MFTSet reps: 10 weight: 10 rpe: 6).
66 Removed: (MFTSet reps: 6 weight: 15 rpe: 8).
67 Removed: (MFTSet reps: 6 weight: 17.5 rpe: 10) }).
68 Removed: (MFTExercise declineBenchPress sets: {
69 Removed: (MFTSet reps: 5 rpe: 6).
70 Removed: (MFTSet reps: 6 rpe: 8).
71 Removed: (MFTSet reps: 8 rpe: 10) }) }.
34 Added: id: 1;
35 Added: userId: 1;
36 Added: date: (Date fromString: '2024-01-01');
37 Added: primaryMuscleGroupId: 4;
38 Added: sets:
39 Added: (placeholderSets select: [ :set | set workoutId = 1 ]).
72 40 workout2 := self new
73 Removed: date: (Date fromString: '01/02/2024');
74 Removed: exercises: {
75 Removed: (MFTExercise cableRow sets: {
76 Removed: (MFTSet reps: 8 rpe: 6).
77 Removed: (MFTSet reps: 6 rpe: 8).
78 Removed: (MFTSet reps: 6 rpe: 10).
79 Removed: (MFTSet reps: 15 rpe: 4) }).
80 Removed: MFTExercise shoulderPressDumbell.
81 Removed: MFTExercise declineBenchPress }.
41 Added: id: 2;
42 Added: userId: 1;
43 Added: date: (Date fromString: '2024-01-02');
44 Added: primaryMuscleGroupId: 5;
45 Added: sets:
46 Added: (placeholderSets select: [ :set | set workoutId = 2 ]).
82 47 workout3 := self new
83 Removed: date: (Date fromString: '01/03/2024');
84 Removed: addExercise: MFTExercise cableRow;
85 Removed: addExercise: MFTExercise shoulderPressDumbell;
86 Removed: addExercise: MFTExercise declineBenchPress.
87 Removed: workout4 := self new
88 Removed: date: (Date fromString: '01/09/2024');
89 Removed: exercises: {
90 Removed: MFTExercise cableRow.
91 Removed: MFTExercise shoulderPressDumbell.
92 Removed: MFTExercise declineBenchPress }.
93 Removed: workout5 := self new
94 Removed: date: (Date fromString: '01/10/2024');
95 Removed: exercises: {
96 Removed: MFTExercise cableRow.
97 Removed: MFTExercise shoulderPressDumbell.
98 Removed: MFTExercise declineBenchPress }.
99 Removed:
48 Added: id: 3;
49 Added: userId: 1;
50 Added: date: (Date fromString: '2024-01-03');
51 Added: primaryMuscleGroupId: 6;
52 Added: sets:
53 Added: (placeholderSets select: [ :set | set workoutId = 3 ]).
100 54 ^ OrderedCollection withAll: {
101 55 workout1.
102 56 workout2.
103 Removed: workout3.
104 Removed: workout4.
105 Removed: workout5 }
57 Added: workout3 }
106 58 ]
107 59
108 Removed: { #category : #accessing }
109 Removed: MFTWorkout class >> primaryMuscle: aMFTMuscle [
60 Added: { #category : #'as yet unclassified' }
61 Added: MFTWorkout >> anySetHasLoad [
110 62
111 Removed: ^ self new
112 Removed: primaryMuscle: aMFTMuscle;
113 Removed: yourself
63 Added: ^ sets anySatisfy: #hasLoad
114 64 ]
115 65
116 Removed: { #category : #accessing }
117 Removed: MFTWorkout class >> primaryMuscle: aMFTMuscle secondaryMuscles: aMFTMuscleCollection [
66 Added: { #category : #'as yet unclassified' }
67 Added: MFTWorkout >> anySetHasVolume [
118 68
119 Removed: ^ self new
120 Removed: primaryMuscle: aMFTMuscle;
121 Removed: secondaryMuscles: aMFTMuscleCollection;
122 Removed: yourself
69 Added: ^ sets anySatisfy: #hasVolume
123 70 ]
124 71
125 Removed: { #category : #initialization }
126 Removed: MFTWorkout >> addExercise: aMFTExercise [
127 Removed:
128 Removed: exercises add: aMFTExercise
129 Removed: ]
130 Removed:
131 72 { #category : #accessing }
132 73 MFTWorkout >> date [
133 74 ^ date
@@ -138,18 +79,6 @@
138 79 date := aDate
139 80 ]
140 81
141 Removed: { #category : #accessing }
142 Removed: MFTWorkout >> exercises [
143 Removed:
144 Removed: ^ exercises
145 Removed: ]
146 Removed:
147 Removed: { #category : #accessing }
148 Removed: MFTWorkout >> exercises: aMFTExerciseCollection [
149 Removed:
150 Removed: exercises := aMFTExerciseCollection asOrderedCollection
151 Removed: ]
152 Removed:
153 82 { #category : #'as yet unclassified' }
154 83 MFTWorkout >> guessPrimaryMuscle [
155 84 "TODO: guess the workout's primary muscle based on the most represented primary muscle in the workout's exercises."
@@ -161,28 +90,47 @@
161 90 (groupedByMuscle at: muscle) size ]
162 91 ]
163 92
93 Added: { #category : #accessing }
94 Added: MFTWorkout >> id [
95 Added:
96 Added: ^ id
97 Added: ]
98 Added:
99 Added: { #category : #accessing }
100 Added: MFTWorkout >> id: anObject [
101 Added:
102 Added: id := anObject
103 Added: ]
104 Added:
164 105 { #category : #initialization }
165 106 MFTWorkout >> initialize [
166 107
167 108 super initialize.
168 109 date := Date today.
169 Removed: exercises := OrderedCollection new.
170 Removed: primaryMuscle := MFTMuscle new.
171 Removed: secondaryMuscles := OrderedCollection new
110 Added: sets := OrderedCollection new.
111 Added: secondaryMuscleGroupIds := OrderedCollection new
172 112 ]
173 113
174 114 { #category : #accessing }
175 Removed: MFTWorkout >> primaryMuscle [
115 Added: MFTWorkout >> primaryMuscleGroup [
176 116
177 Removed: ^ primaryMuscle
117 Added: ^ MFTSQLite3Database allMuscleGroups
118 Added: detect: [ :group | group id = primaryMuscleGroupId ]
119 Added: ifNone: [ nil ]
178 120 ]
179 121
180 122 { #category : #accessing }
181 Removed: MFTWorkout >> primaryMuscle: aMFTMuscle [
123 Added: MFTWorkout >> primaryMuscleGroupId [
182 124
183 Removed: primaryMuscle := aMFTMuscle
125 Added: ^ primaryMuscleGroupId
184 126 ]
185 127
128 Added: { #category : #accessing }
129 Added: MFTWorkout >> primaryMuscleGroupId: anInteger [
130 Added:
131 Added: primaryMuscleGroupId := anInteger
132 Added: ]
133 Added:
186 134 { #category : #rendering }
187 135 MFTWorkout >> renderContentOn: html [
188 136
@@ -190,31 +138,31 @@
190 138 html
191 139 definitionTerm: 'Date';
192 140 definitionData: date.
193 Removed: primaryMuscle englishName ifNotEmpty: [
141 Added: self primaryMuscleGroup ifNotNil: [
194 142 html
195 Removed: definitionTerm: 'Primary Muscle';
196 Removed: definitionData: primaryMuscle englishName ].
197 Removed: secondaryMuscles ifNotEmpty: [
143 Added: definitionTerm: 'Primary Muscle Group';
144 Added: definitionData: self primaryMuscleGroup englishName ].
145 Added: self secondaryMuscleGroups ifNotEmpty: [
198 146 html
199 Removed: definitionTerm: 'Secondary Muscles';
147 Added: definitionTerm: 'Secondary Muscle Group';
200 148 definitionData:
201 Removed: (secondaryMuscles collect: #englishName) asCommaString ] ].
202 Removed: exercises ifNotEmpty: [ self renderExerciseTableOn: html ]
149 Added: (self secondaryMuscleGroups collect: #englishName) asCommaString ] ].
150 Added: sets ifNotEmpty: [ self renderSetsTableOn: html ]
203 151 ]
204 152
205 153 { #category : #rendering }
206 Removed: MFTWorkout >> renderExerciseTableFooterOn: html [
154 Added: MFTWorkout >> renderSetsTableFooterOn: html [
207 155
208 156 | totalReps totalVolume |
209 Removed: totalReps := (exercises collect: [ :exercise |
210 Removed: exercise repsBySet sum ]) sum.
211 Removed: totalVolume := (exercises collect: #volume) sum.
212 Removed:
157 Added: totalReps := (sets collect: #reps) sum.
158 Added: totalVolume := (sets collect: #volume) sum.
213 159 html tableFoot: [
160 Added: html tableData.
214 161 html tableData: 'Total'.
215 162 html tableData
216 163 style: 'text-align: right';
217 164 with: totalReps.
165 Added: self anySetHasLoad ifTrue: [ html tableData ].
218 166 totalVolume ~~ 0 ifTrue: [
219 167 html tableData
220 168 style: 'text-align: right';
@@ -222,61 +170,106 @@
222 170 ]
223 171
224 172 { #category : #rendering }
225 Removed: MFTWorkout >> renderExerciseTableOn: html [
173 Added: MFTWorkout >> renderSetsTableHeaderOn: html [
226 174
227 Removed: | anExerciseHasVolume |
228 Removed: anExerciseHasVolume := exercises anySatisfy: [ :ex | ex volume ~~ 0 ].
175 Added: html tableHead: [
176 Added: html tableHeading.
177 Added: html tableHeading: 'Exercise'.
178 Added: html tableHeading
179 Added: style: 'text-align: right';
180 Added: with: 'Reps'.
181 Added: self anySetHasLoad ifTrue: [
182 Added: html tableHeading
183 Added: style: 'text-align: right';
184 Added: with: 'Load' ].
185 Added: self anySetHasVolume ifTrue: [
186 Added: html tableHeading
187 Added: style: 'text-align: right';
188 Added: with: 'Volume' ] ]
189 Added: ]
190 Added:
191 Added: { #category : #rendering }
192 Added: MFTWorkout >> renderSetsTableOn: html [
193 Added:
194 Added: | renderLoad renderVolume |
195 Added: renderLoad := self anySetHasLoad.
196 Added: renderVolume := self anySetHasVolume.
197 Added:
229 198 html table
230 199 class: 'table table-striped table-bordered';
231 200 with: [
232 Removed: self renderTableHeaderOn: html.
233 Removed: exercises do: [ :exercise |
201 Added: self renderSetsTableHeaderOn: html.
202 Added: sets do: [ :set |
234 203 html tableRow: [
235 Removed: html tableData: [
236 Removed: html text: exercise englishName.
237 Removed: exercise sets ifNotEmpty: [
238 Removed: html
239 Removed: text: ': ';
240 Removed: text: exercise repsBySet asCommaString ] ].
241 204 html tableData
242 205 style: 'text-align: right';
243 Removed: with: exercise repsBySet sum.
244 Removed: anExerciseHasVolume ifTrue: [
206 Added: with: set setNumber asString, ' ', set setGroup asString.
207 Added: html tableData: set exercise englishName capitalized.
208 Added: html tableData
209 Added: style: 'text-align: right';
210 Added: with: set reps.
211 Added: renderLoad ifTrue: [
245 212 html tableData
246 213 style: 'text-align: right';
247 Removed: with: exercise volume ] ] ].
248 Removed: self renderExerciseTableFooterOn: html ]
214 Added: with: set load ].
215 Added: renderVolume ifTrue: [
216 Added: html tableData
217 Added: style: 'text-align: right';
218 Added: with: set volume ] ] ].
219 Added: self renderSetsTableFooterOn: html ]
249 220 ]
250 221
251 Removed: { #category : #rendering }
252 Removed: MFTWorkout >> renderTableHeaderOn: html [
222 Added: { #category : #accessing }
223 Added: MFTWorkout >> secondaryMuscleGroupIds [
253 224
254 Removed: html tableHead: [
255 Removed: html tableHeading: 'Exercise'.
256 Removed: html tableHeading
257 Removed: style: 'text-align: right';
258 Removed: with: 'Reps'.
259 Removed: (exercises anySatisfy: [ :exercise | exercise volume ~~ 0 ])
260 Removed: ifTrue: [
261 Removed: html tableHeading
262 Removed: style: 'text-align: right';
263 Removed: with: 'Volume' ] ]
225 Added: ^ secondaryMuscleGroupIds
264 226 ]
265 227
266 228 { #category : #accessing }
267 Removed: MFTWorkout >> secondaryMuscles [
229 Added: MFTWorkout >> secondaryMuscleGroupIds: anIntegerCollection [
268 230
269 Removed: ^ secondaryMuscles
231 Added: secondaryMuscleGroupIds := anIntegerCollection
270 232 ]
271 233
234 Added: { #category : #'as yet unclassified' }
235 Added: MFTWorkout >> secondaryMuscleGroups [
236 Added:
237 Added: ^ MFTSQLite3Database allMuscleGroups select: [ :group |
238 Added: secondaryMuscleGroupIds includes: group id ]
239 Added: ]
240 Added:
272 241 { #category : #accessing }
273 Removed: MFTWorkout >> secondaryMuscles: aMFTMuscleCollection [
242 Added: MFTWorkout >> setGroups [
274 243
275 Removed: secondaryMuscles := aMFTMuscleCollection asOrderedCollection
244 Added: ^ sets groupedBy: #setGroup
276 245 ]
277 246
247 Added: { #category : #accessing }
248 Added: MFTWorkout >> sets [
249 Added:
250 Added: ^ sets
251 Added: ]
252 Added:
253 Added: { #category : #accessing }
254 Added: MFTWorkout >> sets: anObject [
255 Added:
256 Added: sets := anObject
257 Added: ]
258 Added:
259 Added: { #category : #accessing }
260 Added: MFTWorkout >> userId [
261 Added:
262 Added: ^ userId
263 Added: ]
264 Added:
265 Added: { #category : #accessing }
266 Added: MFTWorkout >> userId: anObject [
267 Added:
268 Added: userId := anObject
269 Added: ]
270 Added:
278 271 { #category : #'as yet unclassified' }
279 272 MFTWorkout >> volume [
280 273
281 Removed: ^ (exercises collect: #volume) sum
274 Added: ^ (sets collect: #volume) sum
282 275 ]
src/MyFitnessTracker/MFTWorkoutTask.class.st
index aba391f8..b91b732a 100644..100644
@@ -5,7 +5,7 @@
5 5 'workout',
6 6 'action'
7 7 ],
8 Removed: #category : #'MyFitnessTracker-Dialogs'
8 Added: #category : #'MyFitnessTracker-Dialog'
9 9 }
10 10
11 11 { #category : #actions }
@@ -32,22 +32,30 @@
32 32 { #category : #running }
33 33 MFTWorkoutTask >> go [
34 34
35 Added: | primaryMuscleGroup secondaryMuscleGroups |
36 Added: workout userId: self session user id.
35 37 workout date: (self call: (MFTDateDialog new
36 38 action: action;
37 39 date: workout date;
38 40 completion: 20)).
39 Removed: workout exercises: (self call: (MFTExercisesDialog new
41 Added: workout sets: (self call: (MFTSetsDialog new
40 42 action: action;
41 Removed: exercises: workout exercises;
43 Added: sets: workout sets;
42 44 completion: 40)).
43 Removed: workout primaryMuscle: (self call: (MFTPrimaryMuscleDialog new
44 Removed: action: action;
45 Removed: primaryMuscle: workout primaryMuscle;
46 Removed: completion: 60)).
47 Removed: workout secondaryMuscles: (self call: (MFTSecondaryMusclesDialog new
48 Removed: action: action;
49 Removed: secondaryMuscles: workout secondaryMuscles;
50 Removed: completion: 80)).
45 Added: primaryMuscleGroup := self call: (MFTPrimaryMuscleGroupDialog new
46 Added: action: action;
47 Added: primaryMuscleGroup:
48 Added: workout primaryMuscleGroup;
49 Added: completion: 60).
50 Added: primaryMuscleGroup ifNotNil: [
51 Added: workout primaryMuscleGroupId: primaryMuscleGroup id ].
52 Added: secondaryMuscleGroups := self call:
53 Added: (MFTSecondaryMuscleGroupsDialog new
54 Added: action: action;
55 Added: secondaryMuscleGroups:
56 Added: workout secondaryMuscleGroups;
57 Added: completion: 80).
58 Added: workout secondaryMuscleGroupIds: (secondaryMuscleGroups collect: #id).
51 59
52 60 (self call: (MFTConfirmDialog new
53 61 action: action;
src/MyFitnessTracker/MFTWorkoutsListPage.class.st
index 377cb131..4dfb50dc 100644..100644
@@ -148,11 +148,11 @@
148 148 with: [
149 149 html text:
150 150 workout date yyyymmdd , ', ' , workout date dayOfWeekName.
151 Removed: workout primaryMuscle englishName ifNotEmpty: [
151 Added: workout primaryMuscleGroup ifNotNil: [
152 152 html
153 153 text: ':';
154 154 space;
155 Removed: emphasis: workout primaryMuscle englishName ] ] ].
155 Added: emphasis: workout primaryMuscleGroup englishName ] ] ].
156 156 html accordionCollapse
157 157 id: 'collapse' , i asString;
158 158 class: 'collapse';