Project

General

Profile

Download (17.1 KB) Statistics
| Branch: | Tag: | Revision:
# -*- coding: utf-8 -*-
from datetime import datetime
from collections import defaultdict

from django.db.models import Q

from calebasse.actes.models import Act
from calebasse.dossiers.models import (CmppHealthCareDiagnostic,
CmppHealthCareTreatment)


def list_acts_for_billing_first_round(end_day, service, start_day=None, acts=None, patient=None):
"""Used to sort acts and extract acts billable before specific service
requirements.

At first, all acts not billed are listed per patient.
Then acts are sorted.
Each sorting split the acts in two sets, one set for rejected acts,
on set for selected sets.
First sort rejects acts days where all acts are not locked:
acts_not_locked
Second sort rejects acts not in state 'VALIDE':
acts_not_valide
Third sort rejects acts not billable:
acts_not_billable
Acts billable in :
acts_billable

acts = acts_not_locked + \
acts_not_valide + \
acts_not_billable + \
acts_billable

:param end_day: formatted date that gives the last day when acts are taken
in account.
:type end_day: datetime
:param service: service in which acts are dealt with.
:type service: calebasse.ressources.Service

:returns: a list of dictionnaries where patients are the keys and values
are lists of acts. The second element of this list in not a dict but
a list of the days where are all days are not locked.
:rtype: list
"""

from calebasse.actes.models import Act
if isinstance(end_day, datetime):
end_day = end_day.date()
if start_day and isinstance(start_day, datetime):
start_day = start_day.date()

acts = Act.objects.filter(validation_locked=False,
patient__service=service, date__lte=end_day)
if start_day:
acts = acts.filter(date__gte=start_day)
days_not_locked = sorted(set(acts.values_list('date', flat=True)))

acts = None
if patient:
acts = Act.objects.filter(patient=patient, validation_locked=True,
is_billed=False, valide=True, is_lost=False,
patient__service=service, date__lte=end_day)
else:
acts = Act.objects.filter(validation_locked=True,
is_billed=False, valide=True, is_lost=False,
patient__service=service, date__lte=end_day)
if start_day:
acts = acts.filter(date__gte=start_day)
acts = acts.exclude(date__in=days_not_locked)
acts = acts.order_by('date')

# deprecated
acts_not_locked = {}
acts_not_valide = {}

acts_pause = {}
acts_not_billable = {}
acts_billable = {}


pause_query = Q(pause=True)
billable_query = Q(act_type__billable=True, switch_billable=False) | \
Q(act_type__billable=False, switch_billable=True)

paused_acts = acts.filter(pause_query).select_related('patient', 'act_type')
not_billable_acts = acts.filter(~pause_query & ~billable_query).select_related('patient', 'act_type')
billable_acts = acts.filter(~pause_query & billable_query)
billable_acts = billable_acts.select_related('act_type', 'patient__policyholder__health_center').prefetch_related('patient__act_set', 'patient__act_set__healthcare')

for act in paused_acts:
acts_pause.setdefault(act.patient, []).append(act)

for act in not_billable_acts:
acts_not_billable.setdefault(act.patient, []).append(act)

for act in billable_acts:
acts_billable.setdefault(act.patient, []).append(act)
return (acts_not_locked, days_not_locked, acts_not_valide,
acts_not_billable, acts_pause, acts_billable)


def list_acts_for_billing_CAMSP(start_day, end_day, service, acts=None):
"""Used to sort acts billable by specific service requirements.

For the CAMSP, only the state of the patient record 'CAMSP_STATE_SUIVI'
at the date of the act determine if the acte is billable.

acts = acts_not_locked + \
acts_not_valide + \
acts_not_billable + \
acts_bad_state + \
acts_accepted

:param end_day: formatted date that gives the last day when acts are taken
in account.
:type end_day: datetime
:param service: service in which acts are dealt with.
:type service: calebasse.ressources.Service

:returns: a list of dictionnaries where patients are the keys and values
are lists of acts. The second element of this list in not a dict but
a list of the days where are all days are not locked.
:rtype: list
"""

acts_not_locked, days_not_locked, acts_not_valide, \
acts_not_billable, acts_pause, acts_billable = \
list_acts_for_billing_first_round(end_day, service,
start_day, acts=acts)
acts_bad_state = {}
acts_accepted = {}
patients_missing_policy = []
for patient, acts in acts_billable.iteritems():
for act in acts:
if patient.was_in_state_at_day(act.date, 'SUIVI'):
acts_accepted.setdefault(act.patient, []).append(act)
else:
acts_bad_state.setdefault(act.patient, []).\
append((act, 'NOT_ACCOUNTABLE_STATE'))
for patient in acts_accepted.keys():
if not patient.policyholder or \
not patient.policyholder.health_center or \
not patient.policyholder.management_code or \
not patient.policyholder.social_security_id:
patients_missing_policy.append(patient)
return (acts_not_locked, days_not_locked, acts_not_valide,
acts_not_billable, acts_pause, acts_bad_state,
acts_accepted, patients_missing_policy)


def list_acts_for_billing_SESSAD(start_day, end_day, service, acts=None):
"""Used to sort acts billable by specific service requirements.

For the SESSAD, acts are billable if the state of the patient record at
the date of the act is 'SESSAD_STATE_TRAITEMENT' and there was also a
valid notification at that date.

acts = acts_not_locked + \
acts_not_valide + \
acts_not_billable + \
acts_bad_state + \
acts_missing_valid_notification + \
acts_accepted

:param end_day: formatted date that gives the last day when acts are taken
in account.
:type end_day: datetime
:param service: service in which acts are dealt with.
:type service: calebasse.ressources.Service

:returns: a list of dictionnaries where patients are the keys and values
are lists of acts. The second element of this list in not a dict but
a list of the days where are all days are not locked.
:rtype: list
"""

acts_not_locked, days_not_locked, acts_not_valide, \
acts_not_billable, acts_pause, acts_billable = \
list_acts_for_billing_first_round(end_day, service,
start_day=start_day, acts=acts)
acts_bad_state = {}
acts_missing_valid_notification = {}
acts_accepted = {}
patients_missing_policy = []
for patient, acts in acts_billable.iteritems():
for act in acts:
if patient.was_in_state_at_day(act.date,
'TRAITEMENT'):
if not act.was_covered_by_notification():
acts_missing_valid_notification.\
setdefault(act.patient, []).append(act)
else:
acts_accepted.setdefault(act.patient, []).append(act)
else:
acts_bad_state.setdefault(act.patient, []).\
append((act, 'NOT_ACCOUNTABLE_STATE'))
for patient in acts_accepted.keys():
if not patient.policyholder or \
not patient.policyholder.health_center or \
not patient.policyholder.management_code or \
not patient.policyholder.social_security_id:
patients_missing_policy.append(patient)
return (acts_not_locked, days_not_locked, acts_not_valide,
acts_not_billable, acts_pause, acts_bad_state,
acts_missing_valid_notification, acts_accepted,
patients_missing_policy)


def list_acts_for_billing_CMPP(end_day, service, acts=None):
"""Used to sort acts billable by specific service requirements.

For the CMPP, acts are billable if

acts = acts_not_locked + \
acts_not_valide + \
acts_not_billable + \
acts_diagnostic + \
acts_treatment + \
acts_losts

:param end_day: formatted date that gives the last day when acts are taken
in account.
:type end_day: datetime
:param service: service in which acts are dealt with.
:type service: calebasse.ressources.Service

:returns: a list of dictionnaries where patients are the keys and values
are lists of acts. The second element of this list in not a dict but
a list of the days where are all days are not locked.
:rtype: list
"""

acts_not_locked, days_not_locked, acts_not_valide, \
acts_not_billable, acts_pause, acts_billable = \
list_acts_for_billing_first_round(end_day, service, acts=acts)

acts_diagnostic = {}
acts_treatment = {}
acts_losts = {}
acts_losts_missing_policy = {}
acts_losts_missing_birthdate = {}
patient_ids = [p.id for p in acts_billable]
# compute latest hcds using one query
latest_hcd = {}
hcds = CmppHealthCareDiagnostic.objects.filter(patient_id__in=patient_ids).order_by('-start_date').select_related(depth=1).prefetch_related('act_set')
for hcd in hcds:
if hcd.patient not in latest_hcd:
latest_hcd[hcd.patient] = hcd
# compute two latest hcts using one query
latest_hcts = defaultdict(lambda:[])
hcts = CmppHealthCareTreatment.objects.filter(patient_id__in=patient_ids).order_by('-start_date').select_related(depth=1).prefetch_related('act_set')
for hct in hcts:
if hct.patient not in latest_hcts or len(latest_hcts[hct.patient]) < 2:
latest_hcts[hct.patient].append(hct)

for patient, acts in acts_billable.items():
if not patient.policyholder or \
not patient.policyholder.health_center or \
not patient.policyholder.social_security_id:
acts_losts_missing_policy[patient] = acts
continue
if not patient.birthdate:
acts_losts_missing_birthdate[patient] = acts
continue
# Date de début de la prise en charge ayant servis au dernier acte facturé
lasts_billed = sorted(filter(lambda a: a.is_billed and a.healthcare is not None, patient.act_set.all()), key=lambda a: a.date, reverse=True)
last_hc_date = None
if lasts_billed:
last_hc_date = lasts_billed[0].healthcare.start_date
hcd = None
len_acts_cared_diag = 0
try:
hcd = latest_hcd.get(patient)
if not last_hc_date or last_hc_date <= hcd.start_date:
# actes prise en charge par ce hc
len_acts_cared_diag = len(hcd.act_set.all())
else:
# Comme un PC diag n'a pas de date de fin, on considère qu'elle ne sert plus si un acte a été couvert par une prise en charge plus récente.
hcd = None
except:
pass
'''
We take in account the two last treatment healthcare
'''
hcts = latest_hcts.get(patient, [])
# acts are all billable and chronologically ordered
count_hcd = 0
count_hct_1 = 0
count_hct_2 = 0
for act in acts:
cared = False
# If there is overlapping between hc period the more recent must
# be used ! That should not happen with hct but might between
# hcd and hct.
if len(hcts) > 0 and hcts[0] and hcts[0].start_date <= act.date and hcts[0].end_date >= act.date:
if count_hct_2 < hcts[0].get_act_number() - hcts[0].get_nb_acts_cared():
acts_treatment.setdefault(patient, []).append((act, hcts[0]))
count_hct_2 = count_hct_2 + 1
cared = True
if not cared and len(hcts) > 1 and hcts[1] and hcts[1].start_date <= act.date and hcts[1].end_date >= act.date:
# Ce qui seraient prise en charge
# ne doit pas dépasser la limite de prise en charge du hc
if count_hct_1 < hcts[1].get_act_number() - hcts[1].get_nb_acts_cared():
acts_treatment.setdefault(patient, []).append((act, hcts[1]))
count_hct_1 = count_hct_1 + 1
cared = True
if not cared and hcd and hcd.start_date <= act.date and \
(not hcd.end_date or hcd.end_date >= act.date):
# Ce qui seraient prise en charge
nb_acts_cared = len_acts_cared_diag + count_hcd
# Ne doit pas dépasser la limite de prise en charge du hc
if nb_acts_cared < hcd.get_act_number() :
acts_diagnostic.setdefault(patient, []).append((act, hcd))
count_hcd = count_hcd + 1
cared = True
if not cared:
acts_losts.setdefault(patient, []).append(act)
return (acts_not_locked, days_not_locked, acts_not_valide,
acts_not_billable, acts_pause, acts_diagnostic,
acts_treatment, acts_losts, acts_losts_missing_policy,
acts_losts_missing_birthdate)

def list_acts_for_billing_CMPP_per_patient(patient, end_day, service, acts=None):

acts_not_locked, days_not_locked, acts_not_valide, \
acts_not_billable, acts_pause, acts_billable = \
list_acts_for_billing_first_round(end_day, service, acts=acts, patient=patient)

acts_per_hc = {}
acts_losts = []
if len(acts_billable.keys()) > 1:
raise "Should not find more than one patient"
elif len(acts_billable.keys()) == 1:
# Date de début de la prise en charge ayant servis au dernier acte facturé
lasts_billed = Act.objects.filter(patient=patient, is_billed = True, healthcare__isnull=False).order_by('-date')
last_hc_date = None
if lasts_billed:
last_hc_date = lasts_billed[0].healthcare.start_date
patient, acts = acts_billable.items()[0]
hcd = None
len_acts_cared_diag = 0
try:
hcd = CmppHealthCareDiagnostic.objects.\
filter(patient=patient).latest('start_date')
if not last_hc_date or last_hc_date <= hcd.start_date:
# actes prise en charge par ce hc
len_acts_cared_diag = len(hcd.act_set.all())
else:
# Comme un PC diag n'a pas de date de fin, on considère qu'elle ne sert plus si un acte a été couvert par une prise en charge plus récente.
hcd = None
except:
pass
'''
We take in account the two last treatment healthcare
'''
hcts = None
len_acts_cared_trait = 0
try:
hcts = CmppHealthCareTreatment.objects.\
filter(patient=patient).order_by('-start_date')
except:
pass
# acts are all billable and chronologically ordered
count_hcd = 0
count_hct_1 = 0
count_hct_2 = 0
if hcd:
acts_per_hc[hcd] = []
if len(hcts) > 0:
acts_per_hc[hcts[0]] = []
if len(hcts) > 1:
acts_per_hc[hcts[1]] = []
for act in acts:
cared = False
# If there is overlapping between hc period the more recent must
# be used ! That should not happen with hct but might between
# hcd and hct.
if len(hcts) > 0 and hcts[0] and hcts[0].start_date <= act.date and hcts[0].end_date >= act.date:
if count_hct_2 < hcts[0].get_act_number() - hcts[0].get_nb_acts_cared():
acts_per_hc[hcts[0]].append(act)
count_hct_2 = count_hct_2 + 1
cared = True
if not cared and len(hcts) > 1 and hcts[1] and hcts[1].start_date <= act.date and hcts[1].end_date >= act.date:
# Ce qui seraient prise en charge
# ne doit pas dépasser la limite de prise en charge du hc
if count_hct_1 < hcts[1].get_act_number() - hcts[1].get_nb_acts_cared():
acts_per_hc[hcts[1]].append(act)
count_hct_1 = count_hct_1 + 1
cared = True
if not cared and hcd and hcd.start_date <= act.date and \
(not hcd.end_date or hcd.end_date >= act.date):
# Ce qui seraient prise en charge
nb_acts_cared = len_acts_cared_diag + count_hcd
# Ne doit pas dépasser la limite de prise en charge du hc
if nb_acts_cared < hcd.get_act_number() :
acts_per_hc[hcd].append(act)
count_hcd = count_hcd + 1
cared = True
if not cared:
acts_losts.append(act)
if len(acts_pause.keys()) == 1:
acts_pause = acts_pause.values()[0]
else:
acts_pause = []
return (acts_not_locked, days_not_locked, acts_not_valide,
acts_not_billable, acts_pause, acts_per_hc, acts_losts)
(8-8/16)