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
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-03 01:35 +0000
1from decimal import Decimal
2from typing import Optional
4from sqlalchemy import Integer, String
5from sqlalchemy.types import DECIMAL
6from sqlalchemy.orm import Mapped, mapped_column
8from postrfp.model.meta import Base
11class QuestionScoreComponent(Base):
12 """
13 SQLAlchemy model for the question_score_components view.
14 Provides all scoring components for questions.
15 """
17 __tablename__ = "question_score_components"
18 __table_args__ = {"info": {"is_view": True}}
20 # Explicitly disable the default 'id' column
21 __mapper_args__ = {"primary_key": ["question_id", "issue_id"]}
23 id = None # type: ignore
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)
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)
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")
41 # Ensure all values are Decimal with 4 decimal places
42 raw_score = Decimal(str(self.raw_score)).quantize(Decimal("0.0001"))
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