예제 #1
0
class Infrastructure(SequenceMixin, AbstractBase):
    """
    Health specilities.
    """
    name = models.CharField(max_length=255, unique=True)
    description = models.TextField(null=True, blank=True)
    abbreviation = models.CharField(
        max_length=50,
        null=True,
        blank=True,
        help_text='A short form for the infrastructure')
    category = models.ForeignKey(
        InfrastructureCategory,
        on_delete=models.PROTECT,
        help_text="The classification that the specialities lies in.",
        related_name='category_infrastructure')
    code = SequenceField(unique=True, editable=False)

    def save(self, *args, **kwargs):
        if not self.code:
            self.code = self.generate_next_code_sequence()
        super(Infrastructure, self).save(*args, **kwargs)

    @property
    def category_name(self):
        return self.category.name

    def __str__(self):
        return self.name

    class Meta(AbstractBase.Meta):
        verbose_name_plural = 'infrastructure'
예제 #2
0
class CommunityHealthUnit(SequenceMixin, AbstractBase):
    """
    This is a health service delivery structure within a defined geographical
    area covering a population of approximately 5,000 people.

    Each unit is assigned 2 Community Health Extension Workers (CHEWs) and
    community health volunteers who offer promotive, preventative and basic
    curative health services
    """
    name = models.CharField(max_length=100)
    code = SequenceField(unique=True)
    facility = models.ForeignKey(
        Facility,
        help_text='The facility on which the health unit is tied to.')
    status = models.ForeignKey(Status)
    community = models.ForeignKey(
        Community,
        help_text='Community area within which the health unit is located')
    households_monitored = models.PositiveIntegerField(default=0)
    date_established = models.DateField(default=timezone.now)
    contacts = models.ManyToManyField(Contact,
                                      through=CommunityHealthUnitContact)

    def __unicode__(self):
        return self.name

    def save(self, *args, **kwargs):
        if not self.code:
            self.code = self.generate_next_code_sequence()
        super(CommunityHealthUnit, self).save(*args, **kwargs)
예제 #3
0
class Service(SequenceMixin, AbstractBase):
    """
    A health service.
    """
    name = models.CharField(max_length=255, unique=True)
    description = models.TextField(null=True, blank=True)
    abbreviation = models.CharField(
        max_length=50, null=True, blank=True,
        help_text='A short form for the service e.g FANC for Focused '
        'Antenatal Care')
    category = models.ForeignKey(
        ServiceCategory,
        help_text="The classification that the service lies in.")

    code = SequenceField(unique=True, editable=False)
    options = models.ManyToManyField(Option, through='ServiceOption')

    def save(self, *args, **kwargs):
        if not self.code:
            self.code = self.generate_next_code_sequence()
        super(Service, self).save(*args, **kwargs)

    def __unicode__(self):
        return self.name

    class Meta(AbstractBase.Meta):
        verbose_name_plural = 'services'
예제 #4
0
class Community(SequenceMixin, AbstractBase):
    """
    A certain area within a ward.
    """
    name = models.CharField(max_length=100)
    code = SequenceField(unique=True)
    ward = models.ForeignKey(Ward)

    def save(self, *args, **kwargs):
        if not self.code:
            self.code = self.generate_next_code_sequence()
        super(Community, self).save(*args, **kwargs)

    class Meta(object):
        verbose_name_plural = 'communities'
        unique_together = (
            'name',
            'ward',
        )
예제 #5
0
class AdminOffice(SequenceMixin, AbstractBase):
    """
    The administration offices from the sub-county level to the national level.
    If the county and sub-county are null then the offices are
    assumed to be at the national level.
    """
    code = SequenceField(
        unique=True,
        help_text="A unique number to identify the admin office.",
        editable=False)
    old_code = models.IntegerField(null=True, blank=True)
    county = models.ForeignKey(
        County,
        null=True,
        blank=True,
        on_delete=models.PROTECT,
    )
    sub_county = models.ForeignKey(
        SubCounty,
        null=True,
        blank=True,
        on_delete=models.PROTECT,
    )
    constituency = models.ForeignKey(
        Constituency,
        null=True,
        blank=True,
        on_delete=models.PROTECT,
    )
    coordinates = gis_models.PointField(null=True, blank=True)
    name = models.CharField(max_length=100)
    email = models.EmailField(null=True, blank=True)
    phone_number = models.CharField(max_length=100, null=True, blank=True)
    is_national = models.BooleanField(default=False)
    closed = models.BooleanField(default=False)

    def save(self, *args, **kwargs):
        if not self.code:
            self.code = self.generate_next_code_sequence()
        super(AdminOffice, self).save(*args, **kwargs)
예제 #6
0
class Owner(AbstractBase, SequenceMixin):
    """
    Entity that has exclusive legal rights to the facility.

    For the master facility list, ownership especially for the faith-based
    facilities is broadened to also include the body that coordinates
    service delivery and health programmes. Therefore, the Christian Health
    Association of Kenya (CHAK), Kenya Episcopal Conference (KEC), or
    Supreme Council of Kenya Muslim (SUPKEM) will be termed as owners though
    in fact the facilities under them are owned by the individual churches,
    mosques, or communities affiliated with the faith.
    """
    name = models.CharField(
        max_length=100, unique=True,
        help_text="The name of owner e.g Ministry of Health.")
    description = models.TextField(
        null=True, blank=True, help_text="A brief summary of the owner.")
    code = SequenceField(
        unique=True,
        help_text="A unique number to identify the owner."
        "Could be up to 7 characteres long.", editable=False)
    abbreviation = models.CharField(
        max_length=30, null=True, blank=True,
        help_text="Short form of the name of the owner e.g Ministry of health"
        " could be shortened as MOH")
    owner_type = models.ForeignKey(
        OwnerType,
        help_text="The classification of the owner e.g INDIVIDUAL",
        on_delete=models.PROTECT)

    def save(self, *args, **kwargs):
        if not self.code:
            self.code = self.generate_next_code_sequence()
        super(Owner, self).save(*args, **kwargs)

    def __unicode__(self):
        return self.name
예제 #7
0
class CommunityHealthUnit(SequenceMixin, AbstractBase):
    """
    This is a health service delivery structure within a defined geographical
    area covering a population of approximately 5,000 people.

    Each unit is assigned 2 Community Health Extension Workers (CHEWs) and
    community health volunteers who offer promotive, preventative and basic
    curative health services
    """
    name = models.CharField(max_length=100)
    code = SequenceField(unique=True)
    facility = models.ForeignKey(
        Facility,
        help_text='The facility on which the health unit is tied to.')
    status = models.ForeignKey(Status)
    households_monitored = models.PositiveIntegerField(
        help_text='The number of house holds a CHU is in-charge of')
    date_established = models.DateField(default=timezone.now)
    date_operational = models.DateField(null=True, blank=True)
    is_approved = models.BooleanField(default=False)
    approval_comment = models.TextField(null=True, blank=True)
    approval_date = models.DateTimeField(null=True, blank=True)
    location = models.CharField(max_length=255, null=True, blank=True)
    is_closed = models.BooleanField(default=False)
    closing_comment = models.TextField(null=True, blank=True)
    is_rejected = models.BooleanField(default=False)
    rejection_reason = models.TextField(null=True, blank=True)
    has_edits = models.BooleanField(
        default=False,
        help_text='Indicates that a community health unit has updates that are'
        ' pending approval')

    def __str__(self):
        return self.name

    @property
    def workers(self):
        from .serializers import CommunityHealthWorkerPostSerializer
        return CommunityHealthWorkerPostSerializer(self.health_unit_workers,
                                                   many=True).data

    def validate_facility_is_not_closed(self):
        if self.facility.closed:
            raise ValidationError({
                "facility": [
                    "A Community Unit cannot be attached to a closed "
                    "facility"
                ]
            })

    def validate_either_approved_or_rejected_and_not_both(self):
        error = {
            "approve/reject": [
                "A Community Unit cannot be approved and"
                " rejected at the same time "
            ]
        }
        values = [self.is_approved, self.is_rejected]
        if values.count(True) > 1:
            raise ValidationError(error)

    def validate_date_operation_is_less_than_date_established(self):
        if self.date_operational and self.date_established:
            if self.date_established > self.date_operational:
                raise ValidationError({
                    "date_operational": [
                        "Date operation cannot be greater than date "
                        "established"
                    ]
                })

    def validate_date_established_not_in_future(self):
        """
        Only the date operational needs to be validated.

        date_established should always be less then the date_operational.
        Thus is the date_operational is not in future the date_established
        is also not in future
        """

        today = datetime.datetime.now().date()

        if self.date_operational and self.date_operational > today:
            raise ValidationError({
                "date_operational":
                ["The date operational cannot be in the future"]
            })

    @property
    def contacts(self):
        return [{
            "id": con.id,
            "contact_id": con.contact.id,
            "contact": con.contact.contact,
            "contact_type": con.contact.contact_type.id,
            "contact_type_name": con.contact.contact_type.name
        } for con in CommunityHealthUnitContact.objects.filter(
            health_unit=self)]

    @property
    def json_features(self):
        return {
            "geometry": {
                "coordinates": [
                    self.facility.facility_coordinates_through.coordinates[0],
                    self.facility.facility_coordinates_through.coordinates[1]
                ]
            },
            "properties": {
                "ward": self.facility.ward.id,
                "constituency": self.facility.ward.constituency.id,
                "county": self.facility.ward.county.id
            }
        }

    def clean(self):
        super(CommunityHealthUnit, self).clean()
        self.validate_facility_is_not_closed()
        self.validate_either_approved_or_rejected_and_not_both()
        self.validate_date_operation_is_less_than_date_established()
        self.validate_date_established_not_in_future()

    @property
    def pending_updates(self):
        try:
            chu = ChuUpdateBuffer.objects.get(is_approved=False,
                                              is_rejected=False,
                                              health_unit=self)
            return chu.updates
        except ChuUpdateBuffer.DoesNotExist:
            return {}

    @property
    def latest_update(self):
        try:
            chu = ChuUpdateBuffer.objects.get(is_approved=False,
                                              is_rejected=False,
                                              health_unit=self)
            return chu
        except ChuUpdateBuffer.DoesNotExist:
            return None

    def save(self, *args, **kwargs):
        if not self.code:
            self.code = self.generate_next_code_sequence()
        super(CommunityHealthUnit, self).save(*args, **kwargs)

    @property
    def average_rating(self):
        return self.chu_ratings.aggregate(r=models.Avg('rating'))['r'] or 0

    @property
    def rating_count(self):
        return self.chu_ratings.count()

    class Meta(AbstractBase.Meta):
        unique_together = (
            'name',
            'facility',
        )
        permissions = (
            ("view_rejected_chus",
             "Can see the rejected community health units"),
            ("can_approve_chu",
             "Can approve or reject a Community Health Unit"),
        )
예제 #8
0
class Facility(SequenceMixin, AbstractBase):
    """
    A health institution in Kenya.

    The health institution considered as facilities include:
    Health Centres, Dispensaries, Hospitals etc.
    """
    name = models.CharField(
        max_length=100, unique=True,
        help_text='This is the official name of the facility')
    code = SequenceField(
        unique=True, editable=False,
        help_text='A sequential number allocated to each facility')
    abbreviation = models.CharField(
        max_length=30, null=True, blank=True,
        help_text='A short name for the facility.')
    description = models.TextField(
        null=True, blank=True,
        help_text="A brief summary of the Facility")
    location_desc = models.TextField(
        null=True, blank=True,
        help_text="This field allows a more detailed description of how to"
        "locate the facility e.g Joy medical clinic is in Jubilee Plaza"
        "7th Floor")
    number_of_beds = models.PositiveIntegerField(
        default=0,
        help_text="The number of beds that a facilty has. e.g 0")
    number_of_cots = models.PositiveIntegerField(
        default=0,
        help_text="The number of cots that a facility has e.g 0")
    open_whole_day = models.BooleanField(
        default=False,
        help_text="Is the facility open 24 hours a day?")
    open_whole_week = models.BooleanField(
        default=False,
        help_text="Is the facility open the entire week?")
    is_classified = models.BooleanField(
        default=False,
        help_text="Should the facility geo-codes be visible to the public?"
        "Certain facilities are kept 'off-the-map'")
    is_published = models.BooleanField(
        default=False,
        help_text="Should be True if the facility is to be seen on the "
        "public MFL site")
    facility_type = models.ForeignKey(
        FacilityType,
        help_text="This depends on who owns the facilty. For MOH facilities,"
        "type is the gazetted classification of the facilty."
        "For Non-MOH check under the respective owners.",
        on_delete=models.PROTECT)
    operation_status = models.ForeignKey(
        FacilityStatus, null=True, blank=True,
        help_text="Indicates whether the facility"
        "has been approved to operate, is operating, is temporarily"
        "non-operational, or is closed down")
    ward = models.ForeignKey(
        Ward,
        on_delete=models.PROTECT,
        help_text="County ward in which the facility is located")
    owner = models.ForeignKey(
        Owner, help_text="A link to the organization that owns the facility")
    officer_in_charge = models.ForeignKey(
        Officer, null=True, blank=True,
        help_text="The officer in charge of the facility")
    physical_address = models.ForeignKey(
        PhysicalAddress, null=True, blank=True,
        help_text="Postal and courier addressing for the facility")

    contacts = models.ManyToManyField(
        Contact, through=FacilityContact,
        help_text='Facility contacts - email, phone, fax, postal etc')
    parent = models.ForeignKey(
        'self', help_text='Indicates the umbrella facility of a facility',
        null=True, blank=True)
    attributes = models.TextField(null=True, blank=True)

    @property
    def current_regulatory_status(self):
        try:
            return self.regulatory_details.all()[0]
        except IndexError:
            return []

    def save(self, *args, **kwargs):
        if not self.code:
            self.code = self.generate_next_code_sequence()
        super(Facility, self).save(*args, **kwargs)

    @property
    def is_approved(self):
        approvals = FacilityApproval.objects.filter(facility=self).count()
        if approvals:
            return True
        else:
            False

    def __unicode__(self):
        return self.name

    class Meta(AbstractBase.Meta):
        verbose_name_plural = 'facilities'
예제 #9
0
파일: models.py 프로젝트: uonafya/mfl_api
class CommunityHealthUnit(SequenceMixin, AbstractBase):
    """
    This is a health service delivery structure within a defined geographical
    area covering a population of approximately 5,000 people.

    Each unit is assigned 2 Community Health Extension Workers (CHEWs) and
    community health volunteers who offer promotive, preventative and basic
    curative health services
    """
    name = models.CharField(max_length=100)
    code = SequenceField(unique=True,
                         editable=False,
                         help_text='A sequential number allocated to each chu',
                         null=True,
                         blank=True)
    facility = models.ForeignKey(
        Facility,
        help_text='The facility on which the health unit is tied to.')
    status = models.ForeignKey(Status, on_delete=models.PROTECT)
    households_monitored = models.PositiveIntegerField(
        default=0, help_text='The number of house holds a CHU is in-charge of')
    date_established = models.DateField(default=timezone.now)
    date_operational = models.DateField(null=True, blank=True)
    is_approved = models.NullBooleanField(
        blank=True,
        null=True,
        help_text='Determines if a chu has been approved')
    approval_comment = models.TextField(null=True, blank=True)
    approval_date = models.DateTimeField(null=True, blank=True)
    location = models.CharField(max_length=255, null=True, blank=True)
    is_closed = models.BooleanField(default=False)
    closing_comment = models.TextField(null=True, blank=True)
    is_rejected = models.BooleanField(default=False)
    rejection_reason = models.TextField(null=True, blank=True)
    has_edits = models.BooleanField(
        default=False,
        help_text='Indicates that a community health unit has updates that are'
        ' pending approval')
    number_of_chvs = models.PositiveIntegerField(
        default=0,
        help_text='Number of Community Health volunteers in the CHU')

    def __str__(self):
        return self.name

    @property
    def workers(self):
        from .serializers import CommunityHealthWorkerPostSerializer
        return CommunityHealthWorkerPostSerializer(self.health_unit_workers,
                                                   many=True).data

    def validate_facility_is_not_closed(self):
        if self.facility.closed:
            raise ValidationError({
                "facility": [
                    "A Community Unit cannot be attached to a closed "
                    "facility"
                ]
            })

    def validate_either_approved_or_rejected_and_not_both(self):
        error = {
            "approve/reject": [
                "A Community Unit cannot be approved and"
                " rejected at the same time "
            ]
        }
        values = [self.is_approved, self.is_rejected]
        if values.count(True) > 1:
            raise ValidationError(error)

    def validate_date_operation_is_less_than_date_established(self):
        if self.date_operational and self.date_established:
            if self.date_established > self.date_operational:
                raise ValidationError({
                    "date_operational": [
                        "Date operation cannot be greater than date "
                        "established"
                    ]
                })

    def validate_date_established_not_in_future(self):
        """
        Only the date operational needs to be validated.

        date_established should always be less then the date_operational.
        Thus is the date_operational is not in future the date_established
        is also not in future
        """

        today = datetime.datetime.now().date()

        if self.date_operational and self.date_operational > today:
            raise ValidationError({
                "date_operational":
                ["The date operational cannot be in the future"]
            })

    def validate_comment_required_on_rejection(self):
        """
        Comments will only be required on the rejection of a community health
        unit. The approvals will not require comments.

        """
        if self.is_rejected and not self.rejection_reason:
            raise ValidationError({
                "rejection_reason":
                ["Please provide the reason for rejecting the CHU"]
            })

    @property
    def contacts(self):
        return [{
            "id": con.id,
            "contact_id": con.contact.id,
            "contact": con.contact.contact,
            "contact_type": con.contact.contact_type.id,
            "contact_type_name": con.contact.contact_type.name
        } for con in CommunityHealthUnitContact.objects.filter(
            health_unit=self)]

    @property
    def json_features(self):
        return {
            "geometry": {
                "coordinates": [
                    self.facility.facility_coordinates_through.coordinates[0],
                    self.facility.facility_coordinates_through.coordinates[1]
                ]
            },
            "properties": {
                "ward": self.facility.ward.id,
                "constituency": self.facility.ward.constituency.id,
                "county": self.facility.ward.county.id
            }
        }

    def clean(self):
        super(CommunityHealthUnit, self).clean()
        self.validate_facility_is_not_closed()
        self.validate_either_approved_or_rejected_and_not_both()
        self.validate_date_operation_is_less_than_date_established()
        self.validate_date_established_not_in_future()
        self.validate_comment_required_on_rejection()

    @property
    def pending_updates(self):
        try:
            chu = ChuUpdateBuffer.objects.latest(is_approved=False,
                                                 is_rejected=False,
                                                 health_unit=self)
            return chu.updates
        except ChuUpdateBuffer.DoesNotExist:
            return {}

    @property
    def latest_update(self):
        try:
            chu = ChuUpdateBuffer.objects.latest(is_approved=False,
                                                 is_rejected=False,
                                                 health_unit=self)
            return chu
        except ChuUpdateBuffer.DoesNotExist:
            return None

    def save(self, *args, **kwargs):
        # new chus that have just been added but not approved yet
        if not self.code and not self.is_approved:
            super(CommunityHealthUnit, self).save(*args, **kwargs)
        # existing chus that were approved previously and have been updated
        if self.code and self.is_approved:
            if settings.PUSH_TO_DHIS:
                self.push_chu_to_dhis2()
            super(CommunityHealthUnit, self).save(*args, **kwargs)
        # chus that have just been approved but don't have a code yet
        # and have not been pushed to DHIS yet
        if self.is_approved and not self.code:
            self.code = self.generate_next_code_sequence()
            if settings.PUSH_TO_DHIS:
                self.push_chu_to_dhis2()
            super(CommunityHealthUnit, self).save(*args, **kwargs)

    @property
    def average_rating(self):
        return self.chu_ratings.aggregate(r=models.Avg('rating'))['r'] or 0

    @property
    def rating_count(self):
        return self.chu_ratings.count()

    def push_chu_to_dhis2(self):
        from facilities.models.facility_models import DhisAuth
        import requests
        dhisauth = DhisAuth()
        dhisauth.get_oauth2_token()
        facility_dhis_id = self.get_facility_dhis2_parent_id()
        unit_uuid_status = dhisauth.get_org_unit_id(self.code)
        unit_uuid = unit_uuid_status[0]
        new_chu_payload = {
            "id": unit_uuid,
            "code": str(self.code),
            "name": str(self.name),
            "shortName": str(self.name),
            "displayName": str(self.name),
            "parent": {
                "id": facility_dhis_id
            },
            "openingDate": self.date_operational.strftime("%Y-%m-%d"),
        }
        metadata_payload = {"keph": 'axUnguN4QDh'}

        if unit_uuid_status[1] == 'retrieved':
            r = requests.put(
                settings.DHIS_ENDPOINT + "api/organisationUnits/" +
                new_chu_payload.pop('id'),
                auth=(settings.DHIS_USERNAME, settings.DHIS_PASSWORD),
                headers={"Accept": "application/json"},
                json=new_chu_payload)
            print("Update CHU Response", r.url, r.status_code, r.json())
            LOGGER.info("Update CHU Response: %s" % r.text)
        else:
            r = requests.post(settings.DHIS_ENDPOINT + "api/organisationUnits",
                              auth=(settings.DHIS_USERNAME,
                                    settings.DHIS_PASSWORD),
                              headers={"Accept": "application/json"},
                              json=new_chu_payload)

            print("Create CHU Response", r.url, r.status_code, r.json())
            LOGGER.info("Create CHU Response: %s" % r.text)

        if r.json()["status"] != "OK":
            LOGGER.error("Failed PUSH: error -> {}".format(r.text))
            raise ValidationError({
                "Error!": [
                    "An error occured while pushing Community Unit to DHIS2. This is may be caused by the "
                    "existance of an organisation unit with as similar name as to the one you are creating. "
                    "Or some specific information like codes are not unique"
                ]
            })
        self.push_chu_metadata(metadata_payload, unit_uuid)

    def push_chu_metadata(self, metadata_payload, chu_uid):
        # Keph Level
        import requests
        r_keph = requests.post(
            settings.DHIS_ENDPOINT + "api/organisationUnitGroups/" +
            metadata_payload['keph'] + "/organisationUnits/" + chu_uid,
            auth=(settings.DHIS_USERNAME, settings.DHIS_PASSWORD),
            headers={"Accept": "application/json"},
        )
        LOGGER.info('Metadata CUs pushed successfullly')

    def get_facility_dhis2_parent_id(self):
        from facilities.models.facility_models import DhisAuth
        import requests
        r = requests.get(settings.DHIS_ENDPOINT + "api/organisationUnits.json",
                         auth=(settings.DHIS_USERNAME, settings.DHIS_PASSWORD),
                         headers={"Accept": "application/json"},
                         params={
                             "query": self.facility.code,
                             "fields": "[id,name]",
                             "filter": "level:in:[5]",
                             "paging": "false"
                         })

        if len(r.json()["organisationUnits"]) is 1:
            if r.json()["organisationUnits"][0]["id"]:
                return r.json()["organisationUnits"][0]["id"]
        else:
            raise ValidationError({
                "Error!": [
                    "Unable to resolve exact Facility linked to the CHU in DHIS2"
                ]
            })

    class Meta(AbstractBase.Meta):
        unique_together = (
            'name',
            'facility',
        )
        permissions = (
            ("view_rejected_chus",
             "Can see the rejected community health units"),
            ("can_approve_chu",
             "Can approve or reject a Community Health Unit"),
        )
 def test_get_prepared_value(self):
     seq = SequenceField()
     self.assertEqual(seq.get_prep_value(value=''), None)