Coverage for apps/report/models.py : 88%
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
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
10from project.models import Project
11from .utils import parse_xml, delete_file
12from .extractor import extract_data
13from .validators import validate_file_extension
16logger = logging.getLogger(__name__)
19class Gender():
20 MALE = 'male'
21 FEMALE = 'female'
22 CHOICES = (
23 (MALE, 'Male'),
24 (FEMALE, 'Female'),
25 )
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])
35 class Meta:
36 verbose_name = 'ADP Management Report'
37 verbose_name_plural = 'ADP Management Reports'
38 ordering = ('-date',)
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')
55 def __str__(self):
56 return f"{self.name}"
59class ProjectSummaryModel(models.Model):
60 project = models.ForeignKey(Project, on_delete=models.CASCADE)
61 date = models.DateField()
63 def __str__(self):
64 return f'{self.date} <{self.project}>'
66 class Meta:
67 abstract = True
68 ordering = ('-date',)
69 unique_together = ('project', 'date',)
72class ProjectSOI(ProjectSummaryModel):
73 total_closed = models.IntegerField(default=0)
74 closed_on = models.IntegerField(default=0)
76 @property
77 def rating(self):
78 if self.total_closed and self.closed_on:
79 return (self.closed_on / self.total_closed) * 100
82class RegisterChildByAgeAndGender(ProjectSummaryModel):
83 age = models.IntegerField()
84 gender = models.CharField(max_length=10, choices=Gender.CHOICES)
85 count = models.IntegerField(default=0)
87 class Meta:
88 unique_together = ('project', 'date', 'age', 'gender',)
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)
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'
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 )
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)
118 class Meta:
119 unique_together = ('project', 'date', 'type', 'participation', 'gender',)
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)
128 class Meta:
129 unique_together = ('project', 'date', 'language', 'people_group', 'disability',)
132class SupportPariticipationDetail(ProjectSummaryModel):
133 type = models.CharField(max_length=255)
134 comment = models.CharField(max_length=255)
135 count = models.IntegerField(default=0)
137 class Meta:
138 unique_together = ('project', 'date', 'type', 'comment',)
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)
146 class Meta:
147 unique_together = ('project', 'date',)
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',
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',
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',
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)
169 class Meta:
170 unique_together = ('project', 'date',)
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 ]
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)
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 )
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)
205 def __str__(self):
206 return f'{self.report_type}:{self.generated_on}'