From 8a92895371864c0bfc1f4a4c9433fa06ca2753ac Mon Sep 17 00:00:00 2001 From: Enzo Date: Mon, 4 Dec 2023 19:23:30 +0100 Subject: [PATCH] new importation en fonction de la categorie et ready pour l'importation --- .../container/profile/profile_info_user.dart | 14 +- lib/modele/activity.dart | 3 + lib/modele/activity_info/activity_info.dart | 15 + .../activity_info/activity_info_generic.dart | 41 +++ .../activity_info/activity_info_walking.dart | 48 +++ lib/modele/api/request_api.dart | 2 + lib/modele/data_file.dart | 11 - lib/modele/manager_file.dart | 283 ++++++++---------- lib/modele/user.dart | 27 ++ .../list_activity/list_activity_utile.dart | 15 +- 10 files changed, 284 insertions(+), 175 deletions(-) create mode 100644 lib/modele/activity_info/activity_info.dart create mode 100644 lib/modele/activity_info/activity_info_generic.dart create mode 100644 lib/modele/activity_info/activity_info_walking.dart delete mode 100644 lib/modele/data_file.dart diff --git a/lib/common_widget/container/profile/profile_info_user.dart b/lib/common_widget/container/profile/profile_info_user.dart index 431db85..b404b30 100644 --- a/lib/common_widget/container/profile/profile_info_user.dart +++ b/lib/common_widget/container/profile/profile_info_user.dart @@ -16,30 +16,32 @@ class ProfileInfoUser extends StatelessWidget { subtitle: "Nombre d'activité", ), ), - SizedBox( + const SizedBox( width: 15, ), Expanded( child: TitleSubtitleCell( - title: "h/j", + title: context.watch().getTotalTimeAllActivity().toString(), subtitle: "Temps en activité", ), ), - SizedBox( + const SizedBox( width: 15, ), Expanded( child: TitleSubtitleCell( - title: "+ m", + title: + "${context.watch().getTotalDenivelePositif().toString()} + m", subtitle: "Total dénivelé positif", ), ), - SizedBox( + const SizedBox( width: 15, ), Expanded( child: TitleSubtitleCell( - title: "- m", + title: + "${context.watch().getTotalDeniveleNegatif().toString()} - m", subtitle: "Total dénivelé négatif", ), ), diff --git a/lib/modele/activity.dart b/lib/modele/activity.dart index ae41f54..afdb3c3 100644 --- a/lib/modele/activity.dart +++ b/lib/modele/activity.dart @@ -16,6 +16,9 @@ class ActivityOfUser { String get fileUuid => _fileUuid; String get nameFile => _nameFile; String get category => _categorie; + double get time => _timeActivity; + double get denivelePos => _denivelePositif; + double get deniveleNeg => _deniveleNegatif; Map get enteteCSV => _enteteCSV; // -- Getter/Setter -- Ancien // diff --git a/lib/modele/activity_info/activity_info.dart b/lib/modele/activity_info/activity_info.dart new file mode 100644 index 0000000..c487c87 --- /dev/null +++ b/lib/modele/activity_info/activity_info.dart @@ -0,0 +1,15 @@ +abstract class ActivityInfo { + // -- Time -- // + String startTime = "2000-01-01"; + double timeOfActivity = 0.0; + + // -- BPM -- // + int bpmMax = 0; + int bpmMin = 300; + int bpmAvg = 0; + + // -- Fonction pour lire le csv et remplir la classe -- // + ActivityInfo getData(List> csv); + + String toJson(); +} diff --git a/lib/modele/activity_info/activity_info_generic.dart b/lib/modele/activity_info/activity_info_generic.dart new file mode 100644 index 0000000..54485bd --- /dev/null +++ b/lib/modele/activity_info/activity_info_generic.dart @@ -0,0 +1,41 @@ +import 'dart:convert'; + +import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart'; + +class ActivityInfoGeneric implements ActivityInfo { + // ------- Ajout --------- // + + // ------- Activity Info -------- // + @override + late int bpmAvg; + + @override + late int bpmMax; + + @override + late int bpmMin; + + @override + late String startTime; + + @override + late double timeOfActivity; + + @override + ActivityInfo getData(List> csv) { + return this; + } + + @override + // Méthode pour convertir les attributs en JSON + String toJson() { + Map jsonMap = { + 'bpmAvg': bpmAvg, + 'bpmMax': bpmMax, + 'bpmMin': bpmMin, + 'startTime': startTime, + 'timeOfActivity': timeOfActivity, + }; + return jsonEncode(jsonMap); + } +} diff --git a/lib/modele/activity_info/activity_info_walking.dart b/lib/modele/activity_info/activity_info_walking.dart new file mode 100644 index 0000000..65395ed --- /dev/null +++ b/lib/modele/activity_info/activity_info_walking.dart @@ -0,0 +1,48 @@ +import 'dart:convert'; + +import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart'; + +class ActivityInfoWalking implements ActivityInfo { + // ------- Ajout --------- // + + // -- Denivelé -- // + double denivelePositif = 0.0; + double deniveleNegatif = 0.0; + + // ------- Activity Info -------- // + @override + int bpmAvg = 0; + + @override + int bpmMax = 0; + + @override + int bpmMin = 0; + + @override + String startTime = "2000-01-01"; + + @override + double timeOfActivity = 0.0; + + @override + ActivityInfo getData(List> csv) { + for (int i = 0; i < csv.length; i++) {} + return this; + } + + @override + // Méthode pour convertir les attributs en JSON + String toJson() { + Map jsonMap = { + 'denivelePositif': denivelePositif, + 'deniveleNegatif': deniveleNegatif, + 'bpmAvg': bpmAvg, + 'bpmMax': bpmMax, + 'bpmMin': bpmMin, + 'startTime': startTime, + 'timeOfActivity': timeOfActivity, + }; + return jsonEncode(jsonMap); + } +} diff --git a/lib/modele/api/request_api.dart b/lib/modele/api/request_api.dart index b10b3cc..7d54ff8 100644 --- a/lib/modele/api/request_api.dart +++ b/lib/modele/api/request_api.dart @@ -32,6 +32,8 @@ class RequestApi extends IDataStrategy { if ((response.statusCode == 404)) { return const Tuple2(false, "404 - NOT FOUND"); } + print(response.statusCode); + print(response.body); return const Tuple2(false, "Fail"); } diff --git a/lib/modele/data_file.dart b/lib/modele/data_file.dart deleted file mode 100644 index d954520..0000000 --- a/lib/modele/data_file.dart +++ /dev/null @@ -1,11 +0,0 @@ -class DataFile { - final List> csvData; - final String category; - final String startTime; - final double denivelePositif; - final double deniveleNegatif; - final double timeOfActivity; - - DataFile(this.csvData, this.category, this.startTime, this.denivelePositif, - this.deniveleNegatif, this.timeOfActivity); -} diff --git a/lib/modele/manager_file.dart b/lib/modele/manager_file.dart index ec463ff..8dc171f 100644 --- a/lib/modele/manager_file.dart +++ b/lib/modele/manager_file.dart @@ -3,7 +3,10 @@ import 'dart:typed_data'; import 'package:csv/csv.dart'; import 'package:fit_tool/fit_tool.dart'; import 'package:path_provider/path_provider.dart'; -import 'package:smartfit_app_mobile/modele/data_file.dart'; +import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart'; +import 'package:smartfit_app_mobile/modele/activity_info/activity_info_generic.dart'; +import 'package:smartfit_app_mobile/modele/activity_info/activity_info_walking.dart'; +import 'package:tuple/tuple.dart'; class ManagerFile { // -- Field @@ -19,10 +22,10 @@ class ManagerFile { final String _fieldTemperature = "temperature"; // -- Not in CSV -- // - final String _session = "session"; - final String _startTime = "start_time"; - final String _sport = "sport"; - final String _timeActivity = "total_elapsed_time"; + static const String _session = "session"; + static const String _startTime = "start_time"; + static const String _sport = "sport"; + static const String _timeActivity = "total_elapsed_time"; // -- Getter field String get fieldTimeStamp => _fieldTimestamp; @@ -36,7 +39,14 @@ class ManagerFile { String get fieldTotalCalories => _fieldTotalCalorie; String get fieldTemperature => _fieldTemperature; + // -- Categorie -- // + static const String _generic = "generic"; + static const String _velo = "cycling"; + static const String _marche = "walking"; + List allowedFieldWalking = List.empty(growable: true); + List allowedFieldGeneric = List.empty(growable: true); + List allowedFieldCycling = List.empty(growable: true); ManagerFile() { allowedFieldWalking = [ @@ -51,71 +61,125 @@ class ManagerFile { _fieldTotalCalorie, _fieldTemperature ]; + + allowedFieldGeneric = [_fieldTimestamp, _fieldBPM]; + + allowedFieldCycling = [ + _fieldTimestamp, + _fieldPositionLatitue, + _fieldPositionLongitude, + _fieldDistance, + _fieldBPM, + _fieldSpeed, + _fieldAltitude, + _fieldTotalCalorie, + _fieldTemperature + ]; + } + + // -- Read the byte of file CSV -- // + List> convertByteIntoCSV(Uint8List bytes) { + return const CsvToListConverter().convert(utf8.decode(bytes)); } - DataFile convertBytesFitFileIntoCSVList(Uint8List bytes) { - FitFile fitFile = FitFile.fromBytes(bytes); + String _getCategoryById(int id) { + switch (id) { + case 0: + return _generic; + case 2: + return _velo; + case 11: + return _marche; + default: + return _generic; + } + } + + // ------------- Get The path of application --- // + Future get localPath async { + final directory = await getApplicationDocumentsDirectory(); + return directory.path; + } + + Tuple4>, ActivityInfo, String> + convertBytesFitFileIntoCSVListAndGetInfo(Uint8List bytes) { + List fitFile = FitFile.fromBytes(bytes).records; + String categorie; + List fieldAllowed = []; + ActivityInfo info; + // -- Chercher ligne session -- // + List ligneSession = _getLigneSession(fitFile); + if (ligneSession.isEmpty) { + return Tuple4(false, List.empty(), ActivityInfoGeneric(), ""); + } + categorie = + _getCategoryById(int.parse(_getXfromListe(_sport, ligneSession))); + + // -- Si la catégorie est pas prévu est est généric -- // + switch (categorie) { + case (_marche): + fieldAllowed = allowedFieldWalking; + info = ActivityInfoWalking(); + break; + case (_generic): + fieldAllowed = allowedFieldGeneric; + info = ActivityInfoGeneric(); + break; + default: + // A REMETRE EN GENERIC + //fieldAllowed = allowedFieldGeneric; + //info = ActivityInfoGeneric(); + //categorie = _generic; + fieldAllowed = allowedFieldWalking; + info = ActivityInfoWalking(); + break; + } + List> csvData = transformDataMapIntoCSV( + getDataOfListeOfRecord(fitFile, fieldAllowed), fieldAllowed); + // Remplir info avec la ligne session + info.startTime = DateTime.fromMillisecondsSinceEpoch( + int.parse(_getXfromListe(_startTime, ligneSession))) + .toIso8601String(); + + return Tuple4(true, csvData, info.getData(csvData), categorie); + } + + List _getLigneSession(List listRecord) { + for (int i = listRecord.length - 1; i != listRecord.length - 5; i--) { + List tmpListe = listRecord[i].toRow(); + if (tmpListe[0] == "Data" && tmpListe[2] == _session) { + return tmpListe; + } + } + return List.empty(); + } + + String _getXfromListe(String x, List liste) { + for (int i = 0; i < liste.length; i++) { + if (liste[i] == x) { + return liste[i + 1].toString(); + } + } + return "null"; + } - // ----------- Lire le fit et extarire les données qu'on choisi ----------- // + List>> getDataOfListeOfRecord( + List listeRecord, List allowedField) { List>> dataResult = List.empty(growable: true); - // -- Start Time default -- // - String startTime = "2000-01-01"; - // -- Category Default -- // - String category = "Generic"; - // -- Time of activity default -- // - double timeActivity = 0.0; - // -- Denivelé positif et négatif -- // - double denivelePositif = 0.0; - double deniveleNegatif = 0.0; - double lastDenivele = 0.0; - // --------------------------------------- // - - for (Record element in fitFile.records) { + + for (Record element in listeRecord) { List listeField = element.toRow(); Map> ligneDataResult = {}; // -- Skip ligne whith no data -- // bool skip = true; - // -- Session -- // - bool sesssionLigne = false; + // -- Si ce n'est pas de la data on pass -- // if (listeField[0] != "Data") { continue; } - for (int i = 0; i < listeField.length;) { - // -- Check si c'est une ligne session --// - if (i == 0 && listeField[2] == _session) { - sesssionLigne = true; - } - // -- Si ligne session && starttime -- // - if (sesssionLigne && listeField[i] == _startTime) { - startTime = - DateTime.fromMillisecondsSinceEpoch(listeField[i + 1] as int) - .toIso8601String(); - } - // -- Si ligne session && sport -- // - if (sesssionLigne && listeField[i] == _sport) { - category = _getCategoryById(listeField[i + 1] as int); - } - - // -- Si ligne session && total_elapsed_time -- // - if (sesssionLigne && listeField[i] == _timeActivity) { - timeActivity = listeField[i + 1]; - } - - // Calcul denivelé positif et négatif - if (listeField[i] == _fieldAltitude) { - if (listeField[i + 1] > lastDenivele) { - denivelePositif += listeField[i + 1] - lastDenivele; - } else { - deniveleNegatif += (listeField[i + 1] - lastDenivele) * -1; - } - lastDenivele = listeField[i + 1]; - } - //------// - - if (allowedFieldWalking.contains(listeField[i])) { + if (allowedField.contains(listeField[i])) { Map tmp = {}; tmp["Value"] = listeField[i + 1].toString(); tmp["Unite"] = listeField[i + 2].toString(); @@ -123,27 +187,33 @@ class ManagerFile { i += 2; skip = false; } + + // -- Pour boucler -- // i += 1; } if (!skip) { dataResult.add(ligneDataResult); } } - // -------- FIN ---------- // + return dataResult; + } + List> transformDataMapIntoCSV( + List>> listeMap, + List fieldAllowed) { // ------- Création du csv ----- // // --- Création de l'entête -- // List enteteCSV = []; - for (String field in allowedFieldWalking) { + for (String field in fieldAllowed) { enteteCSV.add("Value_$field"); enteteCSV.add("Unite_$field"); } List> csvData = List.empty(growable: true); // - for (Map> ligne in dataResult) { + for (Map> ligne in listeMap) { List tmpLigne = List.empty(growable: true); - for (String field in allowedFieldWalking) { + for (String field in fieldAllowed) { if (!ligne.containsKey(field)) { tmpLigne.add("null"); tmpLigne.add("null"); @@ -156,97 +226,6 @@ class ManagerFile { } csvData.insert(0, enteteCSV); // ------- FIN --------------- // - return DataFile(csvData, category, startTime, denivelePositif, - deniveleNegatif, timeActivity); + return csvData; } - - // -- Read the byte of file CSV -- // - List> convertByteIntoCSV(Uint8List bytes) { - return const CsvToListConverter().convert(utf8.decode(bytes)); - } - - String _getCategoryById(int id) { - switch (id) { - case 0: - return "generic"; - case 2: - return "cycling"; - case 11: - return "walking"; - default: - return "generic"; - } - } - - // ------------- Get The path of application --- // - Future get localPath async { - final directory = await getApplicationDocumentsDirectory(); - return directory.path; - } - - /* - // ----- Read csv File ------- // - Future> readCSVFile(String path) async { - if (File(path).exists() == false) return List.empty(); - final input = File(path).openRead(); - final fields = await input - .transform(utf8.decoder) - .transform(const CsvToListConverter()) - .toList(); - return fields; - } - - // ----- Read a file FIT --- // - Future> readFitFile(String path) async { - if (File(path).existsSync() == false) return List.empty(); - - final file = File(path); - final bytes = await file.readAsBytes(); - final fitFile = FitFile.fromBytes(bytes); - - return fitFile.toRows(); - } - - Future> readFitFileWhithFile(File file) async { - final bytes = await file.readAsBytes(); - final fitFile = FitFile.fromBytes(bytes); - return fitFile.toRows(); - } - - List readFitFileWeb(Uint8List bytes) { - final fitFile = FitFile.fromBytes(bytes); - return fitFile.toRows(); - }*/ - - /* - // --- A modifier si utilisé --- // - Future saveFileLocal(String nameFileWithExtension, String path) async { - /* - final outFile = File("${await localPath}\\Files\\$nameFileWithExtension"); - if (outFile.existsSync() == false) { - outFile.createSync(recursive: true); - } - await outFile.writeAsString(await file.readAsString()); - return true;*/ - }*/ - /* - // -- Check si le fichier existe localement -- // - Future fileExist(String filname) async { - Directory directory = Directory("${await localPath}\\Files\\"); - if (!directory.existsSync()) { - print("Le dossier n'existe pas !"); - return false; - } - List files = directory.listSync(); - for (FileSystemEntity file in files) { - if (file.path.split("\\").last == filname) { - return true; - } - } - return false; - }*/ - - // --- Ligne utile --- // - //final csv = const ListToCsvConverter().convert(fitFile.toRows()); - //await outFile.writeAsString(csv);*/ } diff --git a/lib/modele/user.dart b/lib/modele/user.dart index e866ebc..688015f 100644 --- a/lib/modele/user.dart +++ b/lib/modele/user.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:smartfit_app_mobile/modele/activity.dart'; import 'package:smartfit_app_mobile/modele/manager_selected_activity.dart'; @@ -24,4 +25,30 @@ class User extends ChangeNotifier { listActivity.insert(index, activity); notifyListeners(); } + + // ------------ Fonction Calcul -------- // + + double getTotalTimeAllActivity() { + double totalTime = 0.0; + for (ActivityOfUser activity in listActivity) { + totalTime += activity.time; + } + return totalTime; + } + + double getTotalDenivelePositif() { + double totalDevPos = 0.0; + for (ActivityOfUser activity in listActivity) { + totalDevPos += activity.denivelePos; + } + return totalDevPos; + } + + double getTotalDeniveleNegatif() { + double totalDevNeg = 0.0; + for (ActivityOfUser activity in listActivity) { + totalDevNeg += activity.denivelePos; + } + return totalDevNeg; + } } diff --git a/lib/modele/utile/list_activity/list_activity_utile.dart b/lib/modele/utile/list_activity/list_activity_utile.dart index 06b751f..ce7a38d 100644 --- a/lib/modele/utile/list_activity/list_activity_utile.dart +++ b/lib/modele/utile/list_activity/list_activity_utile.dart @@ -5,9 +5,9 @@ import 'package:csv/csv.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:smartfit_app_mobile/modele/activity.dart'; +import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart'; import 'package:smartfit_app_mobile/modele/api/i_data_strategy.dart'; import 'package:smartfit_app_mobile/modele/api/request_api.dart'; -import 'package:smartfit_app_mobile/modele/data_file.dart'; import 'package:smartfit_app_mobile/modele/manager_file.dart'; import 'package:smartfit_app_mobile/modele/user.dart'; import 'package:tuple/tuple.dart'; @@ -51,11 +51,11 @@ class ListActivityUtile { notZero = true; } Provider.of(context, listen: false).addActivity(ActivityOfUser( - element["creation_date"].toString(), element["category"].toString(), + element["creation_date"].toString(), element["uuid"].toString(), element["filename"].toString(), - /* + /* element["timeActivity"], element["denivelePositif"], element["deniveleNegatif"]*/ @@ -73,15 +73,18 @@ class ListActivityUtile { Future> _addFile( Uint8List bytes, String filename, String token) async { // -- Transormer le fit en CSV - DataFile dataFile = _managerFile.convertBytesFitFileIntoCSVList(bytes); + Tuple4>, ActivityInfo, String> resultData = + _managerFile.convertBytesFitFileIntoCSVListAndGetInfo(bytes); - String csvString = const ListToCsvConverter().convert(dataFile.csvData); + String csvString = const ListToCsvConverter().convert(resultData.item2); Uint8List byteCSV = Uint8List.fromList(utf8.encode(csvString)); // --- Save Local // --- Api + print("Start"); + print(resultData.item3.toJson()); Tuple2 result = await _strategy.uploadFileByte( - token, byteCSV, filename, dataFile.category, dataFile.startTime); + token, byteCSV, filename, resultData.item4, resultData.item3.startTime); if (result.item1 == false) { return Tuple2(false, result.item2); }