Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import re 

2import logging 

3 

4from django.contrib.auth.models import User 

5from django.db import models 

6from django.contrib.postgres.fields import JSONField 

7from django.core.exceptions import ValidationError 

8from django.dispatch import receiver 

9 

10from project.models import Project 

11from .utils import parse_xml, delete_file 

12from .extractor import extract_data 

13from .validators import validate_file_extension 

14 

15 

16logger = logging.getLogger(__name__) 

17 

18 

19class Gender(): 

20 MALE = 'male' 

21 FEMALE = 'female' 

22 CHOICES = ( 

23 (MALE, 'Male'), 

24 (FEMALE, 'Female'), 

25 ) 

26 

27 

28class Report(models.Model): 

29 name = models.CharField(max_length=255) 

30 project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='reports') 

31 date = models.DateField(default=None, null=True) 

32 data = JSONField(default=None, blank=True, null=True) 

33 file = models.FileField(upload_to='reports/', validators=[validate_file_extension]) 

34 

35 class Meta: 

36 verbose_name = 'ADP Management Report' 

37 verbose_name_plural = 'ADP Management Reports' 

38 ordering = ('-date',) 

39 

40 @staticmethod 

41 def extract_from_file(file): 

42 try: 

43 return extract_data( 

44 parse_xml( 

45 file.open().read() 

46 ) 

47 ) 

48 except (IndexError, ValueError, KeyError): 

49 logger.exception( 

50 '{0} XML Extraction Failed: "{1}" {0}'.format('*' * 22, file), 

51 exc_info=True, 

52 ) 

53 raise ValidationError(u'Unsupported xml file') 

54 

55 def __str__(self): 

56 return f"{self.name}" 

57 

58 

59class ProjectSummaryModel(models.Model): 

60 project = models.ForeignKey(Project, on_delete=models.CASCADE) 

61 date = models.DateField() 

62 

63 def __str__(self): 

64 return f'{self.date} <{self.project}>' 

65 

66 class Meta: 

67 abstract = True 

68 ordering = ('-date',) 

69 unique_together = ('project', 'date',) 

70 

71 

72class ProjectSOI(ProjectSummaryModel): 

73 total_closed = models.IntegerField(default=0) 

74 closed_on = models.IntegerField(default=0) 

75 

76 @property 

77 def rating(self): 

78 if self.total_closed and self.closed_on: 

79 return (self.closed_on / self.total_closed) * 100 

80 

81 

82class RegisterChildByAgeAndGender(ProjectSummaryModel): 

83 age = models.IntegerField() 

84 gender = models.CharField(max_length=10, choices=Gender.CHOICES) 

85 count = models.IntegerField(default=0) 

86 

87 class Meta: 

88 unique_together = ('project', 'date', 'age', 'gender',) 

89 

90 

91class PresenceAndParticipation(ProjectSummaryModel): 

92 total_rc_temporarily_away = models.IntegerField(default=0) 

93 total_no_of_rc_records_dropped_during_the_month = models.IntegerField(default=0) 

94 

95 

96class ChildFamilyParticipation(ProjectSummaryModel): 

97 CHILD_PARTICIPATION = 'child_participation' 

98 FAMILY_PARTICIPATION = 'family_participation' 

99 CHILD_SUPPORT = 'child_support' 

100 FAMILY_SUPPORT = 'family_support' 

101 BENEFIT_SUPPORT = 'benefit_support' 

102 

103 TYPE_CHOICES = ( 

104 # Participation 

105 (CHILD_PARTICIPATION, 'Child Participation'), 

106 (FAMILY_PARTICIPATION, 'Family Participation'), 

107 # Support 

108 (CHILD_SUPPORT, 'Child Support'), 

109 (FAMILY_SUPPORT, 'Family Support'), 

110 (BENEFIT_SUPPORT, 'Benefit Support'), 

111 ) 

112 

113 type = models.CharField(max_length=30, choices=TYPE_CHOICES) 

114 gender = models.CharField(max_length=10, choices=Gender.CHOICES) 

115 participation = models.IntegerField(default=0) 

116 count = models.IntegerField(default=0) 

117 

118 class Meta: 

119 unique_together = ('project', 'date', 'type', 'participation', 'gender',) 

120 

121 

122class LanguagePeopleGroupDisability(ProjectSummaryModel): 

123 language = models.CharField(max_length=255) 

124 people_group = models.CharField(max_length=255) 

125 disability = models.CharField(max_length=255, null=True, blank=True) 

126 count = models.IntegerField(default=0) 

127 

128 class Meta: 

129 unique_together = ('project', 'date', 'language', 'people_group', 'disability',) 

130 

131 

132class SupportPariticipationDetail(ProjectSummaryModel): 

133 type = models.CharField(max_length=255) 

134 comment = models.CharField(max_length=255) 

135 count = models.IntegerField(default=0) 

136 

137 class Meta: 

138 unique_together = ('project', 'date', 'type', 'comment',) 

139 

140 

141class MostVulnerableChildrenIndicator(ProjectSummaryModel): 

142 mvc_count = models.IntegerField(default=0) 

143 rc_not_vc_count = models.IntegerField(default=0) 

144 rc_count = models.IntegerField(default=0) 

145 

146 class Meta: 

147 unique_together = ('project', 'date',) 

148 

149 

150class MostVulnerableChildrenVulnerabilityMarker(ProjectSummaryModel): 

151 FIELDS = ( 

152 'Child_Labor', 'Sexual_Abuse', 'Physical_Abuse', 'Child_Trafficking', 'Child_Marriage', 'Early_Sexual_Debut', 

153 'Substance_Use', 'Abusive_Exploitative_Relationships', 

154 

155 'Child_Malnourished', 'Child_in_Household_Below_Poverty_Threshold', 'No_Access_to_Basic_Services_and_Facilities', 

156 'Child_is_Orphan__Abandoned__Neglected', 'Child_Not_in_School', 'Child_Living_in_Public_Property__Slums', 

157 'Child_Head_of_Household', 'Child_Living_or_Working_on_the_Street', 'Child_Caregiver', 

158 'Child_living_with_step_parents__divorced_parents__single_parent', 'Extreme_Deprivation', 

159 

160 'Child_Living_in_Disaster_Prone_Area', 'Child_Living_in_Areas_With_Conflict', 'Child_Living_in_Former_War_Zones', 

161 'Child_Affected_by_Epidemic', 'Catastrophe_Disaster', 

162 

163 'Child_with_Disability', 'Child_Belongs_to_a_Marginalized_Group', 'Child_Refugee__Children_of_Refugees__Migrant', 

164 'Child_Delinquent', 'Child_Whose_Parents_are_Imprisoned', 'Child_Without_Birth_Registration', 

165 'Child_Living_in_Isolation', 'Serious_Discimination' 

166 ) 

167 data = JSONField(default=dict) 

168 

169 class Meta: 

170 unique_together = ('project', 'date',) 

171 

172 @classmethod 

173 def get_data_fields(cls): 

174 return [ 

175 (field, re.sub(r'(_)\1+', r'_', f'total_{field}'.lower())) for field in cls.FIELDS 

176 ] 

177 

178 

179@receiver(models.signals.post_delete, sender=Report) 

180def delete_report_file(sender, instance, *args, **kwargs): 

181 """ Deletes report file on `post_delete` """ 

182 if instance.file: 

183 delete_file(instance.file.path) 

184 

185 

186class BulkImportReport(models.Model): 

187 """ 

188 For bulk import logging 

189 """ 

190 SUCCESS = 'success' 

191 FAILED = 'failed' 

192 STATUS_CHOICES = ( 

193 (SUCCESS, 'Success'), 

194 (FAILED, 'Failed'), 

195 ) 

196 

197 report_type = models.CharField(max_length=100) 

198 file = models.FileField(upload_to='bulk-import-report/') # Provide by user 

199 generated_on = models.DateField(default=None, null=True) # Provide by user 

200 created_at = models.DateTimeField(auto_now_add=True) 

201 created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) 

202 status = models.CharField(max_length=10, null=True, choices=STATUS_CHOICES,) 

203 log_message = models.TextField(null=True) 

204 

205 def __str__(self): 

206 return f'{self.report_type}:{self.generated_on}'