mirror of
https://github.com/gauvainboiche/ketapk.git
synced 2026-09-02 11:13:11 +02:00
fix: Deploying version dynamically + adding Tauri project files
This commit is contained in:
+1
-2
@@ -1,7 +1,6 @@
|
|||||||
# Origin project
|
# Origin project
|
||||||
KetaPK_FR_1_00/
|
KetaPK_FR_1_00/
|
||||||
KetaPK_FR_1_00*
|
KetaPK_FR_1_00*
|
||||||
# !KetaPK_FR_1_00.xlsm
|
|
||||||
code_vba.txt
|
code_vba.txt
|
||||||
|
|
||||||
# Node files
|
# Node files
|
||||||
@@ -15,7 +14,7 @@ __pycache__/
|
|||||||
uv.lock
|
uv.lock
|
||||||
|
|
||||||
# Tauri files
|
# Tauri files
|
||||||
src-tauri/
|
src-tauri/*/
|
||||||
|
|
||||||
# Virtual environments
|
# Virtual environments
|
||||||
.venv
|
.venv
|
||||||
+10
-1
@@ -1,4 +1,4 @@
|
|||||||
import { MODES, PK_MODELS, CLINICAL_THRESHOLDS, generateSimulationData, processTimelineEvents } from './pkCore.js';
|
import { MODES, PK_MODELS, CLINICAL_THRESHOLDS, APP_VERSION, generateSimulationData, processTimelineEvents } from './pkCore.js';
|
||||||
import { adjustChartYScale } from './chartManager.js';
|
import { adjustChartYScale } from './chartManager.js';
|
||||||
import { initCalculatorUI } from './calculator.js';
|
import { initCalculatorUI } from './calculator.js';
|
||||||
import { exportSimulationToCSV } from './csvExport.js';
|
import { exportSimulationToCSV } from './csvExport.js';
|
||||||
@@ -8,6 +8,13 @@ let currentMode = MODES.ESKETAMINE;
|
|||||||
let pkChart = null;
|
let pkChart = null;
|
||||||
let currentSimulationData = null;
|
let currentSimulationData = null;
|
||||||
|
|
||||||
|
function updateVersionDisplay() {
|
||||||
|
const versionEl = document.querySelector('[data-i18n="app.version"]');
|
||||||
|
if (versionEl) {
|
||||||
|
versionEl.textContent = t('app.version', { version: APP_VERSION });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const timelineInputs = Array.from({ length: 61 }, (_, i) => ({
|
const timelineInputs = Array.from({ length: 61 }, (_, i) => ({
|
||||||
time: i * 5,
|
time: i * 5,
|
||||||
bolusMg: 0,
|
bolusMg: 0,
|
||||||
@@ -16,12 +23,14 @@ const timelineInputs = Array.from({ length: 61 }, (_, i) => ({
|
|||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
await initI18n();
|
await initI18n();
|
||||||
|
updateVersionDisplay();
|
||||||
|
|
||||||
const selectLang = document.getElementById('selectLang');
|
const selectLang = document.getElementById('selectLang');
|
||||||
if (selectLang) {
|
if (selectLang) {
|
||||||
selectLang.value = getCurrentLang();
|
selectLang.value = getCurrentLang();
|
||||||
selectLang.addEventListener('change', async (e) => {
|
selectLang.addEventListener('change', async (e) => {
|
||||||
await initI18n(e.target.value);
|
await initI18n(e.target.value);
|
||||||
|
updateVersionDisplay();
|
||||||
renderTimelineGrid();
|
renderTimelineGrid();
|
||||||
updateSimulation();
|
updateSimulation();
|
||||||
});
|
});
|
||||||
|
|||||||
+12
-21
@@ -1,8 +1,6 @@
|
|||||||
import { t } from './i18n.js';
|
import { t } from './i18n.js';
|
||||||
|
import { APP_VERSION } from './pkCore.js';
|
||||||
|
|
||||||
/**
|
|
||||||
* Formate la date au format strict : YYYY_DD_MM_HH_MM_SS
|
|
||||||
*/
|
|
||||||
function getFormattedTimestamp() {
|
function getFormattedTimestamp() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const YYYY = now.getFullYear();
|
const YYYY = now.getFullYear();
|
||||||
@@ -14,27 +12,20 @@ function getFormattedTimestamp() {
|
|||||||
return `${YYYY}_${DD}_${MM}_${HH}_${Min}_${SS}`;
|
return `${YYYY}_${DD}_${MM}_${HH}_${Min}_${SS}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Transforme le nom saisi en slug propre (ex: "DUPONT Jean" -> "dupont_jean")
|
|
||||||
*/
|
|
||||||
function slugify(text) {
|
function slugify(text) {
|
||||||
if (!text) return '';
|
if (!text) return '';
|
||||||
return text
|
return text
|
||||||
.toString()
|
.toString()
|
||||||
.trim()
|
.trim()
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.normalize("NFD").replace(/[\u0300-\u036f]/g, "") // Enlève les accents
|
.normalize("NFD").replace(/[\u0300-\u036f]/g, "")
|
||||||
.replace(/[^a-z0-9]+/g, "_") // Remplace les caractères spéciaux par _
|
.replace(/[^a-z0-9]+/g, "_")
|
||||||
.replace(/^_+|_+$/g, ""); // Nettoie les _ aux bornes
|
.replace(/^_+|_+$/g, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Exporte les données de simulation au format CSV
|
|
||||||
*/
|
|
||||||
export async function exportSimulationToCSV(simulationData, patientInfo) {
|
export async function exportSimulationToCSV(simulationData, patientInfo) {
|
||||||
if (!simulationData || !simulationData.timeline) return;
|
if (!simulationData || !simulationData.timeline) return;
|
||||||
|
|
||||||
// \uFEFF (BOM UTF-8) pour qu'Excel ouvre le CSV directement avec les accents
|
|
||||||
let csvContent = "\uFEFF";
|
let csvContent = "\uFEFF";
|
||||||
|
|
||||||
// Métadonnées du rapport
|
// Métadonnées du rapport
|
||||||
@@ -43,9 +34,12 @@ export async function exportSimulationToCSV(simulationData, patientInfo) {
|
|||||||
csvContent += `${t('patient.name_label') || 'Patient'}:,${patientInfo.name}\n`;
|
csvContent += `${t('patient.name_label') || 'Patient'}:,${patientInfo.name}\n`;
|
||||||
}
|
}
|
||||||
csvContent += `${t('csv.weight')}:,${patientInfo.weight}\n`;
|
csvContent += `${t('csv.weight')}:,${patientInfo.weight}\n`;
|
||||||
csvContent += `${t('csv.unit')}:,${patientInfo.infusionUnit}\n\n`;
|
csvContent += `${t('csv.unit')}:,${patientInfo.infusionUnit}\n`;
|
||||||
|
|
||||||
// En-têtes du tableau selon la langue active
|
// Ligne de Traçabilité de Version
|
||||||
|
csvContent += `${t('csv.version') || 'Version'}:,v${APP_VERSION}\n\n`;
|
||||||
|
|
||||||
|
// En-têtes du tableau
|
||||||
const headers = [
|
const headers = [
|
||||||
t('csv.col_time'),
|
t('csv.col_time'),
|
||||||
t('csv.col_bolus'),
|
t('csv.col_bolus'),
|
||||||
@@ -56,7 +50,7 @@ export async function exportSimulationToCSV(simulationData, patientInfo) {
|
|||||||
];
|
];
|
||||||
csvContent += headers.join(",") + "\n";
|
csvContent += headers.join(",") + "\n";
|
||||||
|
|
||||||
// Lignes de données (0 à 300 min)
|
// Lignes de données
|
||||||
simulationData.timeline.forEach((item, index) => {
|
simulationData.timeline.forEach((item, index) => {
|
||||||
const dominoCp = (simulationData.domino && simulationData.domino[index]) ? Math.round(simulationData.domino[index].cp) : "N/A";
|
const dominoCp = (simulationData.domino && simulationData.domino[index]) ? Math.round(simulationData.domino[index].cp) : "N/A";
|
||||||
const clementsCp = (simulationData.clements && simulationData.clements[index]) ? Math.round(simulationData.clements[index].cp) : "N/A";
|
const clementsCp = (simulationData.clements && simulationData.clements[index]) ? Math.round(simulationData.clements[index].cp) : "N/A";
|
||||||
@@ -73,14 +67,12 @@ export async function exportSimulationToCSV(simulationData, patientInfo) {
|
|||||||
csvContent += row.join(",") + "\n";
|
csvContent += row.join(",") + "\n";
|
||||||
});
|
});
|
||||||
|
|
||||||
// Construction du nom de fichier : ketapk_YYYY_DD_MM_HH_MM_SS.csv ou ketapk_nom_prenom_YYYY_DD_MM_HH_MM_SS.csv
|
|
||||||
const timestamp = getFormattedTimestamp();
|
const timestamp = getFormattedTimestamp();
|
||||||
const slugName = slugify(patientInfo.name);
|
const slugName = slugify(patientInfo.name);
|
||||||
const fileName = slugName
|
const fileName = slugName
|
||||||
? `ketapk_${slugName}_${timestamp}.csv`
|
? `ketapk_${slugName}_${timestamp}.csv`
|
||||||
: `ketapk_${timestamp}.csv`;
|
: `ketapk_${timestamp}.csv`;
|
||||||
|
|
||||||
// 1. Solution principale : API Native File System (Parfait sous WebView2 / Tauri Desktop)
|
|
||||||
if (window.showSaveFilePicker) {
|
if (window.showSaveFilePicker) {
|
||||||
try {
|
try {
|
||||||
const handle = await window.showSaveFilePicker({
|
const handle = await window.showSaveFilePicker({
|
||||||
@@ -95,12 +87,11 @@ export async function exportSimulationToCSV(simulationData, patientInfo) {
|
|||||||
await writable.close();
|
await writable.close();
|
||||||
return;
|
return;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.name === 'AbortError') return; // Annulation utilisateur
|
if (err.name === 'AbortError') return;
|
||||||
console.warn('Fallback vers Data URI suite à l’erreur native :', err);
|
console.warn('Fallback vers Data URI :', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Fallback Data URI (Compatibilité web directe sans passer par Blob)
|
|
||||||
const encodedUri = "data:text/csv;charset=utf-8," + encodeURIComponent(csvContent);
|
const encodedUri = "data:text/csv;charset=utf-8," + encodeURIComponent(csvContent);
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.setAttribute("href", encodedUri);
|
link.setAttribute("href", encodedUri);
|
||||||
|
|||||||
@@ -5,6 +5,11 @@
|
|||||||
* ============================================================================
|
* ============================================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version unique de l'application (Centralisée)
|
||||||
|
*/
|
||||||
|
export const APP_VERSION = '0.16.0';
|
||||||
|
|
||||||
export const MODES = {
|
export const MODES = {
|
||||||
ESKETAMINE: 'ESKETAMINE',
|
ESKETAMINE: 'ESKETAMINE',
|
||||||
RACEMIQUE: 'RACEMIQUE'
|
RACEMIQUE: 'RACEMIQUE'
|
||||||
|
|||||||
+13
-12
@@ -1,7 +1,19 @@
|
|||||||
{
|
{
|
||||||
"app": {
|
"app": {
|
||||||
"title": "药代动力学模拟",
|
"title": "药代动力学模拟",
|
||||||
"version": "v1.00 • Dr Georges Mion"
|
"version": "v{version} • Dr Georges Mion"
|
||||||
|
},
|
||||||
|
"csv": {
|
||||||
|
"title": "KetaPK - 原始数据报告",
|
||||||
|
"version": "软件版本",
|
||||||
|
"weight": "患者体重 (kg)",
|
||||||
|
"unit": "输注单位",
|
||||||
|
"col_time": "时间 (min)",
|
||||||
|
"col_bolus": "负荷剂量 (mg)",
|
||||||
|
"col_infusion": "有效输注速率",
|
||||||
|
"col_domino": "Domino (ng/mL)",
|
||||||
|
"col_clements": "Clements (ng/mL)",
|
||||||
|
"col_kamp": "Kamp (ng/mL)"
|
||||||
},
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
"mode_esk": "右旋氯胺酮",
|
"mode_esk": "右旋氯胺酮",
|
||||||
@@ -35,17 +47,6 @@
|
|||||||
"col_clements": "Clements (ng/mL)",
|
"col_clements": "Clements (ng/mL)",
|
||||||
"col_kamp": "Kamp (ng/mL)"
|
"col_kamp": "Kamp (ng/mL)"
|
||||||
},
|
},
|
||||||
"csv": {
|
|
||||||
"title": "KetaPK - 原始数据报告",
|
|
||||||
"weight": "患者体重 (kg)",
|
|
||||||
"unit": "输注单位",
|
|
||||||
"col_time": "时间 (min)",
|
|
||||||
"col_bolus": "负荷剂量 (mg)",
|
|
||||||
"col_infusion": "有效输注速率",
|
|
||||||
"col_domino": "Domino (ng/mL)",
|
|
||||||
"col_clements": "Clements (ng/mL)",
|
|
||||||
"col_kamp": "Kamp (ng/mL)"
|
|
||||||
},
|
|
||||||
"zones": {
|
"zones": {
|
||||||
"inactive": "无活性",
|
"inactive": "无活性",
|
||||||
"anti_hyperalgesic": "抗痛觉过敏",
|
"anti_hyperalgesic": "抗痛觉过敏",
|
||||||
|
|||||||
+13
-12
@@ -1,7 +1,19 @@
|
|||||||
{
|
{
|
||||||
"app": {
|
"app": {
|
||||||
"title": "Pharmaco Kinetics",
|
"title": "Pharmaco Kinetics",
|
||||||
"version": "v1.00 • Dr Georges Mion"
|
"version": "v{version} • Dr Georges Mion"
|
||||||
|
},
|
||||||
|
"csv": {
|
||||||
|
"title": "KetaPK - Raw Data Report",
|
||||||
|
"version": "App version",
|
||||||
|
"weight": "Patient weight (kg)",
|
||||||
|
"unit": "Infusion unit",
|
||||||
|
"col_time": "Time (min)",
|
||||||
|
"col_bolus": "Bolus (mg)",
|
||||||
|
"col_infusion": "Active infusion",
|
||||||
|
"col_domino": "Domino (ng/mL)",
|
||||||
|
"col_clements": "Clements (ng/mL)",
|
||||||
|
"col_kamp": "Kamp (ng/mL)"
|
||||||
},
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
"mode_esk": "ESKETAMINE",
|
"mode_esk": "ESKETAMINE",
|
||||||
@@ -35,17 +47,6 @@
|
|||||||
"col_clements": "Clements (ng/mL)",
|
"col_clements": "Clements (ng/mL)",
|
||||||
"col_kamp": "Kamp (ng/mL)"
|
"col_kamp": "Kamp (ng/mL)"
|
||||||
},
|
},
|
||||||
"csv": {
|
|
||||||
"title": "KetaPK - Raw Data Report",
|
|
||||||
"weight": "Patient weight (kg)",
|
|
||||||
"unit": "Infusion unit",
|
|
||||||
"col_time": "Time (min)",
|
|
||||||
"col_bolus": "Bolus (mg)",
|
|
||||||
"col_infusion": "Active infusion",
|
|
||||||
"col_domino": "Domino (ng/mL)",
|
|
||||||
"col_clements": "Clements (ng/mL)",
|
|
||||||
"col_kamp": "Kamp (ng/mL)"
|
|
||||||
},
|
|
||||||
"zones": {
|
"zones": {
|
||||||
"inactive": "Inactive",
|
"inactive": "Inactive",
|
||||||
"anti_hyperalgesic": "Anti-hyperalgesic",
|
"anti_hyperalgesic": "Anti-hyperalgesic",
|
||||||
|
|||||||
+13
-12
@@ -1,7 +1,19 @@
|
|||||||
{
|
{
|
||||||
"app": {
|
"app": {
|
||||||
"title": "Pharmaco Kinetics",
|
"title": "Pharmaco Kinetics",
|
||||||
"version": "v1.00 • Dr Georges Mion"
|
"version": "v{version} • Dr Georges Mion"
|
||||||
|
},
|
||||||
|
"csv": {
|
||||||
|
"title": "KetaPK - Rapport de donnees brutes",
|
||||||
|
"version": "Version application",
|
||||||
|
"weight": "Poids du patient (kg)",
|
||||||
|
"unit": "Unite de perfusion",
|
||||||
|
"col_time": "Temps (min)",
|
||||||
|
"col_bolus": "Bolus (mg)",
|
||||||
|
"col_infusion": "Perfusion active",
|
||||||
|
"col_domino": "Domino (ng/mL)",
|
||||||
|
"col_clements": "Clements (ng/mL)",
|
||||||
|
"col_kamp": "Kamp (ng/mL)"
|
||||||
},
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
"mode_esk": "ESKETAMINE",
|
"mode_esk": "ESKETAMINE",
|
||||||
@@ -35,17 +47,6 @@
|
|||||||
"col_clements": "Clements (ng/mL)",
|
"col_clements": "Clements (ng/mL)",
|
||||||
"col_kamp": "Kamp (ng/mL)"
|
"col_kamp": "Kamp (ng/mL)"
|
||||||
},
|
},
|
||||||
"csv": {
|
|
||||||
"title": "KetaPK - Rapport de donnees brutes",
|
|
||||||
"weight": "Poids du patient (kg)",
|
|
||||||
"unit": "Unite de perfusion",
|
|
||||||
"col_time": "Temps (min)",
|
|
||||||
"col_bolus": "Bolus (mg)",
|
|
||||||
"col_infusion": "Perfusion active",
|
|
||||||
"col_domino": "Domino (ng/mL)",
|
|
||||||
"col_clements": "Clements (ng/mL)",
|
|
||||||
"col_kamp": "Kamp (ng/mL)"
|
|
||||||
},
|
|
||||||
"zones": {
|
"zones": {
|
||||||
"inactive": "Inactif",
|
"inactive": "Inactif",
|
||||||
"anti_hyperalgesic": "Anti-hyperalgésique",
|
"anti_hyperalgesic": "Anti-hyperalgésique",
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
/target/
|
||||||
|
/target/debug
|
||||||
|
/target/release/*
|
||||||
|
/gen/schemas
|
||||||
|
|
||||||
|
# Exclude app.exe
|
||||||
|
!/target/release/app.exe
|
||||||
Generated
+4480
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "ketapk"
|
||||||
|
version = "0.16.0"
|
||||||
|
description = "KétaPK"
|
||||||
|
authors = ["Gauvain BOICHÉ", "Pr. Georges MION"]
|
||||||
|
license = "Proprietary"
|
||||||
|
repository = ""
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.77.2"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "app_lib"
|
||||||
|
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tauri-build = { version = "2.6.3", features = [] }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde_json = "1.0"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
log = "0.4"
|
||||||
|
tauri = { version = "2.11.3", features = [] }
|
||||||
|
tauri-plugin-log = "2"
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
tauri_build::build()
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
|
"productName": "ketapk",
|
||||||
|
"version": "0.16.0",
|
||||||
|
"identifier": "com.ketapk.desktop",
|
||||||
|
"build": {
|
||||||
|
"beforeDevCommand": "",
|
||||||
|
"beforeBuildCommand": "",
|
||||||
|
"frontendDist": "../frontend"
|
||||||
|
},
|
||||||
|
"app": {
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"title": "KétaPK",
|
||||||
|
"width": 1280,
|
||||||
|
"height": 800,
|
||||||
|
"resizable": true,
|
||||||
|
"fullscreen": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"bundle": {
|
||||||
|
"active": true,
|
||||||
|
"targets": ["msi"],
|
||||||
|
"category": "Medical",
|
||||||
|
"icon": [
|
||||||
|
"icons/32x32.png",
|
||||||
|
"icons/128x128.png",
|
||||||
|
"icons/128x128@2x.png",
|
||||||
|
"icons/icon.icns",
|
||||||
|
"icons/icon.ico"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user