213 lines
8.2 KiB
Python
213 lines
8.2 KiB
Python
|
from datetime import timedelta
|
||
|
from tempfile import NamedTemporaryFile
|
||
|
|
||
|
from django.http import HttpResponse
|
||
|
|
||
|
from openpyxl import Workbook
|
||
|
from openpyxl.styles import Font
|
||
|
from openpyxl.utils import get_column_letter
|
||
|
|
||
|
from .utils import format_d_m_Y
|
||
|
|
||
|
openxml_contenttype = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||
|
|
||
|
|
||
|
class OpenXMLExport:
|
||
|
def __init__(self, sheet_title=None):
|
||
|
self.wb = Workbook()
|
||
|
self.ws = self.wb.active
|
||
|
if sheet_title:
|
||
|
self.ws.title = sheet_title
|
||
|
self.bold = Font(name='Calibri', bold=True)
|
||
|
self.row_idx = 1
|
||
|
|
||
|
def write_line(self, values, bold=False, col_widths=()):
|
||
|
# A values item can be an object with a `value` attribute
|
||
|
for col_idx, value in enumerate(values, start=1):
|
||
|
cell = self.ws.cell(row=self.row_idx, column=col_idx)
|
||
|
if isinstance(value, timedelta):
|
||
|
cell.number_format = '[h]:mm;@'
|
||
|
elif hasattr(value, 'number_format'):
|
||
|
cell.number_format = value.number_format
|
||
|
cell.value = getattr(value, 'value', value)
|
||
|
if bold:
|
||
|
cell.font = self.bold
|
||
|
if col_widths and len(col_widths) >= col_idx:
|
||
|
self.ws.column_dimensions[get_column_letter(col_idx)].width = col_widths[col_idx - 1]
|
||
|
self.row_idx += 1
|
||
|
|
||
|
def add_sheet(self, title):
|
||
|
self.wb.create_sheet(title)
|
||
|
self.ws = self.wb[title]
|
||
|
self.row_idx = 1
|
||
|
|
||
|
def get_http_response(self, filename):
|
||
|
with NamedTemporaryFile() as tmp:
|
||
|
self.wb.save(tmp.name)
|
||
|
tmp.seek(0)
|
||
|
response = HttpResponse(tmp, content_type=openxml_contenttype)
|
||
|
response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
|
||
|
return response
|
||
|
|
||
|
|
||
|
class ExportReporting(OpenXMLExport):
|
||
|
def __init__(self):
|
||
|
super().__init__('temp')
|
||
|
self.first_sheet = True
|
||
|
# Totaux pour vérification interne dans les tests.
|
||
|
self._total_spe = {'eval': timedelta(0), 'suivi': timedelta(0), 'gen': timedelta(0)}
|
||
|
|
||
|
def setup_sheet(self, title):
|
||
|
if not self.first_sheet:
|
||
|
self.add_sheet(title)
|
||
|
else:
|
||
|
self.ws.title = title
|
||
|
self.first_sheet = False
|
||
|
|
||
|
def produce_suivis(self, sheet_title, query, mois):
|
||
|
self.setup_sheet(sheet_title)
|
||
|
SuiviSheet(self, mois).produce(query)
|
||
|
|
||
|
def produce_nouveaux(self, sheet_title, query):
|
||
|
self.setup_sheet(sheet_title)
|
||
|
NouveauxSheet(self).produce(query)
|
||
|
|
||
|
def produce_termines(self, sheet_title, query):
|
||
|
self.setup_sheet(sheet_title)
|
||
|
TerminesSheet(self).produce(query)
|
||
|
|
||
|
def produce_totaux(self, sheet_title):
|
||
|
self.setup_sheet(sheet_title)
|
||
|
self.write_line(['Évaluation', self._total_spe['eval']], col_widths=[25, 10])
|
||
|
self.write_line(['Accompagnement', self._total_spe['suivi']])
|
||
|
self.write_line(['Intervention générale', self._total_spe['gen']])
|
||
|
self.write_line(['Total', self._total_spe['eval'] + self._total_spe['suivi'] + self._total_spe['gen']])
|
||
|
|
||
|
|
||
|
class BaseFamilleSheet:
|
||
|
en_tetes = [
|
||
|
('Institution', 12), ('Prestation', 10), ('Nom', 25), ('Prenom', 15), ('Genre', 8),
|
||
|
('Date de naissance', 17), ('Adresse', 20), ('NPA', 8), ('Localité', 20), ('Canton', 8),
|
||
|
('OPE', 25), ('Nom mère', 20), ('Prénom mère', 20), ('Nom père', 20), ('Prénom père', 20),
|
||
|
('Autorité parentale', 18), ('Statut marital', 15), ('Statut financier', 15),
|
||
|
('Fam. monopar.', 15), ('Nbre enfants', 12),
|
||
|
('Date demande', 15), ('Provenance', 15), ('Motif demande', 40), ('Début suivi', 15),
|
||
|
]
|
||
|
|
||
|
def __init__(self, exp):
|
||
|
self.exp = exp
|
||
|
|
||
|
def _set_col_dimensions(self):
|
||
|
for col_idx, (_, size) in enumerate(self.en_tetes, start=1):
|
||
|
self.exp.ws.column_dimensions[get_column_letter(col_idx)].width = size
|
||
|
|
||
|
def produce(self, query):
|
||
|
self._set_col_dimensions()
|
||
|
self.exp.write_line([et[0] for et in self.en_tetes], bold=True)
|
||
|
for famille in query:
|
||
|
self.produce_famille(famille)
|
||
|
self.exp.ws.freeze_panes = self.exp.ws['A2']
|
||
|
|
||
|
def produce_famille(self, famille):
|
||
|
membres_suivis = famille.membres_suivis()
|
||
|
if membres_suivis:
|
||
|
for pers in famille.membres_suivis():
|
||
|
data = self.collect_pers_data(famille, pers)
|
||
|
self.exp.write_line(data)
|
||
|
else:
|
||
|
data = self.collect_pers_data(famille, None)
|
||
|
self.exp.write_line(data)
|
||
|
|
||
|
def collect_pers_data(self, famille, pers):
|
||
|
parents = famille.parents()
|
||
|
mere = next((par for par in parents if par.role.nom == 'Mère'), None)
|
||
|
pere = next((par for par in parents if par.role.nom == 'Père'), None)
|
||
|
return [
|
||
|
'Fondation Transit',
|
||
|
'AEMO',
|
||
|
pers.nom if pers else famille.nom,
|
||
|
pers.prenom if pers else "-",
|
||
|
pers.genre if pers else "-",
|
||
|
format_d_m_Y(pers.date_naissance) if pers else "-",
|
||
|
pers.rue if pers else famille.rue,
|
||
|
pers.npa if pers else famille.npa,
|
||
|
pers.localite if pers else famille.localite,
|
||
|
'NE',
|
||
|
famille.suivi.ope_referent.nom_prenom if famille.suivi.ope_referent else '',
|
||
|
mere.nom if mere else '',
|
||
|
mere.prenom if mere else '',
|
||
|
pere.nom if pere else '',
|
||
|
pere.prenom if pere else '',
|
||
|
famille.get_autorite_parentale_display(),
|
||
|
famille.get_statut_marital_display(),
|
||
|
famille.get_statut_financier_display(),
|
||
|
{True: 'OUI', False: 'NON', None: ''}[famille.monoparentale],
|
||
|
len(famille.membres_suivis()) + len(famille.enfants_non_suivis()),
|
||
|
|
||
|
format_d_m_Y(famille.suivi.date_demande),
|
||
|
famille.get_provenance_display(),
|
||
|
famille.suivi.get_motif_demande_display(),
|
||
|
format_d_m_Y(famille.suivi.date_debut_suivi),
|
||
|
]
|
||
|
|
||
|
|
||
|
class SuiviSheet(BaseFamilleSheet):
|
||
|
en_tetes = BaseFamilleSheet.en_tetes + [('H. Évaluation', 12), ('H. Suivi', 12), ('H. Prest. gén.', 12)]
|
||
|
|
||
|
def __init__(self, exp, date_debut_mois):
|
||
|
self.date_debut_mois = date_debut_mois
|
||
|
super().__init__(exp)
|
||
|
|
||
|
def produce_famille(self, famille):
|
||
|
# Prepare some data common to famille
|
||
|
nb_membres_suivis = len(famille.membres_suivis()) or 1
|
||
|
famille._h_evaluation_par_pers = famille.total_mensuel_evaluation(self.date_debut_mois) // nb_membres_suivis
|
||
|
famille._h_suivi_par_pers = famille.total_mensuel_suivi(self.date_debut_mois) // nb_membres_suivis
|
||
|
super().produce_famille(famille)
|
||
|
|
||
|
def collect_pers_data(self, famille, pers):
|
||
|
data = super().collect_pers_data(famille, pers)
|
||
|
h_evaluation = famille._h_evaluation_par_pers
|
||
|
h_suivi = famille._h_suivi_par_pers
|
||
|
h_prest_gen = famille.prest_gen # Annotation from the view.
|
||
|
data.extend([h_evaluation, h_suivi, h_prest_gen])
|
||
|
# Variables des totaux
|
||
|
self.exp._total_spe['eval'] += h_evaluation
|
||
|
self.exp._total_spe['suivi'] += h_suivi
|
||
|
self.exp._total_spe['gen'] += h_prest_gen
|
||
|
return data
|
||
|
|
||
|
|
||
|
class NouveauxSheet(BaseFamilleSheet):
|
||
|
pass
|
||
|
|
||
|
|
||
|
class TerminesSheet(BaseFamilleSheet):
|
||
|
en_tetes = BaseFamilleSheet.en_tetes + [
|
||
|
('Date fin suivi', 15), ('Motif fin suivi', 15), ('Destination', 15), ('Total heures', 12),
|
||
|
]
|
||
|
|
||
|
def collect_pers_data(self, famille, pers):
|
||
|
data = super().collect_pers_data(famille, pers)
|
||
|
suivi = famille.suivi
|
||
|
data.extend([
|
||
|
format_d_m_Y(suivi.date_fin_suivi),
|
||
|
famille.suivi.get_motif_fin_suivi_display(),
|
||
|
famille.get_destination_display(),
|
||
|
famille.temps_total_prestations_reparti(),
|
||
|
])
|
||
|
return data
|
||
|
|
||
|
|
||
|
class ExportStatistique(OpenXMLExport):
|
||
|
def __init__(self, *args, col_widths=None, **kwargs):
|
||
|
self.col_widths = col_widths
|
||
|
super().__init__(*args, **kwargs)
|
||
|
|
||
|
def fill_data(self, generator):
|
||
|
for row in generator:
|
||
|
if row and row[0] == 'BOLD':
|
||
|
self.write_line(row[1:], bold=True, col_widths=self.col_widths)
|
||
|
else:
|
||
|
self.write_line(row, col_widths=self.col_widths)
|