Coverage for postrfp / model / questionnaire / score_views.py: 92%

36 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-12-03 01:35 +0000

1from decimal import Decimal 

2from typing import Optional 

3 

4from sqlalchemy import Integer, String 

5from sqlalchemy.types import DECIMAL 

6from sqlalchemy.orm import Mapped, mapped_column 

7 

8from postrfp.model.meta import Base 

9 

10 

11class QuestionScoreComponent(Base): 

12 """ 

13 SQLAlchemy model for the question_score_components view. 

14 Provides all scoring components for questions. 

15 """ 

16 

17 __tablename__ = "question_score_components" 

18 __table_args__ = {"info": {"is_view": True}} 

19 

20 # Explicitly disable the default 'id' column 

21 __mapper_args__ = {"primary_key": ["question_id", "issue_id"]} 

22 

23 id = None # type: ignore 

24 

25 # Composite primary key since this is a view 

26 question_id: Mapped[int] = mapped_column(Integer, primary_key=True) 

27 issue_id: Mapped[int] = mapped_column(Integer, primary_key=True) 

28 

29 section_id: Mapped[int] = mapped_column(Integer, nullable=False) 

30 scoreset_id: Mapped[Optional[str]] = mapped_column(String(50)) 

31 raw_score: Mapped[Optional[Decimal]] = mapped_column(DECIMAL(10, 4)) 

32 direct_weight: Mapped[Optional[Decimal]] = mapped_column(DECIMAL(15, 4)) 

33 absolute_weight: Mapped[Optional[Decimal]] = mapped_column(DECIMAL(15, 4)) 

34 weighting_set_id: Mapped[Optional[int]] = mapped_column(Integer) 

35 

36 def get_calculated_score(self, scoring_model: str) -> Decimal: 

37 """Calculate score based on scoring model with consistent precision.""" 

38 if not self.raw_score: 

39 return Decimal("0.0000") 

40 

41 # Ensure all values are Decimal with 4 decimal places 

42 raw_score = Decimal(str(self.raw_score)).quantize(Decimal("0.0001")) 

43 

44 if scoring_model == "Unweighted": 

45 return raw_score 

46 elif scoring_model == "Direct": 

47 if self.direct_weight is not None: 

48 weight = Decimal(str(self.direct_weight)).quantize(Decimal("0.0001")) 

49 return (raw_score * weight).quantize(Decimal("0.0001")) 

50 else: 

51 return raw_score 

52 elif scoring_model == "Absolute": 

53 if self.absolute_weight is not None: 

54 weight = Decimal(str(self.absolute_weight)).quantize(Decimal("0.0001")) 

55 return (raw_score * weight).quantize(Decimal("0.0001")) 

56 else: 

57 return raw_score 

58 else: 

59 return raw_score