Exporter ses donnĂ©es SantĂ© dâiOS est une galĂšre, ce qui nâest pas surprenant venant dâApple. Pour utiliser le modĂšle CICO avec mes donnĂ©es sur Excel, il mâa fallu procĂ©der sur plusieurs Ă©tapes. La mĂ©thode proposĂ©e ici permet dâextraire les donnĂ©es journaliĂšres suivantes : Poids, IMC, Ănergie au repos, Ănergie active et Ănergie totale (Ă©tant la somme des deux prĂ©cĂ©dents).
- Sur lâapplication SantĂ©, il faut se rendre dans son profil (en cliquant sur sa photo de profil), scroller tout en bas puis cliquer sur âExporter toutes mes donnĂ©esâ. La pop-up de partage sâouvre et il est alors facile de partager le fichier obtenu par Airdrop sur son ordinateur.
- Sur son ordinateur, il faut dĂ©compresser le fichier (macOS le fait automatiquement en lâouvrant) et placer le fichier export.xml Ă Â cĂŽtĂ© du script python âhealth_to_excel.pyâ donné en annexe.
- Une fois le script lancé depuis le terminal, votre fichier Excel âsante_globale_continu.csvâ est crĂ©er et manipulable !
health_to_excel.py
- Copiez-collez le script dans un fichier texte nommé âhealth_to_excel.pyâ (avec lâextension .py).
- Le script utilise la derniĂšre version de Python 3 Ă Â installer sur le site officiel du logiciel.
- Le script se lance depuis un terminal avec la commande âpython3 health_to_excel.pyâ. Ne pas oublier de modifier la variable TAILLE_M au dĂ©but du script !
- Le script calcule en plus lâIMC pour chaque jour avec la formule de la puissance Ă Â 2,5 comme dĂ©crite sur WikipĂ©dia.
- Oui, le script est totalement vibe-codé, est totalement non maintainable, mais est parfaitement fonctionnel pour sa fonction.
import xml.etree.ElementTree as ET
import csv
from datetime import datetime, timedelta
from collections import defaultdict
# --- PARAMĂTRES UTILISATEUR ---
TAILLE_M = 0 # Ta taille en mĂštres
# ------------------------------
# 1. Chargement du fichier XML
print("Lecture du fichier XML... (Patientez)")
xml_path = "export.xml"
tree = ET.parse(xml_path)
root = tree.getroot()
print("Extraction, calcul de l'IMC et comblement des trous...")
# Structures pour stocker nos deux types de données
weights_by_date = defaultdict(list)
energy_by_date = defaultdict(lambda: {'active': 0.0, 'resting': 0.0})
all_dates_objects = []
types_energie = {
'HKQuantityTypeIdentifierActiveEnergyBurned': 'active',
'HKQuantityTypeIdentifierBasalEnergyBurned': 'resting'
}
# 2. Extraction unique
for record in root.findall(".//Record"):
record_type = record.get('type')
if record_type == 'HKQuantityTypeIdentifierBodyMass' or record_type in types_energie:
raw_date = record.get('startDate')
try:
dt_object = datetime.strptime(raw_date, '%Y-%m-%d %H:%M:%S %z')
date_formatted = dt_object.strftime('%m/%d/%Y')
all_dates_objects.append(dt_object.date())
# Si c'est un POIDS
if record_type == 'HKQuantityTypeIdentifierBodyMass':
time_formatted = dt_object.strftime('%I:%M:%S %p')
weights_by_date[date_formatted].append({
'Heure': time_formatted,
'Poids': record.get('value')
})
# Si c'est de l'ĂNERGIE
elif record_type in types_energie:
try:
value = float(record.get('value', 0))
except ValueError:
value = 0.0
categorie = types_energie[record_type]
energy_by_date[date_formatted][categorie] += value
except ValueError:
pass
# 3. Génération du calendrier continu combiné et calculs
final_records = []
if all_dates_objects:
start_date = min(all_dates_objects)
end_date = max(all_dates_objects)
current_date = start_date
while current_date <= end_date:
date_str = current_date.strftime('%m/%d/%Y')
# Préparation des données d'énergie
has_energy = date_str in energy_by_date
if has_energy:
active = round(energy_by_date[date_str]['active'], 2)
resting = round(energy_by_date[date_str]['resting'], 2)
total = round(active + resting, 2)
else:
active = resting = total = ""
# S'il y a des pesĂ©es ce jour-lĂ
if date_str in weights_by_date:
for w in weights_by_date[date_str]:
# On récupÚre le poids en tant que nombre
try:
poids_val = float(w['Poids'])
# CALCUL DE L'IMC : 1.3 * (poids / taille^2.5)
imc_val = 1.3 * (poids_val / (TAILLE_M ** 2.5))
imc_str = round(imc_val, 2)
except (ValueError, TypeError):
imc_str = ""
final_records.append({
'Date': date_str,
'Heure': w['Heure'],
'Poids': w['Poids'],
'IMC': imc_str,
'Energie_Active': active,
'Energie_Repos': resting,
'Energie_Totale': total
})
# S'il n'y a PAS de pesĂ©e ce jour-lĂ
else:
final_records.append({
'Date': date_str,
'Heure': '',
'Poids': '',
'IMC': '',
'Energie_Active': active,
'Energie_Repos': resting,
'Energie_Totale': total
})
current_date += timedelta(days=1)
print(f"{len(final_records)} lignes prĂȘtes pour le fichier final.")
# 4. Ăcriture du CSV
output_file = 'sante_globale_continu.csv'
try:
with open(output_file, mode='w', newline='', encoding='utf-8') as csvfile:
# On a ajouté la colonne 'IMC' juste aprÚs le Poids
fieldnames = ['Date', 'Heure', 'Poids', 'IMC', 'Energie_Active', 'Energie_Repos', 'Energie_Totale']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(final_records)
print(f"Terminé !")
except IOError:
print("Erreur d'écriture. Vérifiez que le fichier n'est pas ouvert ailleurs.")