google_sheets_api.dart 10,2 ko
Newer Older
import 'dart:convert';
import 'package:flutter/services.dart' show rootBundle;
import 'package:googleapis_auth/auth_io.dart' as auth;
import 'package:googleapis/sheets/v4.dart' as sheets;

class GoogleSheetsApi {

  // 1. l'ID de feuille
  static const _spreadsheetId = '1tbWnNSx3J-OAM7rrAxU_YLM97z2zMFtV055nS66Ig3c';

  // 2. Définir les permissions
  static const _scopes = [sheets.SheetsApi.spreadsheetsScope];

  // 3. Fonction pour s'authentifier (utilise le fichier JSON)
  Future<auth.AutoRefreshingAuthClient?> _getAuthClient() async {
    try {
      final jsonString = await rootBundle.loadString('assets/credentials.json');
      final credentialsJson = json.decode(jsonString);

      final credentials = auth.ServiceAccountCredentials.fromJson(credentialsJson);

      final client = await auth.clientViaServiceAccount(credentials, _scopes);
      return client;
    } catch (e) {
      print('Erreur lors de l\'authentification : $e');

  // 4. Fonction pour ajouter une nouvelle ligne
  Future<bool> appendData(List<dynamic> rowData) async {
    final client = await _getAuthClient();
    if (client == null) {
      print("Erreur d'authentification");
      return false;
    }

    final api = sheets.SheetsApi(client);

    // Le nom de votre feuille (ex: 'Feuille1')
    final String range = 'données etudiants';

    var values = sheets.ValueRange.fromJson({
      'values': [ rowData ] // Les données à ajouter
    });

    try {
      await api.spreadsheets.values.append(
          values,
          _spreadsheetId,
          range,
          valueInputOption: 'RAW'
      );
      print('Données ajoutées !');
      client.close();
      return true;
    } catch (e) {
      print('Erreur lors de l\'ajout des données : $e');
      client.close();
      return false;
    }
  }

  // 5. Fonction pour lire des données (exemple)
  Future<List<List<dynamic>>?> readData() async {
    final client = await _getAuthClient();
    if (client == null) {
      print("Erreur d'authentification");
      return null;
    }

    final api = sheets.SheetsApi(client);

    final String range = 'données etudiants!A1:C10';

    try {
      final result = await api.spreadsheets.values.get(
        _spreadsheetId,
        range,
      );
      print('Données lues : ${result.values}');
      client.close();
      return result.values;
    } catch (e) {
      print('Erreur lors de la lecture des données : $e');
      client.close();
      return null;
    }
  }

  // 6. NOUVELLE FONCTION : Lire toutes les valeurs d'une colonne
  Future<List<String>> readColumn(String columnName) async {
    final client = await _getAuthClient();
    if (client == null) {
      print("Erreur d'authentification");
      return [];
    }

    final api = sheets.SheetsApi(client);

    final String range = 'données etudiants!$columnName:$columnName';

    try {
      final result = await api.spreadsheets.values.get(
        _spreadsheetId,
        range,
      );

      client.close();

      if (result.values == null) {
        return []; // La colonne est vide
      }

      // Convertir la List<List<dynamic>> en une simple List<String>
      List<String> columnData = [];
      for (var row in result.values!) {
        if (row.isNotEmpty) {
          columnData.add(row[0].toString());
        }
      }
      return columnData;

    } catch (e) {
      print('Erreur lors de la lecture de la colonne : $e');
      client.close();
      return [];
    }
  }


  // 7. FONCTION MODIFIÉE : Trouver un nom par l'UID (stocké en Colonne D)
  Future<String?> findStudentNameByNfcUid(String uidToCheck) async {
    final client = await _getAuthClient();
    if (client == null) {
      print("Erreur d'authentification");
      return null;
    }
    final api = sheets.SheetsApi(client);

    // On lit les colonnes Nom (C) et Mac (D).
    final String range = 'données etudiants!C:D';

    try {
      final result = await api.spreadsheets.values.get(
        _spreadsheetId,
        range,
      );
      client.close();

      if (result.values == null) {
        print("La feuille est vide ou n'a pas pu être lue.");
        return null;
      }

      final uidUpper = uidToCheck.toUpperCase();

      for (int i = 0; i < result.values!.length; i++) {
        final row = result.values![i];
        if (row.length == 2 && row[1] != null) {
          String nfcUidFromSheet = row[1].toString().toUpperCase();
            await  updateLastScan(i + 2, "F");
      print("UID non trouvé dans la feuille.");
      return null;

    } catch (e) {
      print('Erreur lors de la recherche de l\'UID : $e');
      client.close();
      return null;
    }
  }
  // 8. Récupérer la liste de présence par date (pour le PDF)
  Future<List<Map<String, String>>> getPresenceByDate(String date) async {
    final client = await _getAuthClient();
    if (client == null) return [];

    final api = sheets.SheetsApi(client);
    final String range = 'données etudiants!A:F';

    final result = await api.spreadsheets.values.get(_spreadsheetId, range);
    client.close();

    if (result.values == null) return [];

    List<Map<String, String>> resultList = [];

    for (int i = 1; i < result.values!.length; i++) {
      final row = result.values![i];

      final dateAjout = row.length > 5 ? row[5]?.toString() ?? "" : "";


      if (dateAjout.startsWith(date)) {
        resultList.add({
          'N° Etudiant': row.length > 0 ? row[0]?.toString() ?? '' : '',
          'Prénom': row.length > 1 ? row[1]?.toString() ?? '' : '',
          'Nom': row.length > 2 ? row[2]?.toString() ?? '' : '',
          'Mac NFC': row.length > 3 ? row[3]?.toString() ?? '' : '',
          'QR': row.length > 4 ? row[4]?.toString() ?? '' : '',
          'date_ajout': dateAjout,
        });
      }
    }

    return resultList;
  }
  Future<void> updateLastScan(int rowIndex, String columnLetter) async {
    final client = await _getAuthClient();
    if (client == null) return;

    final api = sheets.SheetsApi(client);
    final now = DateTime.now().toIso8601String();

    final range = 'données etudiants!$columnLetter$rowIndex';

    final oldResult = await api.spreadsheets.values.get(_spreadsheetId, range);

    String newValue = now;

    if (oldResult.values != null &&
        oldResult.values!.isNotEmpty &&
        oldResult.values!.first.isNotEmpty) {
      final oldValue = oldResult.values!.first.first.toString().trim();

      if (!oldValue.endsWith(now)) {
        newValue = "$oldValue;$now";
      } else {
        newValue = oldValue;
      }
    }

    final values = sheets.ValueRange.fromJson({
      "values": [[newValue]]
    });

    await api.spreadsheets.values.update(
      values,
      _spreadsheetId,
      range,
      valueInputOption: "RAW",
  Future<String?> findStudentNameByQr(String qrValue) async {
    final client = await _getAuthClient();
    if (client == null) {
      print("Erreur d'authentification");
      return null;
    }

    final api = sheets.SheetsApi(client);

    final String range = 'données etudiants!A:F';

    try {
      final result = await api.spreadsheets.values.get(
        _spreadsheetId,
        range,
      );

      client.close();

      if (result.values == null) {
        print("La feuille est vide ou n'a pas pu être lue.");
        return null;
      }

      // Parcourt chaque ligne
      for (int i = 0; i < result.values!.length; i++) {
        final row = result.values![i];

        if (row.length > 4 && row[4] != null) {
          String qrFromSheet = row[4].toString();
          if (qrFromSheet == qrValue) {
            await updateLastScan(i + 2, "F");
            return row[2].toString();
          }
        }
      }

      print("QR code non trouvé dans la feuille.");
      return null;
    } catch (e) {
      print('Erreur lors de la recherche du QR : $e');
      client.close();
      return null;
    }
  }
  Future<int?> findRowByStudentNumber(String studentNumber) async {
    final client = await _getAuthClient();
    if (client == null) return null;

    final api = sheets.SheetsApi(client);
    final range = 'données etudiants!A:A';

    final result = await api.spreadsheets.values.get(
      _spreadsheetId,
      range,
    );

    client.close();

    if (result.values == null) return null;

    for (int i = 0; i < result.values!.length; i++) {
      if (result.values![i].isNotEmpty &&
          result.values![i][0].toString() == studentNumber) {
        return i + 1; // index réel Google Sheets
      }
    }

    return null;
  }
  Future<bool> updateStudentFields({
    required int rowIndex,
    String? nfc,
    String? qr,
  }) async {
    final client = await _getAuthClient();
    if (client == null) return false;

    final api = sheets.SheetsApi(client);

    List<List<dynamic>> updates = [];

    if (nfc != null && nfc.isNotEmpty) {
      updates.add(['D$rowIndex', nfc]);
    }

    if (qr != null && qr.isNotEmpty) {
      updates.add(['E$rowIndex', qr]);
    }

    for (var u in updates) {
      await api.spreadsheets.values.update(
        sheets.ValueRange.fromJson({'values': [[u[1]]]}),
        _spreadsheetId,
        'données etudiants!${u[0]}',
        valueInputOption: 'USER_ENTERED',
      );
    }

    client.close();
    return true;
  }
  Future<List<Map<String, String>>> getAllPresences() async {
    final client = await _getAuthClient();
    if (client == null) return [];

    final api = sheets.SheetsApi(client);

    final result = await api.spreadsheets.values.get(
      _spreadsheetId,
      'données etudiants!A:F',
    );

    client.close();

    if (result.values == null || result.values!.length <= 1) return [];

    final rows = result.values!;
    final List<Map<String, String>> data = [];

    for (int i = 1; i < rows.length; i++) { // skip header
      final row = rows[i];

      data.add({
        'N° Etudiant': row.length > 0 ? row[0].toString() : '',
        'Prénom': row.length > 1 ? row[1].toString() : '',
        'Nom': row.length > 2 ? row[2].toString() : '',
        'Mac NFC': row.length > 3 ? row[3].toString() : '',
        'QR': row.length > 4 ? row[4].toString() : '',
        'date_ajout': row.length > 5 ? row[5].toString() : '', // LISTE DES DATES
      });
    }

    return data;
  }