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.")