<commit_before>from collections import OrderedDict
from datetime import datetime
from sqlalchemy import Column, Integer, select, Boolean, join, Text, ForeignKey

from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship, aliased
from radar.database import db
from radar.models import MetaModelMixin, OrganisationPatient
from radar.models.patient_demographics import PatientDemographics
from radar.models.organisations import Organisation
from radar.organisations import is_radar_organisation

GENDER_NOT_KNOWN = 0
GENDER_MALE = 1
GENDER_FEMALE = 2
GENDER_NOT_SPECIFIED = 9

GENDERS = OrderedDict([
    (GENDER_NOT_KNOWN, 'Not Known'),
    (GENDER_MALE, 'Male'),
    (GENDER_FEMALE, 'Female'),
    (GENDER_NOT_SPECIFIED, 'Not Specified'),
])


class Patient(db.Model, MetaModelMixin):
    __tablename__ = 'patients'

    id = Column(Integer, primary_key=True)

    is_active = Column(Boolean, nullable=False, default=True)
    comments = Column(Text)

    organisation_patients = relationship('OrganisationPatient', foreign_keys=[OrganisationPatient.patient_id])
    cohort_patients = relationship('CohortPatient')

    patient_demographics = relationship('PatientDemographics')
    patient_numbers = relationship('PatientNumber')
    patient_aliases = relationship('PatientAlias')

    @property
    def organisations(self):
        return [x.organisation for x in self.organisation_patients]

    @property
    def cohorts(self):
        return [x.cohort for x in self.cohort_patients]

    @hybrid_property
    def recruited_date(self):
        for x in self.organisation_patients:
            if is_radar_organisation(x.organisation):
                return x.recruited_date

        return None

    @recruited_date.expression
    def recruited_date(cls):
        return select([OrganisationPatient.recruited_date])\
            .select_from(join(OrganisationPatient, Organisation))\
            .where(OrganisationPatient.patient_id == cls.id)\
            .where(Organisation.code == 'RADAR')\
            .where(Organisation.type == 'OTHER')\
            .limit(1)\
            .as_scalar()

    def latest_demographics_attr(self, attr):
        demographics = self.latest_demographics

        if demographics is None:
            return None

        return getattr(demographics, attr)

    @classmethod
    def latest_demographics_query(cls, column):
        patient_alias = aliased(Patient)

        return select([column])\
            .select_from(join(PatientDemographics, patient_alias))\
            .where(patient_alias.id == cls.id)\
            .order_by(PatientDemographics.modified_date.desc())\
            .limit(1)\
            .as_scalar()

    @property
    def latest_demographics(self):
        patient_demographics = self.patient_demographics

        if len(patient_demographics) == 0:
            return None

        def by_modified_date(x):
            return x.modified_date or datetime.min

        return max(patient_demographics, key=by_modified_date)

    @hybrid_property
    def first_name(self):
        return self.latest_demographics_attr('first_name')

    @hybrid_property
    def last_name(self):
        return self.latest_demographics_attr('last_name')

    @hybrid_property
    def date_of_birth(self):
        return self.latest_demographics_attr('date_of_birth')

    @hybrid_property
    def date_of_death(self):
        return self.latest_demographics_attr('date_of_death')

    # TODO test this
    @hybrid_property
    def ethnicity_code(self):
        return self.latest_demographics_attr('ethnicity_code')

    @hybrid_property
    def gender(self):
        return self.latest_demographics_attr('gender')

    @hybrid_property
    def is_male(self):
        return self.gender == GENDER_MALE

    @hybrid_property
    def is_female(self):
        return self.gender == GENDER_FEMALE

    @first_name.expression
    def first_name(cls):
        return cls.latest_demographics_query(PatientDemographics.first_name)

    @last_name.expression
    def last_name(cls):
        return cls.latest_demographics_query(PatientDemographics.last_name)

    @date_of_birth.expression
    def date_of_birth(cls):
        return cls.latest_demographics_query(PatientDemographics.date_of_birth)

    @date_of_death.expression
    def date_of_death(cls):
        return cls.latest_demographics_query(PatientDemographics.date_of_death)

    # TODO test this
    @ethnicity_code.expression
    def ethnicity_code(cls):
        return cls.latest_demographics_query(PatientDemographics.ethnicity_code)

    @gender.expression
    def gender(cls):
        return cls.latest_demographics_query(PatientDemographics.gender)

    @property
    def earliest_date_of_birth(self):
        earliest_date_of_birth = None

        for demographics in self.patient_demographics:
            date_of_birth = demographics.date_of_birth

            if date_of_birth is not None and (earliest_date_of_birth is None or date_of_birth < earliest_date_of_birth):
                earliest_date_of_birth = date_of_birth

        return earliest_date_of_birth<commit_msg>Add functions to check group membership<commit_after>from collections import OrderedDict
from datetime import datetime
from sqlalchemy import Column, Integer, select, Boolean, join, Text

from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship, aliased
from radar.database import db
from radar.models import MetaModelMixin, OrganisationPatient
from radar.models.patient_demographics import PatientDemographics
from radar.models.organisations import Organisation
from radar.cohorts import is_radar_cohort

GENDER_NOT_KNOWN = 0
GENDER_MALE = 1
GENDER_FEMALE = 2
GENDER_NOT_SPECIFIED = 9

GENDERS = OrderedDict([
    (GENDER_NOT_KNOWN, 'Not Known'),
    (GENDER_MALE, 'Male'),
    (GENDER_FEMALE, 'Female'),
    (GENDER_NOT_SPECIFIED, 'Not Specified'),
])


class Patient(db.Model, MetaModelMixin):
    __tablename__ = 'patients'

    id = Column(Integer, primary_key=True)

    is_active = Column(Boolean, nullable=False, default=True)
    comments = Column(Text)

    organisation_patients = relationship('OrganisationPatient', foreign_keys=[OrganisationPatient.patient_id])
    cohort_patients = relationship('CohortPatient')

    patient_demographics = relationship('PatientDemographics')
    patient_numbers = relationship('PatientNumber')
    patient_aliases = relationship('PatientAlias')

    @property
    def organisations(self):
        return [x.organisation for x in self.organisation_patients]

    @property
    def cohorts(self):
        return [x.cohort for x in self.cohort_patients]

    @hybrid_property
    def recruited_date(self):
        for x in self.cohort_patients:
            if is_radar_cohort(x.organisation):
                return x.recruited_date

        return None

    @recruited_date.expression
    def recruited_date(cls):
        return select([OrganisationPatient.recruited_date])\
            .select_from(join(OrganisationPatient, Organisation))\
            .where(OrganisationPatient.patient_id == cls.id)\
            .where(Organisation.code == 'RADAR')\
            .where(Organisation.type == 'OTHER')\
            .limit(1)\
            .as_scalar()

    def latest_demographics_attr(self, attr):
        demographics = self.latest_demographics

        if demographics is None:
            return None

        return getattr(demographics, attr)

    @classmethod
    def latest_demographics_query(cls, column):
        patient_alias = aliased(Patient)

        return select([column])\
            .select_from(join(PatientDemographics, patient_alias))\
            .where(patient_alias.id == cls.id)\
            .order_by(PatientDemographics.modified_date.desc())\
            .limit(1)\
            .as_scalar()

    @property
    def latest_demographics(self):
        patient_demographics = self.patient_demographics

        if len(patient_demographics) == 0:
            return None

        def by_modified_date(x):
            return x.modified_date or datetime.min

        return max(patient_demographics, key=by_modified_date)

    @hybrid_property
    def first_name(self):
        return self.latest_demographics_attr('first_name')

    @hybrid_property
    def last_name(self):
        return self.latest_demographics_attr('last_name')

    @hybrid_property
    def date_of_birth(self):
        return self.latest_demographics_attr('date_of_birth')

    @hybrid_property
    def date_of_death(self):
        return self.latest_demographics_attr('date_of_death')

    # TODO test this
    @hybrid_property
    def ethnicity_code(self):
        return self.latest_demographics_attr('ethnicity_code')

    @hybrid_property
    def gender(self):
        return self.latest_demographics_attr('gender')

    @hybrid_property
    def is_male(self):
        return self.gender == GENDER_MALE

    @hybrid_property
    def is_female(self):
        return self.gender == GENDER_FEMALE

    @first_name.expression
    def first_name(cls):
        return cls.latest_demographics_query(PatientDemographics.first_name)

    @last_name.expression
    def last_name(cls):
        return cls.latest_demographics_query(PatientDemographics.last_name)

    @date_of_birth.expression
    def date_of_birth(cls):
        return cls.latest_demographics_query(PatientDemographics.date_of_birth)

    @date_of_death.expression
    def date_of_death(cls):
        return cls.latest_demographics_query(PatientDemographics.date_of_death)

    # TODO test this
    @ethnicity_code.expression
    def ethnicity_code(cls):
        return cls.latest_demographics_query(PatientDemographics.ethnicity_code)

    @gender.expression
    def gender(cls):
        return cls.latest_demographics_query(PatientDemographics.gender)

    @property
    def earliest_date_of_birth(self):
        earliest_date_of_birth = None

        for demographics in self.patient_demographics:
            date_of_birth = demographics.date_of_birth

            if date_of_birth is not None and (earliest_date_of_birth is None or date_of_birth < earliest_date_of_birth):
                earliest_date_of_birth = date_of_birth

        return earliest_date_of_birth

    def in_cohort(self, cohort):
        return any(x == cohort for x in self.cohorts)

    def in_organisation(self, organisation):
        return any(x == organisation for x in self.organisations)
{'fraction_non_alphanumeric': 0.07760936300844205, 'fraction_numerical': 0.001343054489639294, 'mean_word_length': 3.382093316519546, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3285475', 'n_tokens_mistral': 3315, 'n_tokens_neox': 2970, 'n_words': 607}