Compare commits
6 Commits
main
...
13e794eb34
| Author | SHA1 | Date | |
|---|---|---|---|
| 13e794eb34 | |||
| e58f867bd0 | |||
| 57540d5159 | |||
| d6943faf59 | |||
| 8562c45f05 | |||
| 1cc2c754b7 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -195,12 +195,7 @@ Endobest Reporting/
|
||||
jsons history/
|
||||
nul
|
||||
|
||||
# Ignore all json, exe, log, txt, csv and xlsx files
|
||||
dashboard.log
|
||||
*.json
|
||||
*.exe
|
||||
*.log
|
||||
*.txt
|
||||
*.csv
|
||||
/*.xlsx
|
||||
!eb_org_center_mapping.xlsx
|
||||
/pyproject.toml
|
||||
Binary file not shown.
BIN
config/Endobest_Dashboard_Config-new.xlsx
Normal file
BIN
config/Endobest_Dashboard_Config-new.xlsx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
config/eb_dashboard_extended_template-new.xlsx
Normal file
BIN
config/eb_dashboard_extended_template-new.xlsx
Normal file
Binary file not shown.
Binary file not shown.
40
dashboard.log
Normal file
40
dashboard.log
Normal file
@@ -0,0 +1,40 @@
|
||||
2026-02-09 17:08:47,130 - WARNING - Error in get_record_by_patient_id (Attempt 1): Server error '502 Bad Gateway' for url 'https://api-hcp.ziwig-connect.com/api/records/byPatient'
|
||||
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502
|
||||
2026-02-09 17:08:47,130 - WARNING - Error in get_record_by_patient_id (Attempt 1): Server error '502 Bad Gateway' for url 'https://api-hcp.ziwig-connect.com/api/records/byPatient'
|
||||
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502
|
||||
2026-02-09 17:08:47,130 - WARNING - Error in get_record_by_patient_id (Attempt 1): Server error '502 Bad Gateway' for url 'https://api-hcp.ziwig-connect.com/api/records/byPatient'
|
||||
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502
|
||||
2026-02-09 17:09:45,882 - WARNING - Error in get_record_by_patient_id (Attempt 1): [WinError 10054] Une connexion existante a d<> <20>tre ferm<72>e par l<>h<EFBFBD>te distant
|
||||
2026-02-09 17:09:45,883 - WARNING - Error in get_all_questionnaires_by_patient (Attempt 1): [WinError 10054] Une connexion existante a d<> <20>tre ferm<72>e par l<>h<EFBFBD>te distant
|
||||
2026-02-09 17:09:45,882 - WARNING - Error in get_all_questionnaires_by_patient (Attempt 1): [WinError 10054] Une connexion existante a d<> <20>tre ferm<72>e par l<>h<EFBFBD>te distant
|
||||
2026-02-09 17:09:45,883 - WARNING - Error in get_record_by_patient_id (Attempt 1): [WinError 10054] Une connexion existante a d<> <20>tre ferm<72>e par l<>h<EFBFBD>te distant
|
||||
2026-02-09 17:09:45,889 - WARNING - Error in get_all_questionnaires_by_patient (Attempt 1): [WinError 10054] Une connexion existante a d<> <20>tre ferm<72>e par l<>h<EFBFBD>te distant
|
||||
2026-02-09 17:09:45,890 - WARNING - Error in get_record_by_patient_id (Attempt 1): [WinError 10054] Une connexion existante a d<> <20>tre ferm<72>e par l<>h<EFBFBD>te distant
|
||||
2026-02-09 17:09:45,890 - WARNING - Error in get_all_questionnaires_by_patient (Attempt 1): [WinError 10054] Une connexion existante a d<> <20>tre ferm<72>e par l<>h<EFBFBD>te distant
|
||||
2026-02-09 17:09:45,892 - WARNING - Error in get_all_questionnaires_by_patient (Attempt 1): [WinError 10054] Une connexion existante a d<> <20>tre ferm<72>e par l<>h<EFBFBD>te distant
|
||||
2026-02-09 17:09:45,890 - WARNING - Error in get_record_by_patient_id (Attempt 1): [WinError 10054] Une connexion existante a d<> <20>tre ferm<72>e par l<>h<EFBFBD>te distant
|
||||
2026-02-09 17:09:45,892 - WARNING - Error in get_all_questionnaires_by_patient (Attempt 1): [WinError 10054] Une connexion existante a d<> <20>tre ferm<72>e par l<>h<EFBFBD>te distant
|
||||
2026-02-09 17:09:45,891 - WARNING - Error in get_all_questionnaires_by_patient (Attempt 1): [WinError 10054] Une connexion existante a d<> <20>tre ferm<72>e par l<>h<EFBFBD>te distant
|
||||
2026-02-09 17:09:45,892 - WARNING - Error in get_all_questionnaires_by_patient (Attempt 1): [WinError 10054] Une connexion existante a d<> <20>tre ferm<72>e par l<>h<EFBFBD>te distant
|
||||
2026-02-09 17:17:16,591 - WARNING - Error in get_all_questionnaires_by_patient (Attempt 1): Server error '502 Bad Gateway' for url 'https://api-hcp.ziwig-connect.com/api/surveys/filter/with-answers'
|
||||
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502
|
||||
2026-02-09 17:17:16,591 - WARNING - Error in get_record_by_patient_id (Attempt 1): Server error '502 Bad Gateway' for url 'https://api-hcp.ziwig-connect.com/api/records/byPatient'
|
||||
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502
|
||||
2026-02-09 17:17:16,592 - WARNING - Error in get_record_by_patient_id (Attempt 1): Server error '502 Bad Gateway' for url 'https://api-hcp.ziwig-connect.com/api/records/byPatient'
|
||||
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502
|
||||
2026-02-09 17:21:04,034 - WARNING - Error in get_request_by_tube_id (Attempt 1): Client error '401 Unauthorized' for url 'https://api-lab.ziwig-connect.com/api/requests/by-tube-id/55241110000385?isAdmin=true&organization=undefined'
|
||||
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401
|
||||
2026-02-09 17:21:04,070 - WARNING - Error in get_request_by_tube_id (Attempt 1): Client error '401 Unauthorized' for url 'https://api-lab.ziwig-connect.com/api/requests/by-tube-id/55241110001327?isAdmin=true&organization=undefined'
|
||||
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401
|
||||
2026-02-09 17:21:05,040 - WARNING - Error in get_record_by_patient_id (Attempt 1): Client error '401 Unauthorized' for url 'https://api-hcp.ziwig-connect.com/api/records/byPatient'
|
||||
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401
|
||||
2026-02-09 17:21:05,043 - WARNING - Error in get_record_by_patient_id (Attempt 1): Client error '401 Unauthorized' for url 'https://api-hcp.ziwig-connect.com/api/records/byPatient'
|
||||
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401
|
||||
2026-02-09 17:21:05,045 - WARNING - Error in get_all_questionnaires_by_patient (Attempt 1): Client error '401 Unauthorized' for url 'https://api-hcp.ziwig-connect.com/api/surveys/filter/with-answers'
|
||||
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401
|
||||
2026-02-09 17:21:05,045 - WARNING - Error in get_all_questionnaires_by_patient (Attempt 1): Client error '401 Unauthorized' for url 'https://api-hcp.ziwig-connect.com/api/surveys/filter/with-answers'
|
||||
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401
|
||||
2026-02-09 17:21:05,331 - WARNING - Error in get_record_by_patient_id (Attempt 1): Client error '401 Unauthorized' for url 'https://api-hcp.ziwig-connect.com/api/records/byPatient'
|
||||
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401
|
||||
2026-02-09 17:21:06,089 - WARNING - Error in get_record_by_patient_id (Attempt 1): Client error '401 Unauthorized' for url 'https://api-hcp.ziwig-connect.com/api/records/byPatient'
|
||||
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401
|
||||
BIN
eb_dashboard-no-6-month-visit.exe
Normal file
BIN
eb_dashboard-no-6-month-visit.exe
Normal file
Binary file not shown.
503
eb_dashboard.py
503
eb_dashboard.py
@@ -21,7 +21,7 @@
|
||||
# identification, and support for complex data extraction using JSON path expressions.
|
||||
import json
|
||||
import logging
|
||||
import msvcrt
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
@@ -86,10 +86,6 @@ from eb_dashboard_utils import (
|
||||
clear_httpx_client,
|
||||
get_thread_position,
|
||||
get_config_path,
|
||||
set_dashboard_config_path_override,
|
||||
get_dashboard_config_path,
|
||||
set_output_file_suffix,
|
||||
get_output_filename,
|
||||
thread_local_storage,
|
||||
run_with_context
|
||||
)
|
||||
@@ -107,23 +103,8 @@ from eb_dashboard_excel_export import (
|
||||
set_dependencies as excel_set_dependencies
|
||||
)
|
||||
|
||||
def _fatal_cli_error(message):
|
||||
"""Print a CLI error then wait for Enter before exiting (keeps console open on Windows Explorer launch)."""
|
||||
print(message)
|
||||
input("Press Enter to exit...")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# Handle --add-suffix <value> early: must precede logging.basicConfig (affects log filename)
|
||||
if "--add-suffix" in sys.argv:
|
||||
_idx = sys.argv.index("--add-suffix")
|
||||
if _idx + 1 >= len(sys.argv):
|
||||
_fatal_cli_error("Error: --add-suffix requires a value")
|
||||
set_output_file_suffix(sys.argv[_idx + 1])
|
||||
del sys.argv[_idx:_idx + 2]
|
||||
|
||||
logging.basicConfig(level=logging.WARNING, format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
filename=get_output_filename(LOG_FILE_NAME), filemode='w')
|
||||
logging.basicConfig(level=logging.WARNING, format='%(asctime)s - %(levelname)s - %(message)s', filename=LOG_FILE_NAME,
|
||||
filemode='w')
|
||||
|
||||
|
||||
# ============================================================================
|
||||
@@ -137,10 +118,6 @@ access_token = ""
|
||||
refresh_token = ""
|
||||
threads_list = []
|
||||
_token_refresh_lock = threading.Lock()
|
||||
on_retry_exhausted = "ask" # "ask" | "ignore" | "abort" — set at startup
|
||||
fetch_six_month_visit = False # Whether to fetch 6-month visit data (slow, ~5s per patient)
|
||||
_stored_username = "" # Credentials stored at login for automatic re-login
|
||||
_stored_password = ""
|
||||
_threads_list_lock = threading.Lock()
|
||||
global_pbar = None
|
||||
_global_pbar_lock = threading.Lock()
|
||||
@@ -149,7 +126,6 @@ _user_interaction_lock = threading.Lock()
|
||||
# Global variables (mutable, set at runtime - not constants)
|
||||
inclusions_mapping_config = []
|
||||
organizations_mapping_config = []
|
||||
_include_drafts = False # Set by --include-drafts CLI arg if provided
|
||||
excel_export_config = None
|
||||
excel_export_enabled = False
|
||||
|
||||
@@ -172,23 +148,6 @@ if "--debug" in sys.argv:
|
||||
sys.argv.remove("--debug")
|
||||
enable_debug_mode()
|
||||
|
||||
# Handle --config <path> override (remove from sys.argv to preserve positional args)
|
||||
if "--config" in sys.argv:
|
||||
_idx = sys.argv.index("--config")
|
||||
if _idx + 1 >= len(sys.argv):
|
||||
_fatal_cli_error("Error: --config requires a file path argument")
|
||||
_raw_config_path = sys.argv[_idx + 1]
|
||||
del sys.argv[_idx:_idx + 2]
|
||||
if os.path.isabs(_raw_config_path):
|
||||
set_dashboard_config_path_override(_raw_config_path)
|
||||
else:
|
||||
set_dashboard_config_path_override(os.path.join(get_config_path(), _raw_config_path))
|
||||
|
||||
# Handle --include-drafts flag (remove from sys.argv to preserve positional args)
|
||||
_include_drafts = "--include-drafts" in sys.argv
|
||||
if _include_drafts:
|
||||
sys.argv.remove("--include-drafts")
|
||||
|
||||
# --- Progress Bar Configuration ---
|
||||
# NOTE: BAR_N_FMT_WIDTH, BAR_TOTAL_FMT_WIDTH, BAR_TIME_WIDTH, BAR_RATE_WIDTH
|
||||
# are imported from eb_dashboard_constants.py (SINGLE SOURCE OF TRUTH)
|
||||
@@ -227,10 +186,8 @@ def new_token():
|
||||
finally:
|
||||
if attempt < ERROR_MAX_RETRY - 1:
|
||||
sleep(WAIT_BEFORE_RETRY)
|
||||
# Refresh token exhausted — attempt full re-login with stored credentials
|
||||
logging.warning("Refresh token exhausted. Attempting re-login with stored credentials.")
|
||||
_do_login(_stored_username, _stored_password)
|
||||
logging.info("Re-login successful. New tokens acquired.")
|
||||
logging.critical("Persistent error in refresh_token")
|
||||
raise httpx.RequestError(message="Persistent error in refresh_token")
|
||||
|
||||
|
||||
def api_call_with_retry(func):
|
||||
@@ -255,10 +212,7 @@ def api_call_with_retry(func):
|
||||
|
||||
if isinstance(exc, httpx.HTTPStatusError) and exc.response.status_code == 401:
|
||||
logging.info(f"Token expired for {func_name}. Refreshing token.")
|
||||
try:
|
||||
new_token()
|
||||
except (httpx.RequestError, httpx.HTTPStatusError) as token_exc:
|
||||
logging.warning(f"Token refresh/re-login failed for {func_name}: {token_exc}")
|
||||
new_token()
|
||||
|
||||
if attempt < ERROR_MAX_RETRY - 1:
|
||||
sleep(WAIT_BEFORE_RETRY)
|
||||
@@ -271,41 +225,32 @@ def api_call_with_retry(func):
|
||||
sleep(WAIT_BEFORE_NEW_BATCH_OF_RETRIES)
|
||||
break # Exit for loop to restart batch in while True
|
||||
else:
|
||||
# All automatic batches exhausted — apply on_retry_exhausted policy
|
||||
# All automatic batches exhausted, ask the user
|
||||
with _user_interaction_lock:
|
||||
if on_retry_exhausted == "ignore":
|
||||
console.print(f"\n[bold red]Persistent error in {func_name} after {batch_count} batches ({total_attempts} attempts).[/bold red]")
|
||||
console.print(f"[red]Exception: {exc}[/red]")
|
||||
|
||||
choice = questionary.select(
|
||||
f"What would you like to do for {func_name}?",
|
||||
choices=[
|
||||
"Retry (try another batch of retries)",
|
||||
"Ignore (return None and continue)",
|
||||
"Stop script (critical error)"
|
||||
]
|
||||
).ask()
|
||||
|
||||
if choice == "Retry (try another batch of retries)":
|
||||
logging.info(f"User chose to retry {func_name}. Restarting batch sequence.")
|
||||
batch_count = 1 # Reset batch counter for the next interactive round
|
||||
break # Exit for loop to restart batch in while True
|
||||
elif choice == "Ignore (return None and continue)":
|
||||
# Retrieve context if available
|
||||
ctx = getattr(thread_local_storage, "current_patient_context", {"id": "Unknown", "pseudo": "Unknown"})
|
||||
logging.warning(f"[AUTO-IGNORE] Skipping {func_name} for Patient {ctx['id']} ({ctx['pseudo']}). Error: {exc}")
|
||||
logging.warning(f"[IGNORE] User opted to skip {func_name} for Patient {ctx['id']} ({ctx['pseudo']}). Error: {exc}")
|
||||
return None
|
||||
|
||||
elif on_retry_exhausted == "abort":
|
||||
logging.critical(f"[AUTO-ABORT] Stopping script after persistent error in {func_name}. Error: {exc}")
|
||||
raise httpx.RequestError(message=f"Persistent error in {func_name} (auto-aborted)")
|
||||
|
||||
else: # "ask" — display error then interactive prompt
|
||||
console.print(f"\n[bold red]Persistent error in {func_name} after {batch_count} batches ({total_attempts} attempts).[/bold red]")
|
||||
console.print(f"[red]Exception: {exc}[/red]")
|
||||
|
||||
choice = questionary.select(
|
||||
f"What would you like to do for {func_name}?",
|
||||
choices=[
|
||||
"Retry (try another batch of retries)",
|
||||
"Ignore (return None and continue)",
|
||||
"Stop script (critical error)"
|
||||
]
|
||||
).ask()
|
||||
|
||||
if choice == "Retry (try another batch of retries)":
|
||||
logging.info(f"User chose to retry {func_name}. Restarting batch sequence.")
|
||||
batch_count = 1 # Reset batch counter for the next interactive round
|
||||
break # Exit for loop to restart batch in while True
|
||||
elif choice == "Ignore (return None and continue)":
|
||||
ctx = getattr(thread_local_storage, "current_patient_context", {"id": "Unknown", "pseudo": "Unknown"})
|
||||
logging.warning(f"[IGNORE] User opted to skip {func_name} for Patient {ctx['id']} ({ctx['pseudo']}). Error: {exc}")
|
||||
return None
|
||||
else:
|
||||
logging.critical(f"User chose to stop script after persistent error in {func_name}.")
|
||||
raise httpx.RequestError(message=f"Persistent error in {func_name} (stopped by user)")
|
||||
else:
|
||||
logging.critical(f"User chose to stop script after persistent error in {func_name}.")
|
||||
raise httpx.RequestError(message=f"Persistent error in {func_name} (stopped by user)")
|
||||
|
||||
return wrapper
|
||||
|
||||
@@ -314,37 +259,8 @@ def api_call_with_retry(func):
|
||||
# BLOCK 3: AUTHENTICATION
|
||||
# ============================================================================
|
||||
|
||||
def _do_login(username, password):
|
||||
"""Performs the two-step authentication (IAM → RC) with the given credentials.
|
||||
Updates global access_token and refresh_token on success.
|
||||
Raises httpx.RequestError or httpx.HTTPStatusError on failure.
|
||||
Must NOT acquire _token_refresh_lock (caller's responsibility).
|
||||
"""
|
||||
global access_token, refresh_token
|
||||
|
||||
client = get_httpx_client()
|
||||
client.base_url = IAM_URL
|
||||
response = client.post(API_AUTH_LOGIN_ENDPOINT,
|
||||
json={"username": username, "password": password},
|
||||
timeout=60)
|
||||
response.raise_for_status()
|
||||
master_token = response.json()["access_token"]
|
||||
user_id = response.json()["userId"]
|
||||
|
||||
client = get_httpx_client()
|
||||
client.base_url = RC_URL
|
||||
response = client.post(API_AUTH_CONFIG_TOKEN_ENDPOINT,
|
||||
headers={"Authorization": f"Bearer {master_token}"},
|
||||
json={"userId": user_id, "clientId": RC_APP_ID,
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"},
|
||||
timeout=20)
|
||||
response.raise_for_status()
|
||||
access_token = response.json()["access_token"]
|
||||
refresh_token = response.json()["refresh_token"]
|
||||
|
||||
|
||||
def login():
|
||||
global _stored_username, _stored_password
|
||||
global access_token, refresh_token
|
||||
|
||||
user_name = (questionary.text("login :", default=DEFAULT_USER_NAME).ask())
|
||||
password = (questionary.password("password :", default=DEFAULT_PASSWORD).ask())
|
||||
@@ -352,18 +268,42 @@ def login():
|
||||
return "Exit"
|
||||
|
||||
try:
|
||||
_do_login(user_name, password)
|
||||
client = get_httpx_client()
|
||||
client.base_url = IAM_URL
|
||||
response = client.post(API_AUTH_LOGIN_ENDPOINT, json={"username": user_name, "password": password},
|
||||
timeout=20)
|
||||
response.raise_for_status()
|
||||
master_token = response.json()["access_token"]
|
||||
user_id = response.json()["userId"]
|
||||
except httpx.RequestError as exc:
|
||||
print(f"Login Error : {exc}")
|
||||
logging.warning(f"Login Error : {exc}")
|
||||
return "Error"
|
||||
except httpx.HTTPStatusError as exc:
|
||||
print(f"Login Error : {exc.response.status_code} for Url {exc.request.url}")
|
||||
logging.warning(f"Login Error : {exc.response.status_code} for Url {exc.request.url}")
|
||||
logging.warning(
|
||||
f"Login Error : {exc.response.status_code} for Url {exc.request.url}")
|
||||
return "Error"
|
||||
|
||||
try:
|
||||
client = get_httpx_client()
|
||||
client.base_url = RC_URL
|
||||
response = client.post(API_AUTH_CONFIG_TOKEN_ENDPOINT, headers={"Authorization": f"Bearer {master_token}"},
|
||||
json={"userId": user_id, "clientId": RC_APP_ID,
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"},
|
||||
timeout=20)
|
||||
response.raise_for_status()
|
||||
access_token = response.json()["access_token"]
|
||||
refresh_token = response.json()["refresh_token"]
|
||||
except httpx.RequestError as exc:
|
||||
print(f"Login Error : {exc}")
|
||||
logging.warning(f"Login Error : {exc}")
|
||||
return "Error"
|
||||
except httpx.HTTPStatusError as exc:
|
||||
print(f"Login Error : {exc.response.status_code} for Url {exc.request.url}")
|
||||
logging.warning(f"Login Error : {exc}")
|
||||
return "Error"
|
||||
|
||||
_stored_username = user_name
|
||||
_stored_password = password
|
||||
print()
|
||||
print("Login Success")
|
||||
return "Success"
|
||||
@@ -373,100 +313,6 @@ def login():
|
||||
# BLOCK 3B: FILE UTILITIES
|
||||
# ============================================================================
|
||||
|
||||
def ask_on_retry_exhausted():
|
||||
"""Asks the user what to do when all API retry batches are exhausted."""
|
||||
global on_retry_exhausted
|
||||
choice = questionary.select(
|
||||
"On retry exhausted :",
|
||||
choices=[
|
||||
"Ask (interactive prompt)",
|
||||
"Ignore (return None and continue)",
|
||||
"Abort (stop script)"
|
||||
]
|
||||
).ask()
|
||||
if choice is None or choice == "Ask (interactive prompt)":
|
||||
on_retry_exhausted = "ask"
|
||||
elif choice == "Ignore (return None and continue)":
|
||||
on_retry_exhausted = "ignore"
|
||||
else:
|
||||
on_retry_exhausted = "abort"
|
||||
|
||||
|
||||
def ask_fetch_six_month_visit():
|
||||
"""Asks the user whether to fetch 6-month visit data (slow API call, ~5s per patient)."""
|
||||
global fetch_six_month_visit
|
||||
choice = questionary.select(
|
||||
"Fetch 6-month visit progress data? (slow, ~5s per patient) :",
|
||||
choices=[
|
||||
"No (skip, faster execution)",
|
||||
"Yes (fetch 6-month visit data)"
|
||||
]
|
||||
).ask()
|
||||
fetch_six_month_visit = (choice == "Yes (fetch 6-month visit data)")
|
||||
|
||||
|
||||
def wait_for_scheduled_launch():
|
||||
"""Asks the user when to start the processing and waits if needed.
|
||||
Options: Immediately / In X minutes / At HH:MM
|
||||
"""
|
||||
choice = questionary.select(
|
||||
"When to start processing ?",
|
||||
choices=["Immediately", "In X minutes", "At HH:MM"]
|
||||
).ask()
|
||||
|
||||
if choice is None or choice == "Immediately":
|
||||
return
|
||||
|
||||
if choice == "In X minutes":
|
||||
minutes_str = questionary.text(
|
||||
"Number of minutes :",
|
||||
validate=lambda x: x.isdigit() and int(x) > 0
|
||||
).ask()
|
||||
if not minutes_str:
|
||||
return
|
||||
target_time = datetime.now() + timedelta(minutes=int(minutes_str))
|
||||
|
||||
else: # "At HH:MM"
|
||||
time_str = questionary.text(
|
||||
"Start time (HH:MM) :",
|
||||
validate=lambda x: bool(re.match(r'^\d{2}:\d{2}$', x)) and
|
||||
0 <= int(x.split(':')[0]) <= 23 and
|
||||
0 <= int(x.split(':')[1]) <= 59
|
||||
).ask()
|
||||
if not time_str:
|
||||
return
|
||||
now = datetime.now()
|
||||
h, m = int(time_str.split(':')[0]), int(time_str.split(':')[1])
|
||||
target_time = now.replace(hour=h, minute=m, second=0, microsecond=0)
|
||||
if target_time <= now:
|
||||
console.print("[yellow]⚠ Specified time is already past. Starting immediately.[/yellow]")
|
||||
return
|
||||
|
||||
print()
|
||||
try:
|
||||
while True:
|
||||
remaining = target_time - datetime.now()
|
||||
if remaining.total_seconds() <= 0:
|
||||
break
|
||||
total_secs = int(remaining.total_seconds())
|
||||
h = total_secs // 3600
|
||||
m = (total_secs % 3600) // 60
|
||||
s = total_secs % 60
|
||||
target_str = target_time.strftime('%H:%M:%S')
|
||||
print(f"\r Starting in {h:02d}:{m:02d}:{s:02d}... (at {target_str}) — Ctrl+C to cancel ",
|
||||
end="", flush=True)
|
||||
sleep(1)
|
||||
# Flush keyboard buffer to prevent stray keystrokes from polluting subsequent prompts
|
||||
while msvcrt.kbhit():
|
||||
msvcrt.getwch()
|
||||
print()
|
||||
console.print("[green]✓ Starting processing.[/green]")
|
||||
except KeyboardInterrupt:
|
||||
print()
|
||||
console.print("[bold red]Launch cancelled by user.[/bold red]")
|
||||
raise SystemExit(0)
|
||||
|
||||
|
||||
def load_json_file(filename):
|
||||
"""
|
||||
Load a JSON file from disk.
|
||||
@@ -494,7 +340,7 @@ def load_json_file(filename):
|
||||
def load_inclusions_mapping_config():
|
||||
"""Loads and validates the inclusions mapping configuration from the Excel file."""
|
||||
global inclusions_mapping_config
|
||||
config_path = get_dashboard_config_path(DASHBOARD_CONFIG_FILE_NAME)
|
||||
config_path = os.path.join(get_config_path(), DASHBOARD_CONFIG_FILE_NAME)
|
||||
|
||||
try:
|
||||
# Load with data_only=True to read calculated values instead of formulas
|
||||
@@ -596,7 +442,7 @@ def load_inclusions_mapping_config():
|
||||
def load_organizations_mapping_config():
|
||||
"""Loads and validates the organizations mapping configuration from the Excel file."""
|
||||
global organizations_mapping_config
|
||||
config_path = get_dashboard_config_path(DASHBOARD_CONFIG_FILE_NAME)
|
||||
config_path = os.path.join(get_config_path(), DASHBOARD_CONFIG_FILE_NAME)
|
||||
|
||||
try:
|
||||
# Load with data_only=True to read calculated values instead of formulas
|
||||
@@ -654,12 +500,6 @@ def _find_questionnaire_by_id(qcm_dict, qcm_id):
|
||||
if not isinstance(qcm_dict, dict):
|
||||
return None
|
||||
qcm_data = qcm_dict.get(qcm_id)
|
||||
if qcm_data and qcm_data.get("_count", 1) > 1:
|
||||
ctx = getattr(thread_local_storage, "current_patient_context", {"id": "Unknown", "pseudo": "Unknown"})
|
||||
logging.error(
|
||||
f"[DUPLICATE QCM] Patient {ctx['id']} ({ctx['pseudo']}): "
|
||||
f"Questionnaire id='{qcm_id}' appeared {qcm_data['_count']} times in API response — using last received copy"
|
||||
)
|
||||
return qcm_data.get("answers") if qcm_data else None
|
||||
|
||||
|
||||
@@ -667,32 +507,20 @@ def _find_questionnaire_by_name(qcm_dict, name):
|
||||
"""Finds a questionnaire by name (sequential search, returns first match)."""
|
||||
if not isinstance(qcm_dict, dict):
|
||||
return None
|
||||
matches = [qcm for qcm in qcm_dict.values()
|
||||
if get_nested_value(qcm, ["questionnaire", "name"]) == name]
|
||||
if len(matches) > 1:
|
||||
ctx = getattr(thread_local_storage, "current_patient_context", {"id": "Unknown", "pseudo": "Unknown"})
|
||||
ids = [get_nested_value(q, ["questionnaire", "id"]) for q in matches]
|
||||
logging.error(
|
||||
f"[DUPLICATE QCM] Patient {ctx['id']} ({ctx['pseudo']}): "
|
||||
f"Questionnaire name='{name}' matches {len(matches)} entries (ids: {ids}) — returning first match"
|
||||
)
|
||||
return matches[0].get("answers") if matches else None
|
||||
for qcm in qcm_dict.values():
|
||||
if get_nested_value(qcm, ["questionnaire", "name"]) == name:
|
||||
return qcm.get("answers")
|
||||
return None
|
||||
|
||||
|
||||
def _find_questionnaire_by_category(qcm_dict, category):
|
||||
"""Finds a questionnaire by category (sequential search, returns first match)."""
|
||||
if not isinstance(qcm_dict, dict):
|
||||
return None
|
||||
matches = [qcm for qcm in qcm_dict.values()
|
||||
if get_nested_value(qcm, ["questionnaire", "category"]) == category]
|
||||
if len(matches) > 1:
|
||||
ctx = getattr(thread_local_storage, "current_patient_context", {"id": "Unknown", "pseudo": "Unknown"})
|
||||
ids = [get_nested_value(q, ["questionnaire", "id"]) for q in matches]
|
||||
logging.error(
|
||||
f"[DUPLICATE QCM] Patient {ctx['id']} ({ctx['pseudo']}): "
|
||||
f"Questionnaire category='{category}' matches {len(matches)} entries (ids: {ids}) — returning first match"
|
||||
)
|
||||
return matches[0].get("answers") if matches else None
|
||||
for qcm in qcm_dict.values():
|
||||
if get_nested_value(qcm, ["questionnaire", "category"]) == category:
|
||||
return qcm.get("answers")
|
||||
return None
|
||||
|
||||
|
||||
def _get_field_value_from_questionnaire(all_questionnaires, field_config):
|
||||
@@ -729,13 +557,6 @@ def get_value_from_inclusion(inclusion_dict, key):
|
||||
|
||||
def _execute_custom_function(function_name, args, output_inclusion):
|
||||
"""Executes a custom function for a calculated field."""
|
||||
|
||||
def dominant_no_value(has_undef, has_na):
|
||||
"""Returns the dominant sentinel: 'undefined' > 'N/A' > None (real value present)."""
|
||||
if has_undef: return "undefined"
|
||||
if has_na: return "N/A"
|
||||
return None
|
||||
|
||||
if function_name == "search_in_fields_using_regex":
|
||||
if not args or len(args) < 2:
|
||||
return "$$$$ Argument Error: search_in_fields_using_regex requires at least 2 arguments"
|
||||
@@ -744,19 +565,16 @@ def _execute_custom_function(function_name, args, output_inclusion):
|
||||
field_names = args[1:]
|
||||
|
||||
field_values = []
|
||||
has_undefined = False
|
||||
has_na = False
|
||||
all_undefined = True
|
||||
|
||||
for field_name in field_names:
|
||||
value = get_value_from_inclusion(output_inclusion, field_name)
|
||||
field_values.append(value)
|
||||
if value is None or value == "undefined":
|
||||
has_undefined = True
|
||||
elif value == "N/A":
|
||||
has_na = True
|
||||
if value is not None and value != "undefined":
|
||||
all_undefined = False
|
||||
|
||||
if not any(v not in (None, "undefined", "N/A") for v in field_values):
|
||||
return dominant_no_value(has_undefined, has_na)
|
||||
if all_undefined:
|
||||
return "undefined"
|
||||
|
||||
try:
|
||||
for value in field_values:
|
||||
@@ -777,8 +595,6 @@ def _execute_custom_function(function_name, args, output_inclusion):
|
||||
|
||||
if value is None or value == "undefined":
|
||||
return "undefined"
|
||||
if value == "N/A":
|
||||
return "N/A"
|
||||
|
||||
match = re.search(r'\((.*?)\)', str(value))
|
||||
return match.group(1) if match else "undefined"
|
||||
@@ -792,8 +608,6 @@ def _execute_custom_function(function_name, args, output_inclusion):
|
||||
|
||||
if status is None or status == "undefined":
|
||||
return "undefined"
|
||||
if status == "N/A":
|
||||
return "N/A"
|
||||
|
||||
if not isinstance(is_terminated, bool) or not is_terminated:
|
||||
return status
|
||||
@@ -803,8 +617,7 @@ def _execute_custom_function(function_name, args, output_inclusion):
|
||||
elif function_name == "if_then_else":
|
||||
# Unified conditional function
|
||||
# Syntax: ["operator", arg1, arg2_optional, result_if_true, result_if_false]
|
||||
# Operators: "is_true", "is_false", "all_true", "any_true", "is_defined", "is_undefined", "all_defined", "==", "!=", ">", ">=", "<", "<="
|
||||
# Sentinel propagation: "undefined" (unknown) > "N/A" (not applicable) > real value.
|
||||
# Operators: "is_true", "is_false", "all_true", "is_defined", "is_undefined", "all_defined", "==", "!="
|
||||
|
||||
if not args or len(args) < 4:
|
||||
return "$$$$ Argument Error: if_then_else requires at least 4 arguments"
|
||||
@@ -832,8 +645,6 @@ def _execute_custom_function(function_name, args, output_inclusion):
|
||||
value = resolve_value(args[1])
|
||||
if value is None or value == "undefined":
|
||||
return "undefined"
|
||||
if value == "N/A":
|
||||
return "N/A"
|
||||
condition = (value is True)
|
||||
result_if_true = resolve_value(args[2])
|
||||
result_if_false = resolve_value(args[3])
|
||||
@@ -844,8 +655,6 @@ def _execute_custom_function(function_name, args, output_inclusion):
|
||||
value = resolve_value(args[1])
|
||||
if value is None or value == "undefined":
|
||||
return "undefined"
|
||||
if value == "N/A":
|
||||
return "N/A"
|
||||
condition = (value is False)
|
||||
result_if_true = resolve_value(args[2])
|
||||
result_if_false = resolve_value(args[3])
|
||||
@@ -857,58 +666,22 @@ def _execute_custom_function(function_name, args, output_inclusion):
|
||||
if not isinstance(fields_arg, list):
|
||||
return "$$$$ Argument Error: all_true requires arg1 to be a list of field names"
|
||||
|
||||
has_undefined = False
|
||||
has_na = False
|
||||
conditions = []
|
||||
for field_name in fields_arg:
|
||||
field_value = get_value_from_inclusion(output_inclusion, field_name)
|
||||
if field_value is None or field_value == "undefined":
|
||||
has_undefined = True
|
||||
elif field_value == "N/A":
|
||||
has_na = True
|
||||
else:
|
||||
conditions.append(field_value)
|
||||
|
||||
status = dominant_no_value(has_undefined, has_na)
|
||||
if status:
|
||||
return status
|
||||
return "undefined"
|
||||
conditions.append(field_value)
|
||||
|
||||
condition = all(conditions)
|
||||
result_if_true = resolve_value(args[2])
|
||||
result_if_false = resolve_value(args[3])
|
||||
|
||||
elif operator == "any_true":
|
||||
# OR semantics: returns "undefined" only if ALL operands are undefined
|
||||
if len(args) != 4:
|
||||
return "$$$$ Argument Error: any_true requires 4 arguments"
|
||||
fields_arg = args[1]
|
||||
if not isinstance(fields_arg, list):
|
||||
return "$$$$ Argument Error: any_true requires arg1 to be a list of field names"
|
||||
|
||||
has_undefined = False
|
||||
has_na = False
|
||||
resolved = []
|
||||
for field_name in fields_arg:
|
||||
field_value = get_value_from_inclusion(output_inclusion, field_name)
|
||||
if field_value is None or field_value == "undefined":
|
||||
has_undefined = True
|
||||
elif field_value == "N/A":
|
||||
has_na = True
|
||||
else:
|
||||
resolved.append(field_value)
|
||||
|
||||
if not resolved:
|
||||
return dominant_no_value(has_undefined, has_na)
|
||||
|
||||
condition = any(resolved)
|
||||
result_if_true = resolve_value(args[2])
|
||||
result_if_false = resolve_value(args[3])
|
||||
|
||||
elif operator == "is_defined":
|
||||
if len(args) != 4:
|
||||
return "$$$$ Argument Error: is_defined requires 4 arguments"
|
||||
value = resolve_value(args[1])
|
||||
condition = (value is not None and value != "undefined" and value != "N/A")
|
||||
condition = (value is not None and value != "undefined")
|
||||
result_if_true = resolve_value(args[2])
|
||||
result_if_false = resolve_value(args[3])
|
||||
|
||||
@@ -929,7 +702,7 @@ def _execute_custom_function(function_name, args, output_inclusion):
|
||||
|
||||
for field_name in fields_arg:
|
||||
field_value = get_value_from_inclusion(output_inclusion, field_name)
|
||||
if field_value is None or field_value == "undefined" or field_value == "N/A":
|
||||
if field_value is None or field_value == "undefined":
|
||||
condition = False
|
||||
break
|
||||
else:
|
||||
@@ -944,12 +717,8 @@ def _execute_custom_function(function_name, args, output_inclusion):
|
||||
value1 = resolve_value(args[1])
|
||||
value2 = resolve_value(args[2])
|
||||
|
||||
v1_undef = value1 is None or value1 == "undefined"
|
||||
v2_undef = value2 is None or value2 == "undefined"
|
||||
status = dominant_no_value(v1_undef or v2_undef,
|
||||
value1 == "N/A" or value2 == "N/A")
|
||||
if status:
|
||||
return status
|
||||
if value1 is None or value1 == "undefined" or value2 is None or value2 == "undefined":
|
||||
return "undefined"
|
||||
|
||||
condition = (value1 == value2)
|
||||
result_if_true = resolve_value(args[3])
|
||||
@@ -961,58 +730,13 @@ def _execute_custom_function(function_name, args, output_inclusion):
|
||||
value1 = resolve_value(args[1])
|
||||
value2 = resolve_value(args[2])
|
||||
|
||||
v1_undef = value1 is None or value1 == "undefined"
|
||||
v2_undef = value2 is None or value2 == "undefined"
|
||||
status = dominant_no_value(v1_undef or v2_undef,
|
||||
value1 == "N/A" or value2 == "N/A")
|
||||
if status:
|
||||
return status
|
||||
if value1 is None or value1 == "undefined" or value2 is None or value2 == "undefined":
|
||||
return "undefined"
|
||||
|
||||
condition = (value1 != value2)
|
||||
result_if_true = resolve_value(args[3])
|
||||
result_if_false = resolve_value(args[4])
|
||||
|
||||
elif operator in (">", ">=", "<", "<="):
|
||||
if len(args) != 5:
|
||||
return f"$$$$ Argument Error: {operator} requires 5 arguments"
|
||||
value1 = resolve_value(args[1])
|
||||
value2 = resolve_value(args[2])
|
||||
|
||||
v1_undef = value1 is None or value1 == "undefined"
|
||||
v2_undef = value2 is None or value2 == "undefined"
|
||||
status = dominant_no_value(v1_undef or v2_undef,
|
||||
value1 == "N/A" or value2 == "N/A")
|
||||
if status:
|
||||
return status
|
||||
|
||||
# Si l'un est numérique, tenter de convertir l'autre ; sinon comparer en string
|
||||
cmp1, cmp2 = value1, value2
|
||||
if isinstance(value1, (int, float)) and not isinstance(value2, (int, float)):
|
||||
try:
|
||||
cmp2 = float(value2)
|
||||
except (ValueError, TypeError):
|
||||
cmp1 = str(value1)
|
||||
elif isinstance(value2, (int, float)) and not isinstance(value1, (int, float)):
|
||||
try:
|
||||
cmp1 = float(value1)
|
||||
except (ValueError, TypeError):
|
||||
cmp2 = str(value2)
|
||||
|
||||
try:
|
||||
if operator == ">":
|
||||
condition = (cmp1 > cmp2)
|
||||
elif operator == ">=":
|
||||
condition = (cmp1 >= cmp2)
|
||||
elif operator == "<":
|
||||
condition = (cmp1 < cmp2)
|
||||
else:
|
||||
condition = (cmp1 <= cmp2)
|
||||
except TypeError:
|
||||
return f"$$$$ Comparison Error: cannot compare {type(cmp1).__name__} and {type(cmp2).__name__}"
|
||||
|
||||
result_if_true = resolve_value(args[3])
|
||||
result_if_false = resolve_value(args[4])
|
||||
|
||||
else:
|
||||
return f"$$$$ Unknown Operator: {operator}"
|
||||
|
||||
@@ -1035,8 +759,6 @@ def process_inclusions_mapping(output_inclusion, inclusion_data, record_data, re
|
||||
|
||||
if condition_value is None or condition_value == "undefined":
|
||||
final_value = "undefined"
|
||||
elif condition_value == "N/A":
|
||||
final_value = "N/A"
|
||||
elif not isinstance(condition_value, bool):
|
||||
final_value = "$$$$ Condition Field Error"
|
||||
elif not condition_value:
|
||||
@@ -1240,13 +962,6 @@ def get_all_questionnaires_by_patient(patient_id, record_data):
|
||||
response.raise_for_status()
|
||||
response_data = response.json()
|
||||
|
||||
# First pass: count occurrences of each q_id to detect duplicates at lookup time
|
||||
q_id_counts = {}
|
||||
for item in response_data:
|
||||
q_id = get_nested_value(item, path=["questionnaire", "id"])
|
||||
if q_id:
|
||||
q_id_counts[q_id] = q_id_counts.get(q_id, 0) + 1
|
||||
|
||||
# Build dictionary with questionnaire metadata for searching
|
||||
results = {}
|
||||
for item in response_data:
|
||||
@@ -1254,17 +969,6 @@ def get_all_questionnaires_by_patient(patient_id, record_data):
|
||||
q_name = get_nested_value(item, path=["questionnaire", "name"])
|
||||
q_category = get_nested_value(item, path=["questionnaire", "category"])
|
||||
answers = get_nested_value(item, path=["answers"], default={})
|
||||
|
||||
# ── DRAFT ANSWERS FILTER ────────────────────────────────────────────────
|
||||
# "Draft" answers have a missing or mismatched subject (patient id):
|
||||
# they are not yet submitted and should not be treated as valid patient data.
|
||||
# Active by default; use --include-drafts CLI flag to disable.
|
||||
if not _include_drafts:
|
||||
answers_patient = answers.get("subject") if isinstance(answers, dict) else None
|
||||
if not answers_patient or answers_patient != patient_id:
|
||||
answers = {}
|
||||
# ────────────────────────────────────────────────────────────────────────
|
||||
|
||||
if q_id:
|
||||
results[q_id] = {
|
||||
"questionnaire": {
|
||||
@@ -1272,8 +976,7 @@ def get_all_questionnaires_by_patient(patient_id, record_data):
|
||||
"name": q_name,
|
||||
"category": q_category
|
||||
},
|
||||
"answers": answers,
|
||||
"_count": q_id_counts[q_id]
|
||||
"answers": answers
|
||||
}
|
||||
return results
|
||||
|
||||
@@ -1432,12 +1135,8 @@ def _process_inclusion_data(inclusion, organization):
|
||||
output_inclusion = {}
|
||||
|
||||
# --- Prepare all data sources ---
|
||||
# 1. Launch Visit Search asynchronously (it's slow, ~5s) — only if enabled by user
|
||||
# We use run_with_context to pass the patient identity to the new thread
|
||||
if fetch_six_month_visit:
|
||||
visit_future = subtasks_thread_pool.submit(run_with_context, search_visit_by_pseudo_and_order, ctx, pseudo, 2)
|
||||
else:
|
||||
visit_future = None
|
||||
# 1. 6-month visit loading disabled on this branch (No-6-Month-Visit)
|
||||
# visit_future = subtasks_thread_pool.submit(run_with_context, search_visit_by_pseudo_and_order, ctx, pseudo, 2)
|
||||
|
||||
# 2. Prepare inclusion_data: enrich inclusion with organization info
|
||||
inclusion_data = dict(inclusion)
|
||||
@@ -1461,11 +1160,8 @@ def _process_inclusion_data(inclusion, organization):
|
||||
logging.error(f"Error fetching request data for patient {patient_id}: {e}")
|
||||
request_data = None
|
||||
|
||||
try:
|
||||
six_month_visit_data = visit_future.result() if visit_future is not None else {}
|
||||
except Exception as e:
|
||||
logging.error(f"Error searching 6-month visit for patient {pseudo}: {e}")
|
||||
six_month_visit_data = None
|
||||
# 6-month visit loading disabled on this branch (No-6-Month-Visit)
|
||||
six_month_visit_data = None
|
||||
|
||||
# --- Process all fields from configuration ---
|
||||
process_inclusions_mapping(output_inclusion, inclusion_data, record_data, request_data, all_questionnaires, six_month_visit_data)
|
||||
@@ -1519,7 +1215,7 @@ def main():
|
||||
load_organizations_mapping_config()
|
||||
|
||||
# Completely externalized Excel-only workflow
|
||||
export_excel_only(sys.argv, get_output_filename(INCLUSIONS_FILE_NAME), get_output_filename(ORGANIZATIONS_FILE_NAME),
|
||||
export_excel_only(sys.argv, INCLUSIONS_FILE_NAME, ORGANIZATIONS_FILE_NAME,
|
||||
inclusions_mapping_config, organizations_mapping_config)
|
||||
return
|
||||
|
||||
@@ -1532,19 +1228,10 @@ def main():
|
||||
if login_status == "Exit":
|
||||
return
|
||||
|
||||
print()
|
||||
ask_fetch_six_month_visit()
|
||||
|
||||
print()
|
||||
number_of_threads = int((questionary.text("Number of threads :", default="12",
|
||||
validate=lambda x: x.isdigit() and 0 < int(x) <= MAX_THREADS).ask()))
|
||||
|
||||
print()
|
||||
ask_on_retry_exhausted()
|
||||
|
||||
print()
|
||||
wait_for_scheduled_launch()
|
||||
|
||||
print()
|
||||
load_inclusions_mapping_config()
|
||||
load_organizations_mapping_config()
|
||||
@@ -1603,14 +1290,6 @@ def main():
|
||||
inclusions_total_count = sum(org.get('patients_count', 0) for org in organizations_list)
|
||||
organizations_list.sort(key=lambda org: (-org.get('patients_count', 0), org.get('name', '')))
|
||||
|
||||
# ╔══════════════════════════════════════════════════════════════════╗
|
||||
# ║ TEST INSTRUMENTATION — REMOVE BEFORE PRODUCTION ║
|
||||
# ║ Limits the table to organizations at ranks 33 and 34 ║
|
||||
# ║ (indices 32 and 33, after descending sort by patients_count) ║
|
||||
# ╚══════════════════════════════════════════════════════════════════╝
|
||||
# organizations_list = [org for i, org in enumerate(organizations_list) if i in (32, 33)]
|
||||
# ══════════════════════════════════════════════════════════════════
|
||||
|
||||
number_of_organizations = len(organizations_list)
|
||||
print(f"{inclusions_total_count} Inclusions in {number_of_organizations} Organizations...")
|
||||
print()
|
||||
@@ -1664,7 +1343,7 @@ def main():
|
||||
has_coherence_critical, has_regression_critical = run_quality_checks(
|
||||
current_inclusions=output_inclusions, # list: données en mémoire (nouvellement collectées)
|
||||
organizations_list=organizations_list, # list: données en mémoire avec compteurs
|
||||
old_inclusions_filename=get_output_filename(INCLUSIONS_FILE_NAME) # str: current file on disk (with suffix if any)
|
||||
old_inclusions_filename=INCLUSIONS_FILE_NAME # str: "endobest_inclusions.json" (version courante sur disque)
|
||||
)
|
||||
|
||||
# === CHECK FOR CRITICAL ISSUES AND ASK USER CONFIRMATION ===
|
||||
@@ -1689,9 +1368,9 @@ def main():
|
||||
# === WRITE NEW FILES ===
|
||||
print("Writing files...")
|
||||
|
||||
with open(get_output_filename(INCLUSIONS_FILE_NAME), 'w', encoding='utf-8') as f_json:
|
||||
with open(INCLUSIONS_FILE_NAME, 'w', encoding='utf-8') as f_json:
|
||||
json.dump(output_inclusions, f_json, indent=4, ensure_ascii=False)
|
||||
with open(get_output_filename(ORGANIZATIONS_FILE_NAME), 'w', encoding='utf-8') as f_json:
|
||||
with open(ORGANIZATIONS_FILE_NAME, 'w', encoding='utf-8') as f_json:
|
||||
json.dump(organizations_list, f_json, indent=4, ensure_ascii=False)
|
||||
|
||||
console.print("[green]✓ Data saved to JSON files[/green]")
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
@echo off
|
||||
eb_dashboard.exe --config "Endobest_Dashboard_Config - Debug.xlsx" --add-suffix %*
|
||||
@@ -1,3 +0,0 @@
|
||||
@echo off
|
||||
call C:\PythonProjects\.rcvenv\Scripts\activate.bat
|
||||
python eb_dashboard.py --config "Endobest_Dashboard_Config - Debug.xlsx" --add-suffix %*
|
||||
@@ -34,7 +34,7 @@ try:
|
||||
except ImportError:
|
||||
xw = None
|
||||
|
||||
from eb_dashboard_utils import get_nested_value, get_config_path, get_dashboard_config_path, get_output_filename
|
||||
from eb_dashboard_utils import get_nested_value, get_config_path
|
||||
from eb_dashboard_constants import (
|
||||
INCLUSIONS_FILE_NAME,
|
||||
ORGANIZATIONS_FILE_NAME,
|
||||
@@ -117,7 +117,7 @@ def load_excel_export_config(console_instance=None):
|
||||
if console_instance:
|
||||
console = console_instance
|
||||
|
||||
config_path = get_dashboard_config_path(DASHBOARD_CONFIG_FILE_NAME)
|
||||
config_path = os.path.join(get_config_path(), DASHBOARD_CONFIG_FILE_NAME)
|
||||
error_messages = []
|
||||
|
||||
try:
|
||||
@@ -399,7 +399,7 @@ def export_to_excel(inclusions_data, organizations_data, excel_config,
|
||||
output_template = workbook_config.get("output_file_name_template")
|
||||
if_output_exists = workbook_config.get("if_output_exists", OUTPUT_ACTION_OVERWRITE)
|
||||
|
||||
# Resolve output filename, then apply --add-suffix if provided
|
||||
# Resolve output filename
|
||||
try:
|
||||
output_filename = output_template.format(**template_vars)
|
||||
except KeyError as e:
|
||||
@@ -407,7 +407,6 @@ def export_to_excel(inclusions_data, organizations_data, excel_config,
|
||||
error_count += 1
|
||||
continue
|
||||
|
||||
output_filename = get_output_filename(output_filename)
|
||||
output_path = os.path.join(EXCEL_OUTPUT_FOLDER, output_filename)
|
||||
|
||||
# Log workbook processing start
|
||||
@@ -557,7 +556,7 @@ def _prepare_template_variables():
|
||||
"""
|
||||
# Get UTC timestamp from inclusions file
|
||||
# Use constant from eb_dashboard_constants (SINGLE SOURCE OF TRUTH)
|
||||
inclusions_file = get_output_filename(INCLUSIONS_FILE_NAME)
|
||||
inclusions_file = INCLUSIONS_FILE_NAME
|
||||
if os.path.exists(inclusions_file):
|
||||
file_mtime = os.path.getmtime(inclusions_file)
|
||||
extract_date_time_utc = datetime.fromtimestamp(file_mtime, tz=timezone.utc)
|
||||
@@ -755,11 +754,7 @@ def _apply_value_replacement(value, replacements):
|
||||
return value
|
||||
|
||||
for value_before, value_after in replacements:
|
||||
# bool is a subclass of int in Python (True == 1, False == 0):
|
||||
# reject the match if one side is bool and the other is not.
|
||||
if isinstance(value, bool) != isinstance(value_before, bool):
|
||||
continue
|
||||
if value == value_before:
|
||||
if value == value_before: # Strict equality
|
||||
return value_after
|
||||
|
||||
return value # No match, return original
|
||||
@@ -1922,9 +1917,9 @@ def export_excel_only(sys_argv,
|
||||
global console
|
||||
|
||||
if not inclusions_filename:
|
||||
inclusions_filename = get_output_filename(INCLUSIONS_FILE_NAME)
|
||||
inclusions_filename = INCLUSIONS_FILE_NAME
|
||||
if not organizations_filename:
|
||||
organizations_filename = get_output_filename(ORGANIZATIONS_FILE_NAME)
|
||||
organizations_filename = ORGANIZATIONS_FILE_NAME
|
||||
|
||||
print()
|
||||
console.print("[bold cyan]═══ EXCEL ONLY MODE ═══[/bold cyan]\n")
|
||||
@@ -2039,8 +2034,8 @@ def run_normal_mode_export(excel_enabled, excel_config,
|
||||
try:
|
||||
# Load JSONs from filesystem to ensure data consistency with what was written
|
||||
# Use constants imported from eb_dashboard_constants.py (SINGLE SOURCE OF TRUTH)
|
||||
inclusions_from_fs = _load_json_file_internal(get_output_filename(INCLUSIONS_FILE_NAME))
|
||||
organizations_from_fs = _load_json_file_internal(get_output_filename(ORGANIZATIONS_FILE_NAME))
|
||||
inclusions_from_fs = _load_json_file_internal(INCLUSIONS_FILE_NAME)
|
||||
organizations_from_fs = _load_json_file_internal(ORGANIZATIONS_FILE_NAME)
|
||||
|
||||
if inclusions_from_fs is None or organizations_from_fs is None:
|
||||
error_msg = "Could not load data files for Excel export"
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
@echo off
|
||||
eb_dashboard.exe --excel-only --config "Endobest_Dashboard_Config - Debug.xlsx" --add-suffix debug %*
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
@echo off
|
||||
call C:\PythonProjects\.rcvenv\Scripts\activate.bat
|
||||
python eb_dashboard.py --excel-only --config "Endobest_Dashboard_Config - Debug.xlsx" --add-suffix debug %*
|
||||
|
||||
@@ -17,7 +17,7 @@ import shutil
|
||||
|
||||
import openpyxl
|
||||
from rich.console import Console
|
||||
from eb_dashboard_utils import get_nested_value, get_old_filename as _get_old_filename, get_config_path, get_dashboard_config_path, get_output_filename
|
||||
from eb_dashboard_utils import get_nested_value, get_old_filename as _get_old_filename, get_config_path
|
||||
from eb_dashboard_constants import (
|
||||
INCLUSIONS_FILE_NAME,
|
||||
ORGANIZATIONS_FILE_NAME,
|
||||
@@ -93,7 +93,7 @@ def load_regression_check_config(console_instance=None):
|
||||
if console_instance:
|
||||
console = console_instance
|
||||
|
||||
config_path = get_dashboard_config_path(DASHBOARD_CONFIG_FILE_NAME)
|
||||
config_path = os.path.join(get_config_path(), DASHBOARD_CONFIG_FILE_NAME)
|
||||
|
||||
try:
|
||||
workbook = openpyxl.load_workbook(config_path)
|
||||
@@ -318,10 +318,10 @@ def run_check_only_mode(sys_argv):
|
||||
|
||||
# Run quality checks (will load all files internally)
|
||||
print()
|
||||
old_inclusions_file = _get_old_filename(get_output_filename(INCLUSIONS_FILE_NAME), OLD_FILE_SUFFIX)
|
||||
old_inclusions_file = _get_old_filename(INCLUSIONS_FILE_NAME, OLD_FILE_SUFFIX)
|
||||
has_coherence_critical, has_regression_critical = run_quality_checks(
|
||||
current_inclusions=get_output_filename(INCLUSIONS_FILE_NAME),
|
||||
organizations_list=get_output_filename(ORGANIZATIONS_FILE_NAME),
|
||||
current_inclusions=INCLUSIONS_FILE_NAME,
|
||||
organizations_list=ORGANIZATIONS_FILE_NAME,
|
||||
old_inclusions_filename=old_inclusions_file
|
||||
)
|
||||
|
||||
@@ -370,8 +370,8 @@ def backup_output_files():
|
||||
except Exception as e:
|
||||
logging.warning(f"Could not backup {source}: {e}")
|
||||
|
||||
_backup_file_silent(get_output_filename(INCLUSIONS_FILE_NAME), _get_old_filename(get_output_filename(INCLUSIONS_FILE_NAME), OLD_FILE_SUFFIX))
|
||||
_backup_file_silent(get_output_filename(ORGANIZATIONS_FILE_NAME), _get_old_filename(get_output_filename(ORGANIZATIONS_FILE_NAME), OLD_FILE_SUFFIX))
|
||||
_backup_file_silent(INCLUSIONS_FILE_NAME, _get_old_filename(INCLUSIONS_FILE_NAME, OLD_FILE_SUFFIX))
|
||||
_backup_file_silent(ORGANIZATIONS_FILE_NAME, _get_old_filename(ORGANIZATIONS_FILE_NAME, OLD_FILE_SUFFIX))
|
||||
|
||||
|
||||
# ============================================================================
|
||||
@@ -431,11 +431,6 @@ def coherence_check(output_inclusions, organizations_list):
|
||||
continue
|
||||
|
||||
patients += 1
|
||||
|
||||
# API statistics exclude non-consented from status sub-counters only
|
||||
if get_nested_value(inclusion, ["Inclusion", "Consent_Signed"]) is not True:
|
||||
continue
|
||||
|
||||
status = get_nested_value(inclusion, ["Inclusion", "Inclusion_Status"], default="")
|
||||
|
||||
if isinstance(status, str):
|
||||
|
||||
@@ -204,43 +204,6 @@ def get_config_path():
|
||||
return CONFIG_FOLDER_NAME
|
||||
|
||||
|
||||
_dashboard_config_path_override = None
|
||||
|
||||
|
||||
def set_dashboard_config_path_override(path):
|
||||
"""Sets a global override for the dashboard config file path (used by --config CLI arg)."""
|
||||
global _dashboard_config_path_override
|
||||
_dashboard_config_path_override = path
|
||||
|
||||
|
||||
def get_dashboard_config_path(config_file_name):
|
||||
"""Returns the dashboard config file path, respecting any --config CLI override."""
|
||||
if _dashboard_config_path_override:
|
||||
return _dashboard_config_path_override
|
||||
return os.path.join(get_config_path(), config_file_name)
|
||||
|
||||
|
||||
_output_file_suffix = None
|
||||
|
||||
|
||||
def set_output_file_suffix(suffix):
|
||||
"""Sets the global output file suffix (used by --add-suffix CLI arg)."""
|
||||
global _output_file_suffix
|
||||
_output_file_suffix = suffix
|
||||
|
||||
|
||||
def get_output_filename(base_name):
|
||||
"""Returns the output filename with the suffix inserted before the extension.
|
||||
|
||||
Example: get_output_filename("endobest_inclusions.json") with suffix "A"
|
||||
→ "endobest_inclusions_A.json"
|
||||
"""
|
||||
if not _output_file_suffix:
|
||||
return base_name
|
||||
name, ext = os.path.splitext(base_name)
|
||||
return f"{name}_{_output_file_suffix}{ext}"
|
||||
|
||||
|
||||
def get_old_filename(current_filename, old_suffix="_old"):
|
||||
"""Generate old backup filename from current filename.
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
@echo off
|
||||
eb_dashboard.exe --include-drafts --add-suffix with_drafts %*
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
@echo off
|
||||
call C:\PythonProjects\.rcvenv\Scripts\activate.bat
|
||||
python eb_dashboard.py --include-drafts --add-suffix with_drafts %*
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
@echo off
|
||||
eb_dashboard.exe --include-drafts --excel-only --add-suffix with_drafts %*
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
@echo off
|
||||
call C:\PythonProjects\.rcvenv\Scripts\activate.bat
|
||||
python eb_dashboard.py --include-drafts --excel-only --add-suffix with_drafts %*
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
@echo off
|
||||
eb_dashboard.exe --include-drafts --excel-only --config "Endobest_Dashboard_Config - Debug.xlsx" --add-suffix with_drafts_debug %*
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
@echo off
|
||||
call C:\PythonProjects\.rcvenv\Scripts\activate.bat
|
||||
python eb_dashboard.py --include-drafts --excel-only --config "Endobest_Dashboard_Config - Debug.xlsx" --add-suffix with_drafts_debug %*
|
||||
|
||||
Binary file not shown.
228230
endobest_inclusions.json
Normal file
228230
endobest_inclusions.json
Normal file
File diff suppressed because it is too large
Load Diff
248294
endobest_inclusions_old.json
Normal file
248294
endobest_inclusions_old.json
Normal file
File diff suppressed because it is too large
Load Diff
704
endobest_organizations.json
Normal file
704
endobest_organizations.json
Normal file
@@ -0,0 +1,704 @@
|
||||
[
|
||||
{
|
||||
"id": "5703b3d6-e415-40b0-90ea-b168835fd720",
|
||||
"name": "HOPITAL PRIVE NATECIA",
|
||||
"Center_Name": "Hôpital Privé Natécia",
|
||||
"patients_count": 169,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 167,
|
||||
"prematurely_terminated_count": 2
|
||||
},
|
||||
{
|
||||
"id": "1de71a30-840b-4c4b-84fc-281ce1b4a5e1",
|
||||
"name": "SANTE ATLANTIQUE",
|
||||
"Center_Name": "Clinique Santé Atlantique",
|
||||
"patients_count": 159,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 157,
|
||||
"prematurely_terminated_count": 1
|
||||
},
|
||||
{
|
||||
"id": "5e6d7afa-6532-495f-a84a-34f644aeaa0f",
|
||||
"name": "CLINIQUE BELLEDONNE",
|
||||
"Center_Name": "Clinique Belledonne",
|
||||
"patients_count": 158,
|
||||
"preincluded_count": 4,
|
||||
"included_count": 153,
|
||||
"prematurely_terminated_count": 1
|
||||
},
|
||||
{
|
||||
"id": "026a6d39-552f-44b9-8a2d-1ecd705f9e08",
|
||||
"name": "HOPITAL AMERICAIN",
|
||||
"Center_Name": "Hôpital Américain de Paris",
|
||||
"patients_count": 158,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 157,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "bf0f96c1-8bbc-4f2c-b360-4a5b27995a12",
|
||||
"name": "SA CLINIQUE TIVOLI-DUCOS",
|
||||
"Center_Name": "Clinique TIVOLI",
|
||||
"patients_count": 155,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 154,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "aba63d11-0dd7-40e8-a652-384c311bb358",
|
||||
"name": "HOPITAL LYON SUD - HOSPICES CIVILS DE LYON",
|
||||
"Center_Name": "Hôpital Lyon Sud - Hospices Civils de Lyon",
|
||||
"patients_count": 127,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 127,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "8488229d-f426-4bdd-923c-f638041b223c",
|
||||
"name": "HOPITAL JEANNE DE FLANDRE DU CHU DE LILLE",
|
||||
"Center_Name": "CHU de Lille - Hôpital Jeanne de Flandre",
|
||||
"patients_count": 91,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 90,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "31665f8d-0f46-44dc-931f-e3aed8879965",
|
||||
"name": "HOPITAL MAISON BLANCHE CHU REIMS",
|
||||
"Center_Name": "CHU de Reims - Hôpital Maison Blanche",
|
||||
"patients_count": 88,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 88,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "b77b301c-10fa-4eca-bb5b-51506766e158",
|
||||
"name": "HOPITAL CHARLES NICOLLE CHU ROUEN",
|
||||
"Center_Name": "CHU de Rouen Normandie - Hôpital Charles-Nicolle",
|
||||
"patients_count": 84,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 83,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "743d0a1a-7edf-4fe6-9d65-0c14363f2153",
|
||||
"name": "CENTRE HOSPITALIER UNIVERSITAIRE JEAN MINJOZ BESANCON",
|
||||
"Center_Name": "CHU Jean Minjoz Besançon - Pôle Mère-Femme",
|
||||
"patients_count": 81,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 81,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "855d7aab-9736-40b7-b9ff-dcc6438890ef",
|
||||
"name": "HOPITAL CROIX-ROUSSE - HOSPICES CIVILS DE LYON",
|
||||
"Center_Name": "Hôpital Croix-Rousse - Hospices Civils de Lyon",
|
||||
"patients_count": 68,
|
||||
"preincluded_count": 5,
|
||||
"included_count": 62,
|
||||
"prematurely_terminated_count": 1
|
||||
},
|
||||
{
|
||||
"id": "74a05936-8fb6-4662-8e95-fd1c91621bf2",
|
||||
"name": "GHU APHP SORBONNE UNIVERSITE SITE TENON",
|
||||
"Center_Name": "GHU APHP - Hôpital Tenon",
|
||||
"patients_count": 62,
|
||||
"preincluded_count": 5,
|
||||
"included_count": 56,
|
||||
"prematurely_terminated_count": 1
|
||||
},
|
||||
{
|
||||
"id": "76d2ebcd-5482-4c5d-8221-c94d1de7261e",
|
||||
"name": "CENTRE HOSPITALIER ANNECY-GENEVOIS SITE ANNECY",
|
||||
"Center_Name": "CH Annecy Genevois – Site d’Annecy",
|
||||
"patients_count": 60,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 60,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "006740fa-696c-4ffb-8b77-7a60d0e23617",
|
||||
"name": "HOPITAL PRIVÉ LE BOIS",
|
||||
"Center_Name": "Hôpital Privé le Bois - Lille",
|
||||
"patients_count": 60,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 59,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "a4b3dfea-1220-4987-b287-f58fde1d0ee5",
|
||||
"name": "CENTRE HOSPITALIER UNIVERSITAIRE COTE DE NACRE",
|
||||
"Center_Name": "CHU de Caen",
|
||||
"patients_count": 59,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 59,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "4aef30cb-778b-409a-b6e2-898bfd91c850",
|
||||
"name": "CENTRE HOSPITALIER GENERAL DE VALENCIENNES",
|
||||
"Center_Name": "CH de Valenciennes",
|
||||
"patients_count": 57,
|
||||
"preincluded_count": 3,
|
||||
"included_count": 54,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "f7d3526b-18d6-43af-b52c-5ca23bac798e",
|
||||
"name": "CENTRE HOSPITALIER REGIONAL D ANGERS",
|
||||
"Center_Name": "CHR d’Angers",
|
||||
"patients_count": 50,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 50,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "1fd6c63b-d909-45d8-8c70-ecb12501c1d1",
|
||||
"name": "HOPITAL DE HAUTEPIERRE",
|
||||
"Center_Name": "CHRU Strasbourg - Hôpital de Hautepierre",
|
||||
"patients_count": 44,
|
||||
"preincluded_count": 2,
|
||||
"included_count": 42,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "d17ddcc4-64db-4cf5-83c5-146f9a060254",
|
||||
"name": "CENTRE HOSPITALIER DE CALAIS.",
|
||||
"Center_Name": "CH de Calais",
|
||||
"patients_count": 42,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 42,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "f6665185-b1ae-4847-a378-652d35158cee",
|
||||
"name": "CLINIQUE BELHARRA",
|
||||
"Center_Name": "Clinique Belharra",
|
||||
"patients_count": 40,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 40,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "fe25553c-4894-4291-b303-c19d2a1c6f0f",
|
||||
"name": "CHU SITE FELIX GUYON (SAINT DENIS)",
|
||||
"Center_Name": "CHU la Réunion - Site Félix Guyon (SAINT DENIS)",
|
||||
"patients_count": 39,
|
||||
"preincluded_count": 6,
|
||||
"included_count": 33,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "12daafee-970b-4781-9121-994d06e3a766",
|
||||
"name": "CHU DE NICE HOPITAL DE L'ARCHET",
|
||||
"Center_Name": "CHU Nice Archet",
|
||||
"patients_count": 38,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 38,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "f07a7374-d731-4fcf-86e9-2f36e5faf342",
|
||||
"name": "CHU MONTPELLIER HOPITAL ARNAUD DE VILLENEUVE",
|
||||
"Center_Name": "CHU Montpellier - Hôpital Arnaud de Villeneuve",
|
||||
"patients_count": 38,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 38,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "9c64545a-b622-4ef9-be0f-f5cc21b9cd66",
|
||||
"name": "HOPITAL NORD - CHU DE GRENOBLE ALPES",
|
||||
"Center_Name": "CHU de Grenoble - Hôpital Nord",
|
||||
"patients_count": 36,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 35,
|
||||
"prematurely_terminated_count": 1
|
||||
},
|
||||
{
|
||||
"id": "c03b88b5-3cd2-4336-9048-19c239baf5ec",
|
||||
"name": "CHRU DE RENNES SITE HOPITAL SUD",
|
||||
"Center_Name": "CHU Rennes",
|
||||
"patients_count": 34,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 34,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "b0ba921e-e24a-41c2-b769-a2aa108cdd58",
|
||||
"name": "CENTRE HOSPITALIER LES ESCARTONS A BRIANCON",
|
||||
"Center_Name": "CH des Escartons de Briançon",
|
||||
"patients_count": 32,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 31,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "b5f30dc5-da3f-4f8b-9a39-33f0fefb7196",
|
||||
"name": "SCM RX TOULOUSE CLINIQUE PASTEUR",
|
||||
"Center_Name": "Clinique Pasteur Toulouse",
|
||||
"patients_count": 32,
|
||||
"preincluded_count": 2,
|
||||
"included_count": 30,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "817429dd-0d7c-4df9-ad70-2f19c205e05a",
|
||||
"name": "CENTRE HOSPITALIER - FALCONAJA - BASTIA",
|
||||
"Center_Name": "CH Bastia",
|
||||
"patients_count": 29,
|
||||
"preincluded_count": 4,
|
||||
"included_count": 25,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "4f943b00-9306-418a-a853-1d97dff71172",
|
||||
"name": "HOPITAL EUROPEEN",
|
||||
"Center_Name": "Hôpital Européen Marseille",
|
||||
"patients_count": 28,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 28,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "002312ec-69fb-4582-9e8f-7c266e5479d2",
|
||||
"name": "HOPITAL NORD - CHU DE SAINT-ETIENNE",
|
||||
"Center_Name": "CHU de Saint-Étienne - Hôpital Nord",
|
||||
"patients_count": 27,
|
||||
"preincluded_count": 2,
|
||||
"included_count": 25,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "0ce39aac-6b5a-418b-8430-c4432e6cd78f",
|
||||
"name": "HOPITAL DE RANGUEIL CHU TOULOUSE",
|
||||
"Center_Name": "CHU de Toulouse - Hôpital Rangueil",
|
||||
"patients_count": 24,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 23,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "8ef8564e-836c-4c03-b4ec-8e28fa76c3c0",
|
||||
"name": "HOPITAL ESTAING - CHU CLERMONT-FERRAND",
|
||||
"Center_Name": "CHU Clermont-Ferrand - Site Estaing",
|
||||
"patients_count": 23,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 22,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "546f981f-6f7d-40c8-9b97-a1a3c46e6674",
|
||||
"name": "GROUPE HOSPITALIER DE LA REGION DE MULHOUSE ET SUD ALSACE",
|
||||
"Center_Name": "GHR Mulhouse Sud Alsace",
|
||||
"patients_count": 20,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 20,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "eb9c561d-37b6-485c-b351-9e19515cb98d",
|
||||
"name": "GROUPE HOSPITALIER PELLEGRIN - CHU",
|
||||
"Center_Name": "CHU de Bordeaux - GH Pellegrin",
|
||||
"patients_count": 20,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 20,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "d1ef2ced-0206-4725-a08c-454d71e823ec",
|
||||
"name": "HOPITAL DE LA MERE ET DE L'ENFANT",
|
||||
"Center_Name": "CHU Limoges - Hôpital de la Mère et de l'Enfant",
|
||||
"patients_count": 20,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 20,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "7f43a297-b156-4fec-8ecf-d8d008b1a1d2",
|
||||
"name": "HOPITAL PRIVE DIJON BOURGOGNE",
|
||||
"Center_Name": "Centre Evidens - Hôpital privé Dijon Bourgogne",
|
||||
"patients_count": 19,
|
||||
"preincluded_count": 4,
|
||||
"included_count": 15,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "d767fcd0-3f4c-4cdf-9cda-b829156d2f95",
|
||||
"name": "HOPITAL PRIVE SUD CORSE",
|
||||
"Center_Name": "Hôpital Privé Sud Corse",
|
||||
"patients_count": 18,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 18,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "58d5b536-b326-4713-9137-bb6f852df0be",
|
||||
"name": "CENTRE HOSPITALIER METROPOLE SAVOIE - CHAMBERY NH",
|
||||
"Center_Name": "CH Métropole Savoie - Site Chambéry",
|
||||
"patients_count": 16,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 16,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "b4b35661-87c8-4333-bfb1-3ce4f1c77565",
|
||||
"name": "CENTRE HOSPITALIER REGIONAL UNIVERSITAIRE BRETONNEAU",
|
||||
"Center_Name": "CHRU Bretonneau",
|
||||
"patients_count": 16,
|
||||
"preincluded_count": 2,
|
||||
"included_count": 14,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "454313d9-c624-43de-a8e3-84989d526403",
|
||||
"name": "CLINIQUE MUTUALISTE LA SAGESSE RENNES",
|
||||
"Center_Name": "Clinique la Sagesse - Rennes",
|
||||
"patients_count": 15,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 15,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "d981c078-189e-4831-a063-778733f7e582",
|
||||
"name": "HOPITAL FOCH",
|
||||
"Center_Name": "Hôpital Foch",
|
||||
"patients_count": 15,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 12,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "940c8425-fe53-45ac-a750-3e195df990e3",
|
||||
"name": "HOPITAL PRIVE D'EURE ET LOIR",
|
||||
"Center_Name": "Hôpital Privé d'Eure et Loir",
|
||||
"patients_count": 14,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 13,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "98cbbefe-920a-495a-981d-02093bd2253d",
|
||||
"name": "CHU LA MILETRIE",
|
||||
"Center_Name": "CHU de Poitiers La Miletrie",
|
||||
"patients_count": 13,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 13,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "38e9ef8a-5073-471a-a3df-14d834db2d12",
|
||||
"name": "GHBS-SITE HÔPITAL DU SCORFF",
|
||||
"Center_Name": "Groupe Hospitalier Bretagne Sud Lorient - GHBS - Lorient (SCORFF)",
|
||||
"patients_count": 13,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 13,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "6316db1a-4a90-4e45-a518-0095d63f7c36",
|
||||
"name": "HOPITAL LE BOCAGE CHRU DIJON",
|
||||
"Center_Name": "CHU Dijon",
|
||||
"patients_count": 12,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 10,
|
||||
"prematurely_terminated_count": 1
|
||||
},
|
||||
{
|
||||
"id": "f790c171-9523-48b6-a246-fdb26628eb6c",
|
||||
"name": "CHRU NANCY - MATERNITE",
|
||||
"Center_Name": "CHRU de Nancy - Maternité",
|
||||
"patients_count": 11,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 10,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "7b8c5362-7c7a-4d93-8b12-58b8e56020d1",
|
||||
"name": "HOPITAL JACQUES MONOD CH LE HAVRE",
|
||||
"Center_Name": "GH du Havre - Hôpital Jacques Monod",
|
||||
"patients_count": 11,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 11,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "6953af68-e30e-437f-93cf-19ed0932c350",
|
||||
"name": "CHRU D'ORLEANS - HOPITAL DE LA SOURCE",
|
||||
"Center_Name": "CHU d'Orléans",
|
||||
"patients_count": 10,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 10,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "f13560a8-3bf6-4322-bdae-332ee3bf0366",
|
||||
"name": "POLYCLINIQUE JEAN VILLAR",
|
||||
"Center_Name": "Polyclinique Jean Villar",
|
||||
"patients_count": 10,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 10,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "da67fb6e-2ccc-4994-8fde-cc2ee2a8e9ca",
|
||||
"name": "GHU AP-HP UNIVERSITE PARIS SACLAY SITE KREMLIN BICETRE",
|
||||
"Center_Name": "GHU APHP - Hôpital Bicêtre",
|
||||
"patients_count": 9,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 7,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "56f427f0-be35-493b-8049-46b747c0105e",
|
||||
"name": "CHU AMIENS SUD",
|
||||
"Center_Name": "CHU Amiens Sud",
|
||||
"patients_count": 7,
|
||||
"preincluded_count": 3,
|
||||
"included_count": 4,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "f784ee6f-27b5-4afe-ba23-4f839d96535c",
|
||||
"name": "APHM HOPITAL NORD",
|
||||
"Center_Name": "APHM Hôpital Nord",
|
||||
"patients_count": 5,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 5,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "1ac60b10-08de-4639-9a49-67167f85844e",
|
||||
"name": "CENTRE HOSPITALIER DE LENS",
|
||||
"Center_Name": "CH de Lens",
|
||||
"patients_count": 5,
|
||||
"preincluded_count": 2,
|
||||
"included_count": 3,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "9ba321be-65d0-4767-9865-a547905e647e",
|
||||
"name": "HOPITAL DE LA CROIX SAINT SIMON",
|
||||
"Center_Name": "GH Diaconesses Croix Saint-Simon",
|
||||
"patients_count": 5,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 4,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "cab5b1e9-d93a-46eb-ae35-a449ea719c65",
|
||||
"name": "GRAND HOSP EST FRANCILIEN MARNE LA VALLEE SITE JOSSIGNY",
|
||||
"Center_Name": "Grand Hôpital de l'Est Francilien - Site de Marne-la-Vallée",
|
||||
"patients_count": 4,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 4,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "96519108-b0b2-4a5f-999f-cccc4e4059c9",
|
||||
"name": "CHU DE NANTES SITE HOTEL DIEU HOPITAL MERE ENFANT",
|
||||
"Center_Name": "CHU de Nantes - Site Hôtel Dieu Hôpital Mère Enfant",
|
||||
"patients_count": 3,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 3,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "fe7548b7-75a1-4e13-97a9-6dc865b04b25",
|
||||
"name": "CHU SITE SUD ( SAINT PIERRE)",
|
||||
"Center_Name": "CHU la Réunion - Site Sud (SAINT PIERRE)",
|
||||
"patients_count": 3,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 3,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "e68c76de-33e8-49e3-978d-c5fb408e7fd5",
|
||||
"name": "CLINIQUE AXIUM",
|
||||
"Center_Name": "Clinique Axium",
|
||||
"patients_count": 3,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 3,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "af09eaa0-7e08-48e3-990c-24ca2fbc3fdc",
|
||||
"name": "SCP GYNECOLOGIE RIVE GAUCHE",
|
||||
"Center_Name": "SCP Gynécologie Clinique Rive Gauche",
|
||||
"patients_count": 3,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 3,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "0466558d-077c-4a51-9d25-2e15e00fabaf",
|
||||
"name": "CENTRE HOSPITALIER DE CANNES SIMONE VEIL",
|
||||
"Center_Name": "CH de Cannes Simone Veil",
|
||||
"patients_count": 2,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 2,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "aa3e8684-07e0-45d9-9cee-144df2e4b430",
|
||||
"name": "CENTRE HOSPITALIER DE PAU",
|
||||
"Center_Name": "CH de Pau",
|
||||
"patients_count": 2,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 2,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "13306b61-7ea6-4d60-b89c-b0df2d9d1605",
|
||||
"name": "CENTRE HOSPITALIER VICTOR PROVO ROUBAIX",
|
||||
"Center_Name": "CH Victor Provo Roubaix",
|
||||
"patients_count": 2,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 2,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "8c27716f-edfe-4a4e-9189-09f39cc64395",
|
||||
"name": "CHI DE MONT DE MARSAN ET DU PAYS DES SOURCES",
|
||||
"Center_Name": "CHI Mont de Marsan",
|
||||
"patients_count": 2,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 2,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "b74c365a-fc63-4763-b57d-abb609debd3b",
|
||||
"name": "CTRE HOSPITALIER INTERCOMMUNAL POISSY ST GERMAIN SITE POISSY",
|
||||
"Center_Name": "CHI Poissy St Germain en Laye - Site Poissy",
|
||||
"patients_count": 2,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 1,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "4d271322-7945-42e8-801b-a3b25fc570be",
|
||||
"name": "GROUPE HOSPITALIER PARIS SAINT JOSEPH",
|
||||
"Center_Name": "Groupe Hospitalier Paris Saint-Joseph",
|
||||
"patients_count": 2,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 2,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "d82d64eb-a1cf-4635-a99f-23def12c7cb5",
|
||||
"name": "CHRU BREST SITE HOPITAL MORVAN",
|
||||
"Center_Name": "CHRU Brest - Site Hôpital Morvan",
|
||||
"patients_count": 1,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 1,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "a47dc8c0-dfea-4a4c-9bcc-1370616ff9c8",
|
||||
"name": "CHU DE MARTINIQUE SITE MFME",
|
||||
"Center_Name": "CHU de Martinique - Site MFME",
|
||||
"patients_count": 1,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 1,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "384d3b28-f33e-43a6-9043-394aa88aead7",
|
||||
"name": "CLINIQUE BOUCHARD",
|
||||
"Center_Name": "Clinique Bouchard - Marseille",
|
||||
"patients_count": 1,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 1,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "0f78459f-b598-4faa-81a1-085a439e8c0e",
|
||||
"name": "HOPITAUX PRIVES ROUENNAIS MATHILDE",
|
||||
"Center_Name": "Hôpitaux Privés Rouennais - Mathilde",
|
||||
"patients_count": 1,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 1,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "02e42e01-56ab-418d-95df-cb53a7ef17b3",
|
||||
"name": "APHM HOPITAL DE LA CONCEPTION",
|
||||
"Center_Name": "APHM Hôpital de la Conception",
|
||||
"patients_count": 0,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 0,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "2ba1f179-425e-4d88-97a7-88a097e639ab",
|
||||
"name": "CENTRE HOSPITALIER DE CAYENNE",
|
||||
"Center_Name": "CHU de Guyane - Site de Cayenne",
|
||||
"patients_count": 0,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 0,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "3e8887e1-7608-40a6-b6f5-87c6fa5a0bd0",
|
||||
"name": "CENTRE HOSPITALIER DE VERSAILLES ANDRE MIGNOT",
|
||||
"Center_Name": "CH de Versailles",
|
||||
"patients_count": 0,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 0,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "6af3784d-5d57-47c3-b924-0d13008ebd88",
|
||||
"name": "CENTRE HOSPITALIER UNIVERSITAIRE DE POINTE-A-PITRE",
|
||||
"Center_Name": "CHU de Guadeloupe - Pointe-à-Pitre",
|
||||
"patients_count": 0,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 0,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "aa6af654-2cf2-4714-885d-a0e40b6f6f0a",
|
||||
"name": "CHR METZ - THIONVILLE - HOPITAL DE MERCY",
|
||||
"Center_Name": "CHR Metz - Thionville - Hôpital de Mercy",
|
||||
"patients_count": 0,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 0,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "8441f339-4dd3-4c8c-bd2d-7751dc7fe43b",
|
||||
"name": "CLINIQUE DU TERTRE ROUGE - POLE SANTE SUD",
|
||||
"Center_Name": "Clinique du Tertre Rouge - Pôle Santé Sud",
|
||||
"patients_count": 0,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 0,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "09c785f2-3deb-4a4a-a1a8-adbca5b767d7",
|
||||
"name": "GHU APHP CENTRE-UNIVERSITE PARIS CITE SITE COCHIN PORT ROYAL",
|
||||
"Center_Name": "APHP Site Cochin",
|
||||
"patients_count": 0,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 0,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "5847c4fa-4025-4303-80eb-deaa07b80c0b",
|
||||
"name": "GHU APHP SORBONNE UNIVERSITE SITE PITIE SALPETRIERE",
|
||||
"Center_Name": "APHP Site Pitié Salpêtriere",
|
||||
"patients_count": 0,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 0,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "dacc94bd-89b0-4cc4-b334-f684360abbe5",
|
||||
"name": "HOPITAL PRIVE D ANTONY",
|
||||
"Center_Name": "Hôpital Privé d'Antony",
|
||||
"patients_count": 0,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 0,
|
||||
"prematurely_terminated_count": 0
|
||||
}
|
||||
]
|
||||
704
endobest_organizations_old.json
Normal file
704
endobest_organizations_old.json
Normal file
@@ -0,0 +1,704 @@
|
||||
[
|
||||
{
|
||||
"id": "5703b3d6-e415-40b0-90ea-b168835fd720",
|
||||
"name": "HOPITAL PRIVE NATECIA",
|
||||
"Center_Name": "Hôpital Privé Natécia",
|
||||
"patients_count": 169,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 167,
|
||||
"prematurely_terminated_count": 2
|
||||
},
|
||||
{
|
||||
"id": "1de71a30-840b-4c4b-84fc-281ce1b4a5e1",
|
||||
"name": "SANTE ATLANTIQUE",
|
||||
"Center_Name": "Clinique Santé Atlantique",
|
||||
"patients_count": 159,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 157,
|
||||
"prematurely_terminated_count": 1
|
||||
},
|
||||
{
|
||||
"id": "5e6d7afa-6532-495f-a84a-34f644aeaa0f",
|
||||
"name": "CLINIQUE BELLEDONNE",
|
||||
"Center_Name": "Clinique Belledonne",
|
||||
"patients_count": 158,
|
||||
"preincluded_count": 4,
|
||||
"included_count": 153,
|
||||
"prematurely_terminated_count": 1
|
||||
},
|
||||
{
|
||||
"id": "026a6d39-552f-44b9-8a2d-1ecd705f9e08",
|
||||
"name": "HOPITAL AMERICAIN",
|
||||
"Center_Name": "Hôpital Américain de Paris",
|
||||
"patients_count": 158,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 157,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "bf0f96c1-8bbc-4f2c-b360-4a5b27995a12",
|
||||
"name": "SA CLINIQUE TIVOLI-DUCOS",
|
||||
"Center_Name": "Clinique TIVOLI",
|
||||
"patients_count": 155,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 154,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "aba63d11-0dd7-40e8-a652-384c311bb358",
|
||||
"name": "HOPITAL LYON SUD - HOSPICES CIVILS DE LYON",
|
||||
"Center_Name": "Hôpital Lyon Sud - Hospices Civils de Lyon",
|
||||
"patients_count": 127,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 127,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "8488229d-f426-4bdd-923c-f638041b223c",
|
||||
"name": "HOPITAL JEANNE DE FLANDRE DU CHU DE LILLE",
|
||||
"Center_Name": "CHU de Lille - Hôpital Jeanne de Flandre",
|
||||
"patients_count": 91,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 90,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "31665f8d-0f46-44dc-931f-e3aed8879965",
|
||||
"name": "HOPITAL MAISON BLANCHE CHU REIMS",
|
||||
"Center_Name": "CHU de Reims - Hôpital Maison Blanche",
|
||||
"patients_count": 88,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 88,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "b77b301c-10fa-4eca-bb5b-51506766e158",
|
||||
"name": "HOPITAL CHARLES NICOLLE CHU ROUEN",
|
||||
"Center_Name": "CHU de Rouen Normandie - Hôpital Charles-Nicolle",
|
||||
"patients_count": 84,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 83,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "743d0a1a-7edf-4fe6-9d65-0c14363f2153",
|
||||
"name": "CENTRE HOSPITALIER UNIVERSITAIRE JEAN MINJOZ BESANCON",
|
||||
"Center_Name": "CHU Jean Minjoz Besançon - Pôle Mère-Femme",
|
||||
"patients_count": 81,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 81,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "855d7aab-9736-40b7-b9ff-dcc6438890ef",
|
||||
"name": "HOPITAL CROIX-ROUSSE - HOSPICES CIVILS DE LYON",
|
||||
"Center_Name": "Hôpital Croix-Rousse - Hospices Civils de Lyon",
|
||||
"patients_count": 68,
|
||||
"preincluded_count": 5,
|
||||
"included_count": 62,
|
||||
"prematurely_terminated_count": 1
|
||||
},
|
||||
{
|
||||
"id": "74a05936-8fb6-4662-8e95-fd1c91621bf2",
|
||||
"name": "GHU APHP SORBONNE UNIVERSITE SITE TENON",
|
||||
"Center_Name": "GHU APHP - Hôpital Tenon",
|
||||
"patients_count": 62,
|
||||
"preincluded_count": 5,
|
||||
"included_count": 56,
|
||||
"prematurely_terminated_count": 1
|
||||
},
|
||||
{
|
||||
"id": "76d2ebcd-5482-4c5d-8221-c94d1de7261e",
|
||||
"name": "CENTRE HOSPITALIER ANNECY-GENEVOIS SITE ANNECY",
|
||||
"Center_Name": "CH Annecy Genevois – Site d’Annecy",
|
||||
"patients_count": 60,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 60,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "006740fa-696c-4ffb-8b77-7a60d0e23617",
|
||||
"name": "HOPITAL PRIVÉ LE BOIS",
|
||||
"Center_Name": "Hôpital Privé le Bois - Lille",
|
||||
"patients_count": 60,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 59,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "a4b3dfea-1220-4987-b287-f58fde1d0ee5",
|
||||
"name": "CENTRE HOSPITALIER UNIVERSITAIRE COTE DE NACRE",
|
||||
"Center_Name": "CHU de Caen",
|
||||
"patients_count": 59,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 59,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "4aef30cb-778b-409a-b6e2-898bfd91c850",
|
||||
"name": "CENTRE HOSPITALIER GENERAL DE VALENCIENNES",
|
||||
"Center_Name": "CH de Valenciennes",
|
||||
"patients_count": 57,
|
||||
"preincluded_count": 3,
|
||||
"included_count": 54,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "f7d3526b-18d6-43af-b52c-5ca23bac798e",
|
||||
"name": "CENTRE HOSPITALIER REGIONAL D ANGERS",
|
||||
"Center_Name": "CHR d’Angers",
|
||||
"patients_count": 50,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 50,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "1fd6c63b-d909-45d8-8c70-ecb12501c1d1",
|
||||
"name": "HOPITAL DE HAUTEPIERRE",
|
||||
"Center_Name": "CHRU Strasbourg - Hôpital de Hautepierre",
|
||||
"patients_count": 44,
|
||||
"preincluded_count": 2,
|
||||
"included_count": 42,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "d17ddcc4-64db-4cf5-83c5-146f9a060254",
|
||||
"name": "CENTRE HOSPITALIER DE CALAIS.",
|
||||
"Center_Name": "CH de Calais",
|
||||
"patients_count": 42,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 42,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "f6665185-b1ae-4847-a378-652d35158cee",
|
||||
"name": "CLINIQUE BELHARRA",
|
||||
"Center_Name": "Clinique Belharra",
|
||||
"patients_count": 40,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 40,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "fe25553c-4894-4291-b303-c19d2a1c6f0f",
|
||||
"name": "CHU SITE FELIX GUYON (SAINT DENIS)",
|
||||
"Center_Name": "CHU la Réunion - Site Félix Guyon (SAINT DENIS)",
|
||||
"patients_count": 39,
|
||||
"preincluded_count": 6,
|
||||
"included_count": 33,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "12daafee-970b-4781-9121-994d06e3a766",
|
||||
"name": "CHU DE NICE HOPITAL DE L'ARCHET",
|
||||
"Center_Name": "CHU Nice Archet",
|
||||
"patients_count": 38,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 38,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "f07a7374-d731-4fcf-86e9-2f36e5faf342",
|
||||
"name": "CHU MONTPELLIER HOPITAL ARNAUD DE VILLENEUVE",
|
||||
"Center_Name": "CHU Montpellier - Hôpital Arnaud de Villeneuve",
|
||||
"patients_count": 38,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 38,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "9c64545a-b622-4ef9-be0f-f5cc21b9cd66",
|
||||
"name": "HOPITAL NORD - CHU DE GRENOBLE ALPES",
|
||||
"Center_Name": "CHU de Grenoble - Hôpital Nord",
|
||||
"patients_count": 36,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 35,
|
||||
"prematurely_terminated_count": 1
|
||||
},
|
||||
{
|
||||
"id": "c03b88b5-3cd2-4336-9048-19c239baf5ec",
|
||||
"name": "CHRU DE RENNES SITE HOPITAL SUD",
|
||||
"Center_Name": "CHU Rennes",
|
||||
"patients_count": 34,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 34,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "b0ba921e-e24a-41c2-b769-a2aa108cdd58",
|
||||
"name": "CENTRE HOSPITALIER LES ESCARTONS A BRIANCON",
|
||||
"Center_Name": "CH des Escartons de Briançon",
|
||||
"patients_count": 32,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 31,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "b5f30dc5-da3f-4f8b-9a39-33f0fefb7196",
|
||||
"name": "SCM RX TOULOUSE CLINIQUE PASTEUR",
|
||||
"Center_Name": "Clinique Pasteur Toulouse",
|
||||
"patients_count": 32,
|
||||
"preincluded_count": 2,
|
||||
"included_count": 30,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "817429dd-0d7c-4df9-ad70-2f19c205e05a",
|
||||
"name": "CENTRE HOSPITALIER - FALCONAJA - BASTIA",
|
||||
"Center_Name": "CH Bastia",
|
||||
"patients_count": 29,
|
||||
"preincluded_count": 4,
|
||||
"included_count": 25,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "4f943b00-9306-418a-a853-1d97dff71172",
|
||||
"name": "HOPITAL EUROPEEN",
|
||||
"Center_Name": "Hôpital Européen Marseille",
|
||||
"patients_count": 28,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 28,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "002312ec-69fb-4582-9e8f-7c266e5479d2",
|
||||
"name": "HOPITAL NORD - CHU DE SAINT-ETIENNE",
|
||||
"Center_Name": "CHU de Saint-Étienne - Hôpital Nord",
|
||||
"patients_count": 27,
|
||||
"preincluded_count": 2,
|
||||
"included_count": 25,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "0ce39aac-6b5a-418b-8430-c4432e6cd78f",
|
||||
"name": "HOPITAL DE RANGUEIL CHU TOULOUSE",
|
||||
"Center_Name": "CHU de Toulouse - Hôpital Rangueil",
|
||||
"patients_count": 24,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 23,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "8ef8564e-836c-4c03-b4ec-8e28fa76c3c0",
|
||||
"name": "HOPITAL ESTAING - CHU CLERMONT-FERRAND",
|
||||
"Center_Name": "CHU Clermont-Ferrand - Site Estaing",
|
||||
"patients_count": 23,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 22,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "546f981f-6f7d-40c8-9b97-a1a3c46e6674",
|
||||
"name": "GROUPE HOSPITALIER DE LA REGION DE MULHOUSE ET SUD ALSACE",
|
||||
"Center_Name": "GHR Mulhouse Sud Alsace",
|
||||
"patients_count": 20,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 20,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "eb9c561d-37b6-485c-b351-9e19515cb98d",
|
||||
"name": "GROUPE HOSPITALIER PELLEGRIN - CHU",
|
||||
"Center_Name": "CHU de Bordeaux - GH Pellegrin",
|
||||
"patients_count": 20,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 20,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "d1ef2ced-0206-4725-a08c-454d71e823ec",
|
||||
"name": "HOPITAL DE LA MERE ET DE L'ENFANT",
|
||||
"Center_Name": "CHU Limoges - Hôpital de la Mère et de l'Enfant",
|
||||
"patients_count": 20,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 20,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "7f43a297-b156-4fec-8ecf-d8d008b1a1d2",
|
||||
"name": "HOPITAL PRIVE DIJON BOURGOGNE",
|
||||
"Center_Name": "Centre Evidens - Hôpital privé Dijon Bourgogne",
|
||||
"patients_count": 19,
|
||||
"preincluded_count": 4,
|
||||
"included_count": 15,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "d767fcd0-3f4c-4cdf-9cda-b829156d2f95",
|
||||
"name": "HOPITAL PRIVE SUD CORSE",
|
||||
"Center_Name": "Hôpital Privé Sud Corse",
|
||||
"patients_count": 18,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 18,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "58d5b536-b326-4713-9137-bb6f852df0be",
|
||||
"name": "CENTRE HOSPITALIER METROPOLE SAVOIE - CHAMBERY NH",
|
||||
"Center_Name": "CH Métropole Savoie - Site Chambéry",
|
||||
"patients_count": 16,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 16,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "b4b35661-87c8-4333-bfb1-3ce4f1c77565",
|
||||
"name": "CENTRE HOSPITALIER REGIONAL UNIVERSITAIRE BRETONNEAU",
|
||||
"Center_Name": "CHRU Bretonneau",
|
||||
"patients_count": 16,
|
||||
"preincluded_count": 2,
|
||||
"included_count": 14,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "454313d9-c624-43de-a8e3-84989d526403",
|
||||
"name": "CLINIQUE MUTUALISTE LA SAGESSE RENNES",
|
||||
"Center_Name": "Clinique la Sagesse - Rennes",
|
||||
"patients_count": 15,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 15,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "d981c078-189e-4831-a063-778733f7e582",
|
||||
"name": "HOPITAL FOCH",
|
||||
"Center_Name": "Hôpital Foch",
|
||||
"patients_count": 15,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 12,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "940c8425-fe53-45ac-a750-3e195df990e3",
|
||||
"name": "HOPITAL PRIVE D'EURE ET LOIR",
|
||||
"Center_Name": "Hôpital Privé d'Eure et Loir",
|
||||
"patients_count": 14,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 13,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "98cbbefe-920a-495a-981d-02093bd2253d",
|
||||
"name": "CHU LA MILETRIE",
|
||||
"Center_Name": "CHU de Poitiers La Miletrie",
|
||||
"patients_count": 13,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 13,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "38e9ef8a-5073-471a-a3df-14d834db2d12",
|
||||
"name": "GHBS-SITE HÔPITAL DU SCORFF",
|
||||
"Center_Name": "Groupe Hospitalier Bretagne Sud Lorient - GHBS - Lorient (SCORFF)",
|
||||
"patients_count": 13,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 13,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "6316db1a-4a90-4e45-a518-0095d63f7c36",
|
||||
"name": "HOPITAL LE BOCAGE CHRU DIJON",
|
||||
"Center_Name": "CHU Dijon",
|
||||
"patients_count": 12,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 10,
|
||||
"prematurely_terminated_count": 1
|
||||
},
|
||||
{
|
||||
"id": "f790c171-9523-48b6-a246-fdb26628eb6c",
|
||||
"name": "CHRU NANCY - MATERNITE",
|
||||
"Center_Name": "CHRU de Nancy - Maternité",
|
||||
"patients_count": 11,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 10,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "7b8c5362-7c7a-4d93-8b12-58b8e56020d1",
|
||||
"name": "HOPITAL JACQUES MONOD CH LE HAVRE",
|
||||
"Center_Name": "GH du Havre - Hôpital Jacques Monod",
|
||||
"patients_count": 11,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 11,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "6953af68-e30e-437f-93cf-19ed0932c350",
|
||||
"name": "CHRU D'ORLEANS - HOPITAL DE LA SOURCE",
|
||||
"Center_Name": "CHU d'Orléans",
|
||||
"patients_count": 10,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 10,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "f13560a8-3bf6-4322-bdae-332ee3bf0366",
|
||||
"name": "POLYCLINIQUE JEAN VILLAR",
|
||||
"Center_Name": "Polyclinique Jean Villar",
|
||||
"patients_count": 10,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 10,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "da67fb6e-2ccc-4994-8fde-cc2ee2a8e9ca",
|
||||
"name": "GHU AP-HP UNIVERSITE PARIS SACLAY SITE KREMLIN BICETRE",
|
||||
"Center_Name": "GHU APHP - Hôpital Bicêtre",
|
||||
"patients_count": 9,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 7,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "56f427f0-be35-493b-8049-46b747c0105e",
|
||||
"name": "CHU AMIENS SUD",
|
||||
"Center_Name": "CHU Amiens Sud",
|
||||
"patients_count": 7,
|
||||
"preincluded_count": 3,
|
||||
"included_count": 4,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "f784ee6f-27b5-4afe-ba23-4f839d96535c",
|
||||
"name": "APHM HOPITAL NORD",
|
||||
"Center_Name": "APHM Hôpital Nord",
|
||||
"patients_count": 5,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 5,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "1ac60b10-08de-4639-9a49-67167f85844e",
|
||||
"name": "CENTRE HOSPITALIER DE LENS",
|
||||
"Center_Name": "CH de Lens",
|
||||
"patients_count": 5,
|
||||
"preincluded_count": 2,
|
||||
"included_count": 3,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "9ba321be-65d0-4767-9865-a547905e647e",
|
||||
"name": "HOPITAL DE LA CROIX SAINT SIMON",
|
||||
"Center_Name": "GH Diaconesses Croix Saint-Simon",
|
||||
"patients_count": 5,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 4,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "cab5b1e9-d93a-46eb-ae35-a449ea719c65",
|
||||
"name": "GRAND HOSP EST FRANCILIEN MARNE LA VALLEE SITE JOSSIGNY",
|
||||
"Center_Name": "Grand Hôpital de l'Est Francilien - Site de Marne-la-Vallée",
|
||||
"patients_count": 4,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 4,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "96519108-b0b2-4a5f-999f-cccc4e4059c9",
|
||||
"name": "CHU DE NANTES SITE HOTEL DIEU HOPITAL MERE ENFANT",
|
||||
"Center_Name": "CHU de Nantes - Site Hôtel Dieu Hôpital Mère Enfant",
|
||||
"patients_count": 3,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 3,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "fe7548b7-75a1-4e13-97a9-6dc865b04b25",
|
||||
"name": "CHU SITE SUD ( SAINT PIERRE)",
|
||||
"Center_Name": "CHU la Réunion - Site Sud (SAINT PIERRE)",
|
||||
"patients_count": 3,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 3,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "e68c76de-33e8-49e3-978d-c5fb408e7fd5",
|
||||
"name": "CLINIQUE AXIUM",
|
||||
"Center_Name": "Clinique Axium",
|
||||
"patients_count": 3,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 3,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "af09eaa0-7e08-48e3-990c-24ca2fbc3fdc",
|
||||
"name": "SCP GYNECOLOGIE RIVE GAUCHE",
|
||||
"Center_Name": "SCP Gynécologie Clinique Rive Gauche",
|
||||
"patients_count": 3,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 3,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "0466558d-077c-4a51-9d25-2e15e00fabaf",
|
||||
"name": "CENTRE HOSPITALIER DE CANNES SIMONE VEIL",
|
||||
"Center_Name": "CH de Cannes Simone Veil",
|
||||
"patients_count": 2,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 2,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "aa3e8684-07e0-45d9-9cee-144df2e4b430",
|
||||
"name": "CENTRE HOSPITALIER DE PAU",
|
||||
"Center_Name": "CH de Pau",
|
||||
"patients_count": 2,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 2,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "13306b61-7ea6-4d60-b89c-b0df2d9d1605",
|
||||
"name": "CENTRE HOSPITALIER VICTOR PROVO ROUBAIX",
|
||||
"Center_Name": "CH Victor Provo Roubaix",
|
||||
"patients_count": 2,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 2,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "8c27716f-edfe-4a4e-9189-09f39cc64395",
|
||||
"name": "CHI DE MONT DE MARSAN ET DU PAYS DES SOURCES",
|
||||
"Center_Name": "CHI Mont de Marsan",
|
||||
"patients_count": 2,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 2,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "b74c365a-fc63-4763-b57d-abb609debd3b",
|
||||
"name": "CTRE HOSPITALIER INTERCOMMUNAL POISSY ST GERMAIN SITE POISSY",
|
||||
"Center_Name": "CHI Poissy St Germain en Laye - Site Poissy",
|
||||
"patients_count": 2,
|
||||
"preincluded_count": 1,
|
||||
"included_count": 1,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "4d271322-7945-42e8-801b-a3b25fc570be",
|
||||
"name": "GROUPE HOSPITALIER PARIS SAINT JOSEPH",
|
||||
"Center_Name": "Groupe Hospitalier Paris Saint-Joseph",
|
||||
"patients_count": 2,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 2,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "d82d64eb-a1cf-4635-a99f-23def12c7cb5",
|
||||
"name": "CHRU BREST SITE HOPITAL MORVAN",
|
||||
"Center_Name": "CHRU Brest - Site Hôpital Morvan",
|
||||
"patients_count": 1,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 1,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "a47dc8c0-dfea-4a4c-9bcc-1370616ff9c8",
|
||||
"name": "CHU DE MARTINIQUE SITE MFME",
|
||||
"Center_Name": "CHU de Martinique - Site MFME",
|
||||
"patients_count": 1,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 1,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "384d3b28-f33e-43a6-9043-394aa88aead7",
|
||||
"name": "CLINIQUE BOUCHARD",
|
||||
"Center_Name": "Clinique Bouchard - Marseille",
|
||||
"patients_count": 1,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 1,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "0f78459f-b598-4faa-81a1-085a439e8c0e",
|
||||
"name": "HOPITAUX PRIVES ROUENNAIS MATHILDE",
|
||||
"Center_Name": "Hôpitaux Privés Rouennais - Mathilde",
|
||||
"patients_count": 1,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 1,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "02e42e01-56ab-418d-95df-cb53a7ef17b3",
|
||||
"name": "APHM HOPITAL DE LA CONCEPTION",
|
||||
"Center_Name": "APHM Hôpital de la Conception",
|
||||
"patients_count": 0,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 0,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "2ba1f179-425e-4d88-97a7-88a097e639ab",
|
||||
"name": "CENTRE HOSPITALIER DE CAYENNE",
|
||||
"Center_Name": "CHU de Guyane - Site de Cayenne",
|
||||
"patients_count": 0,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 0,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "3e8887e1-7608-40a6-b6f5-87c6fa5a0bd0",
|
||||
"name": "CENTRE HOSPITALIER DE VERSAILLES ANDRE MIGNOT",
|
||||
"Center_Name": "CH de Versailles",
|
||||
"patients_count": 0,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 0,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "6af3784d-5d57-47c3-b924-0d13008ebd88",
|
||||
"name": "CENTRE HOSPITALIER UNIVERSITAIRE DE POINTE-A-PITRE",
|
||||
"Center_Name": "CHU de Guadeloupe - Pointe-à-Pitre",
|
||||
"patients_count": 0,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 0,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "aa6af654-2cf2-4714-885d-a0e40b6f6f0a",
|
||||
"name": "CHR METZ - THIONVILLE - HOPITAL DE MERCY",
|
||||
"Center_Name": "CHR Metz - Thionville - Hôpital de Mercy",
|
||||
"patients_count": 0,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 0,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "8441f339-4dd3-4c8c-bd2d-7751dc7fe43b",
|
||||
"name": "CLINIQUE DU TERTRE ROUGE - POLE SANTE SUD",
|
||||
"Center_Name": "Clinique du Tertre Rouge - Pôle Santé Sud",
|
||||
"patients_count": 0,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 0,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "09c785f2-3deb-4a4a-a1a8-adbca5b767d7",
|
||||
"name": "GHU APHP CENTRE-UNIVERSITE PARIS CITE SITE COCHIN PORT ROYAL",
|
||||
"Center_Name": "APHP Site Cochin",
|
||||
"patients_count": 0,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 0,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "5847c4fa-4025-4303-80eb-deaa07b80c0b",
|
||||
"name": "GHU APHP SORBONNE UNIVERSITE SITE PITIE SALPETRIERE",
|
||||
"Center_Name": "APHP Site Pitié Salpêtriere",
|
||||
"patients_count": 0,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 0,
|
||||
"prematurely_terminated_count": 0
|
||||
},
|
||||
{
|
||||
"id": "dacc94bd-89b0-4cc4-b334-f684360abbe5",
|
||||
"name": "HOPITAL PRIVE D ANTONY",
|
||||
"Center_Name": "Hôpital Privé d'Antony",
|
||||
"patients_count": 0,
|
||||
"preincluded_count": 0,
|
||||
"included_count": 0,
|
||||
"prematurely_terminated_count": 0
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user