Codebase hardening: 102 fixes across 35+ files
Deep audit identified 106 findings; 102 fixed, 4 deferred. Covers 8 areas: - Settings & deploy: env-gated DEBUG/SECRET_KEY, HTTPS headers, gunicorn, celery worker - Auth (registered_user): password write_only, request.data fixes, transaction safety, proper HTTP status codes - Workout app: IDOR protection, get_object_or_404, prefetch_related N+1 fixes, transaction.atomic - Video/scripts: path traversal sanitization, HLS trigger guard, auth on cache wipe - Models (exercise/equipment/muscle/superset): null-safe __str__, stable IDs, prefetch support - Generator views: helper for registered_user lookup, logger.exception, bulk_update, transaction wrapping - Generator core (rules/selector/generator): push-pull ratio, type affinity normalization, modality checks, side-pair exact match, word-boundary regex, equipment cache clearing - Generator services (plan_builder/analyzer/normalizer): transaction.atomic, muscle cache, bulk_update, glutes classification fix Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -34,11 +34,13 @@ class CompleteWorkoutSerializer(serializers.ModelSerializer):
|
||||
difficulty=validated_data['difficulty'],
|
||||
total_time=validated_data['total_time'],
|
||||
workout_start_time=validated_data['workout_start_time'],
|
||||
notes=validated_data['notes']
|
||||
# Fix #5: KeyError 'notes' - use .get() with default
|
||||
notes=validated_data.get('notes', '')
|
||||
)
|
||||
if "health_kit_workout_uuid" in validated_data:
|
||||
completed_workout.workout_uuid = validated_data['health_kit_workout_uuid']
|
||||
completed_workout.save()
|
||||
# Fix #6: wrong attribute name - model field is health_kit_workout_uuid
|
||||
completed_workout.health_kit_workout_uuid = validated_data['health_kit_workout_uuid']
|
||||
completed_workout.save()
|
||||
return completed_workout
|
||||
|
||||
class WorkoutSerializer(serializers.ModelSerializer):
|
||||
@@ -53,25 +55,48 @@ class WorkoutSerializer(serializers.ModelSerializer):
|
||||
fields = '__all__'
|
||||
# depth = 1
|
||||
|
||||
def get_muscles(self, obj):
|
||||
def get_muscles(self, obj):
|
||||
# Fix #16: Use prefetched data when available, fall back to query
|
||||
if hasattr(obj, '_prefetched_objects_cache') and 'superset_set' in obj._prefetched_objects_cache:
|
||||
exercise_ids = []
|
||||
for superset in obj.superset_set.all():
|
||||
for se in superset.supersetexercise_set.all():
|
||||
exercise_ids.append(se.exercise_id)
|
||||
if not exercise_ids:
|
||||
return []
|
||||
muscles_names = ExerciseMuscle.objects.filter(exercise__id__in=exercise_ids).values_list('muscle__name', flat=True)
|
||||
return list(set(muscles_names))
|
||||
superset_ids = Superset.objects.filter(workout=obj).values_list('id')
|
||||
exercise_ids = SupersetExercise.objects.filter(superset__id__in=superset_ids).values_list('exercise__id')
|
||||
muscles_names = ExerciseMuscle.objects.filter(exercise__id__in=exercise_ids).values_list('muscle__name', flat=True)
|
||||
return list(set(muscles_names))
|
||||
|
||||
# muscles_names = ExerciseMuscle.objects.filter(exercise__id__in=exercises).values_list('muscle__name', flat=True)
|
||||
# return list(set(muscles_names))
|
||||
|
||||
def get_equipment(self, obj):
|
||||
|
||||
def get_equipment(self, obj):
|
||||
# Fix #16: Use prefetched data when available, fall back to query
|
||||
if hasattr(obj, '_prefetched_objects_cache') and 'superset_set' in obj._prefetched_objects_cache:
|
||||
exercise_ids = []
|
||||
for superset in obj.superset_set.all():
|
||||
for se in superset.supersetexercise_set.all():
|
||||
exercise_ids.append(se.exercise_id)
|
||||
if not exercise_ids:
|
||||
return []
|
||||
equipment_names = WorkoutEquipment.objects.filter(exercise__id__in=exercise_ids).values_list('equipment__name', flat=True)
|
||||
return list(set(equipment_names))
|
||||
superset_ids = Superset.objects.filter(workout=obj).values_list('id')
|
||||
exercise_ids = SupersetExercise.objects.filter(superset__id__in=superset_ids).values_list('exercise__id')
|
||||
exercise_ids = SupersetExercise.objects.filter(superset__id__in=superset_ids).values_list('exercise__id')
|
||||
equipment_names = WorkoutEquipment.objects.filter(exercise__id__in=exercise_ids).values_list('equipment__name', flat=True)
|
||||
return list(set(equipment_names))
|
||||
|
||||
def get_exercise_count(self, obj):
|
||||
|
||||
def get_exercise_count(self, obj):
|
||||
# Fix #16: Use prefetched data when available, fall back to query
|
||||
returnValue = 0
|
||||
if hasattr(obj, '_prefetched_objects_cache') and 'superset_set' in obj._prefetched_objects_cache:
|
||||
for superset in obj.superset_set.all():
|
||||
exercise_count = len(superset.supersetexercise_set.all())
|
||||
returnValue += (superset.rounds * exercise_count)
|
||||
return returnValue
|
||||
supersets = Superset.objects.filter(workout=obj)
|
||||
for superset in supersets:
|
||||
for superset in supersets:
|
||||
exercise_count = SupersetExercise.objects.filter(superset=superset).count()
|
||||
returnValue += (superset.rounds * exercise_count)
|
||||
return returnValue
|
||||
@@ -106,8 +131,7 @@ class WorkoutDetailSerializer(serializers.ModelSerializer):
|
||||
return data
|
||||
|
||||
def get_registered_user(self, obj):
|
||||
objs = RegisteredUser.objects.get(pk=obj.registered_user.pk)
|
||||
data = GetRegisteredUserSerializer(objs, many=False).data
|
||||
data = GetRegisteredUserSerializer(obj.registered_user, many=False).data
|
||||
return data
|
||||
|
||||
class GetCompleteWorkoutSerializer(serializers.ModelSerializer):
|
||||
@@ -142,5 +166,5 @@ class POSTPlannedWorkoutSerializer(serializers.ModelSerializer):
|
||||
workout=validated_data['workout'],
|
||||
on_date=validated_data['on_date']
|
||||
)
|
||||
planned_workout.save()
|
||||
# Fix #18: removed redundant save() right after create()
|
||||
return planned_workout
|
||||
Reference in New Issue
Block a user