Coverage for backend/django/Economics/studies/api_mutations.py: 70%

34 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2026-06-23 21:51 +0000

1from rest_framework.exceptions import ValidationError 

2 

3from Economics.costing.capital.generated_lines import _sync_generated_capital_lines 

4from Economics.results.services.lifecycle.runs import mark_result_runs_stale_for_study 

5from Economics.studies.models import EconomicsStudy 

6 

7 

8def _bad_request_from_error(error: Exception) -> ValidationError: 

9 return ValidationError({"detail": str(error)}) 

10 

11 

12def _mark_study_stale(study: EconomicsStudy, *, reason: str, requires_solve: bool = False) -> None: 

13 mark_result_runs_stale_for_study(study=study, reason=reason, requires_solve=requires_solve) 

14 

15 

16class StudyMutationMixin: 

17 """Mark existing presentation runs stale after mutating study-owned rows.""" 

18 

19 stale_reason = "economics_api_saved" 

20 sync_generated_capital_lines = False 

21 

22 def _study_for_instance(self, instance) -> EconomicsStudy | None: 

23 return getattr(instance, "study", None) 

24 

25 def perform_create(self, serializer): 

26 instance = serializer.save() 

27 study = self._study_for_instance(instance) 

28 if study is not None: 28 ↛ exitline 28 didn't return from function 'perform_create' because the condition on line 28 was always true

29 if self.sync_generated_capital_lines: 29 ↛ 30line 29 didn't jump to line 30 because the condition on line 29 was never true

30 _sync_generated_capital_lines(study) 

31 _mark_study_stale(study, reason=self.stale_reason) 

32 

33 def perform_update(self, serializer): 

34 instance = serializer.save() 

35 study = self._study_for_instance(instance) 

36 if study is not None: 36 ↛ exitline 36 didn't return from function 'perform_update' because the condition on line 36 was always true

37 if self.sync_generated_capital_lines: 

38 _sync_generated_capital_lines(study) 

39 _mark_study_stale(study, reason=self.stale_reason) 

40 

41 def perform_destroy(self, instance): 

42 study = self._study_for_instance(instance) 

43 super().perform_destroy(instance) 

44 if study is not None: 

45 if self.sync_generated_capital_lines: 

46 _sync_generated_capital_lines(study) 

47 _mark_study_stale(study, reason=self.stale_reason)