Coverage for backend / app / job_rating / models.py: 100%

53 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-17 21:34 +0000

1"""Database models for job ratings and their service logs.""" 

2 

3from sqlalchemy import ( 

4 Column, 

5 Integer, 

6 String, 

7 ForeignKey, 

8 Boolean, 

9) 

10from sqlalchemy.dialects.postgresql import ARRAY as PG_ARRAY 

11from sqlalchemy.orm import relationship 

12from sqlalchemy.sql import expression 

13 

14from app.base_models import Owned, CommonBase 

15from app.database import Base 

16from app.service_runner.models import ServiceLog 

17 

18 

19class AiSystemPrompt(CommonBase, Base): 

20 """Represents AI system prompts for job ratings. 

21 

22 Attributes: 

23 ----------- 

24 - `prompt` (str): The AI system prompt. 

25 

26 Relationships 

27 ------------- 

28 - `job_ratings`: JobRating that used the system prompt to rate the job""" 

29 

30 prompt = Column(String, nullable=False) 

31 

32 # Relationships 

33 job_ratings = relationship("JobRating", back_populates="system_prompt") 

34 

35 

36class AiJobPromptTemplate(CommonBase, Base): 

37 """Represents AI job prompt templates for job rating 

38 

39 Attributes: 

40 ----------- 

41 - `prompt` (str): The AI job prompt template. 

42 

43 Relationships 

44 ------------- 

45 - `job_ratings`: JobRating that used the system prompt to rate the job""" 

46 

47 prompt = Column(String, nullable=False) 

48 

49 # Relationships 

50 job_ratings = relationship("JobRating", back_populates="job_prompt_template") 

51 

52 

53class JobRating(Owned, Base): 

54 """Represents user ratings for jobs. 

55 

56 Attributes: 

57 ----------- 

58 - `overall_score` (int): Overall score for the job. 

59 - `technical_score` (int, optional): Technical score for the job. 

60 - `experience_score` (int, optional): Experience score for the job. 

61 - `educational_score` (int, optional): Educational score for the job. 

62 - `interest_score` (int, optional): Interest score for the job. 

63 - `feedback` (str, optional): Additional feedback or comments about the job rating. 

64 - `is_skipped` (bool, optional): Indicates whether the rating process was skipped. 

65 - `skip_reason` (str, optional): Reason for skipping the rating process. 

66 - `is_success` (bool, optional): Indicates whether the rating process was successful. 

67 - `error` (str, optional): Error message if the rating process failed. 

68 - `job_prompt` (str, optional): Job prompt used for the rating. 

69 - `llm_model` (str): LLM model used for the rating. 

70 - `notes` (List[str], optional): Additional notes or comments about the rating. 

71 

72 Foreign keys: 

73 ------------- 

74 - `scraped_job_id` (int): Identifier for the job being rated. 

75 - `user_qualification_id` (int): Identifier for the user qualification entry used to rate the job 

76 - `system_prompt_id` (int, optional): Identifier for the AI system prompt used to rate the job. 

77 - `job_prompt_template_id` (int, optional): Identifier for the AI job prompt template used to rate the job. 

78 

79 Relationships: 

80 -------------- 

81 - `scraped_job` (ScrapedJob): ScrapedJob object related to the rating. 

82 - `use_qualification` (UserQualification): UserQualification object related to the rating. 

83 - `system_prompt` (AiSystemPrompt, optional): AiSystemPrompt object related to the rating. 

84 - `job_prompt_template` (AiJobPromptTemplate, optional): AiJobPromptTemplate object related to the rating.""" 

85 

86 overall_score = Column(Integer, nullable=True) 

87 technical_score = Column(Integer, nullable=True) 

88 experience_score = Column(Integer, nullable=True) 

89 educational_score = Column(Integer, nullable=True) 

90 interest_score = Column(Integer, nullable=True) 

91 feedback = Column(String, nullable=True) 

92 is_skipped = Column(Boolean, nullable=False, server_default=expression.false()) 

93 skip_reason = Column(String, nullable=True) 

94 is_success = Column(Boolean, nullable=True) 

95 error = Column(String, nullable=True) 

96 job_prompt = Column(String, nullable=True) 

97 llm_model = Column(String, nullable=False) 

98 notes = Column(PG_ARRAY(String), server_default="{}", nullable=False) 

99 

100 # Foreign keys 

101 scraped_job_id = Column(Integer, ForeignKey("scraped_job.id", ondelete="CASCADE"), nullable=False) 

102 user_qualification_id = Column(Integer, ForeignKey("user_qualification.id", ondelete="CASCADE"), nullable=False) 

103 system_prompt_id = Column(Integer, ForeignKey("ai_system_prompt.id", ondelete="SET NULL"), nullable=True) 

104 job_prompt_template_id = Column( 

105 Integer, ForeignKey("ai_job_prompt_template.id", ondelete="SET NULL"), nullable=True 

106 ) 

107 

108 # Relationships 

109 scraped_job = relationship("ScrapedJob", back_populates="job_rating") 

110 user_qualification = relationship("UserQualification", back_populates="job_ratings") 

111 system_prompt = relationship("AiSystemPrompt", back_populates="job_ratings") 

112 job_prompt_template = relationship("AiJobPromptTemplate", back_populates="job_ratings") 

113 

114 def __init__(self, **kwargs) -> None: 

115 """Initialise array fields with empty lists if not provided""" 

116 

117 kwargs.setdefault("notes", []) 

118 super().__init__(**kwargs) 

119 

120 

121class JobRatingServiceLog(ServiceLog, CommonBase, Base): 

122 """Represents service logs for job ratings. 

123 

124 Attributes: 

125 ----------- 

126 - `user_found_ids` (List[int]): List of user IDs that were found during the processing. 

127 - `user_processed_ids` (List[int]): List of user IDs that were processed. 

128 - `job_found_ids` (List[int]): List of job IDs that were found during the rating process. 

129 - `job_succeeded_ids` (List[int]): List of job IDs that were successfully rated. 

130 - `job_skipped_ids` (List[int]): List of job IDs that were skipped during the rating process. 

131 - `job_failed_ids` (List[int]): List of job IDs that failed to be rated.""" 

132 

133 user_found_ids = Column(PG_ARRAY(Integer), server_default="{}", nullable=False) 

134 user_processed_ids = Column(PG_ARRAY(Integer), server_default="{}", nullable=False) 

135 job_found_ids = Column(PG_ARRAY(Integer), server_default="{}", nullable=False) 

136 job_succeeded_ids = Column(PG_ARRAY(Integer), server_default="{}", nullable=False) 

137 job_skipped_ids = Column(PG_ARRAY(Integer), server_default="{}", nullable=False) 

138 job_failed_ids = Column(PG_ARRAY(Integer), server_default="{}", nullable=False) 

139 

140 def __init__(self, **kwargs) -> None: 

141 """Initialise array fields with empty lists if not provided""" 

142 

143 kwargs.setdefault("user_found_ids", []) 

144 kwargs.setdefault("user_processed_ids", []) 

145 kwargs.setdefault("job_found_ids", []) 

146 kwargs.setdefault("job_succeeded_ids", []) 

147 kwargs.setdefault("job_skipped_ids", []) 

148 kwargs.setdefault("job_failed_ids", []) 

149 super().__init__(**kwargs)