• Moderatore

    @mirkomassarutto ha detto in API di YouTube: riusciamo a fare un sito per piccole statistiche?:

    Il "giro" sheet, calendar, Google form mi pare un po' articolato... Ma magari mi sbaglio

    In realtà non è articolato perché non hai lo sbattimento di tirare su un db dedicato, costruire una UI, usare una url esterna che poi è da embeddare qui, creare un form per l'iscrizione, ecc, con gli strumenti di Google hai già tutto quello che serve, con gli script app integri gli strumenti fra loro in modo semplice perché sono fatti apposta per comunicare fra loro, e il tutto senza avere necessita di preparare una piattaforma da zero.

    Per esempio gia i google form ti danno la possibilità di salvare su un file sheet e da qui si fa il resto, è molto più immediato.


  • Community Manager

    Eccomi, scusate il ritardo.

    La verità è che, davvero, sono andato un po' in crisi nel capire qual è il processo più semplice da applicare. Non è facile, perché c'è da tenere conto che bisogna farlo riservato alla classe, ALMENO PER IL MOMENTO, e dobbiamo averlo qui su Connect.gt (con tutti i credits di chi lo crea, link e via dicendo).

    Quindi ho pensato un po'.

    Serve un Form sicuro
    Questo perché altrimenti si iscrivono tutti a caso. Non so se dobbiamo autorizzare qualcosa o che.

    La persona deve mettere:

    • Nome su Connect
    • Sua discussione su Connect
    • Suo canale
    • Tema

    Da qui noi salviamo, in più:

    data di compilazione: diventerà la data di inizio alla classe
    iscritti: in quel momento
    iscritti ora: ULTIMO nostro salvataggio
    numero video: in quel momento
    numero video ora: ULTIMO nostro salvataggio
    salto di iscritti:
    salto iscritti in percentuale:
    salto iscritti per video:

    Così riusciamo a fare questa
    alt text

    A me questo serve.

    Se da questo si salva il dato settimanalmente, più avanti possiamo pensare di fare altro.

    Ma in questo momento è troppo complicato. Sono impazzito a pensare le varie cose e così perdo un mucchio di tempo 🙂

    Pensate solo che ora devo capire come far aprire le discussioni qui, se in una nuova sezione o in quella vecchia e nel caso sistemare le discussioni...mmm


  • Moderatore

    Google form secondo me è il mezzo più semplice e sicuro, già di base si può collegare ad uno spreadsheet anche esistente, ogni campo del form sarà la colonna nel foglio poi se servono campi aggiuntivi si aggiunge lo script necessario.

    Da questo foglio poi si procede con il resto.


  • Moderatore

    Devo solo mettermi in pari con alcuni lavori e poi faccio una demo, purtroppo a me le vacanze spezzano il flusso e poi riprendere è sempre un problema...


  • User Attivo

    Ciao @giorgiotave e @overclokk io il sistema di presa API lo ho già definito con tutto quello che ho scritto sopra..
    il concetto è del tipo:

    1. Mi iscrivo e do il mio canale
    2. Il sistema analizza se il canale esiste
    3. Il sistema preleva le stats da quel momento in avanti

    .. personalmente non vedo cose strambe...

    se vuoi ti faccio avere via e.mail il csv.. oppure ti creo un API per prelevarle.

    ditemi voi... integrazione con form... si può fare... ma non hai in mano nulla.... sarà che io con "excel" non mi ci sono mai trovato


    overclokk 1 Risposta
  • User Attivo

    ah giusto per.. il sistema si basa poi sull'ID... che magari non tutti ti sanno dare all'inizio 😉


  • Moderatore

    @mirkomassarutto "excel" sarà il nostro DB, tutto qui, è più semplice per Giorgio poter accedere e vedere i dati in tempo reale, i vari endpoint che hai indicato ci saranno utili per reperire le informazioni necessarie e caricarle sul DB (excel).

    Per una cosa così non è conveniente dover tirare su un server per ospitare un db, creare un form per salvare i dati sul db, creare tutte le query per lavorare con i dati e pensare alla sicurezza del tutto, troppa roba, lo so che lato dev fa più figo e che sono due righe di codice (che poi non è mai vero) ma in questo modo si complica la UX di Giorgio, il problema qui non è reperire le info ma avere una UI gestibile, vedi l'esempio che ha postato Giorgio, è un file "excel", e se riusciamo a dargli quello abbiamo vinto tutti, "excel" poi ha già integrati tutti gli strumenti per filtrare i dati e per aggiungere filtri personalizzati.

    Tutto il sistema si gestisce con simil-javascript così possiamo tirare fuori il dev che è in noi 🙂

    L'unica pecca dei form di google è che puoi fare il check sui campi al submit e non prima, però penso si possa risolvere in qualche modo.


    mirkomassarutto 1 Risposta
  • User Attivo

    @overclokk per me potete fare come volete...
    Non ho capito bene come intendi estrarrei dati da youtube attraverso google form... io per fare questo sto utilizzando direttamente le api

    il sistema io lo ho già mostrato a suo tempo.. ed è fatto con 341 righe di codice https://yt.massarutto.it/tools/ (qui estraggo in questo momento i dati di @giorgiotave e i dati degli ultimi video.

    Ribadisco che non voglio fare tutta l'interfaccia, semplicemente il sistema di presa che genera il CSV che poi @giorgiotave di importa in excel e gestisce come preferisce


    overclokk 1 Risposta
  • Moderatore

    @mirkomassarutto ha detto in API di YouTube: riusciamo a fare un sito per piccole statistiche?:

    Non ho capito bene come intendi estrarrei dati da youtube attraverso google form... io per fare questo sto utilizzando direttamente le api

    Google form serve solo per l'iscrizione, il foglio di calcolo prende i dati tramite le API aggiungendo delle "macro" in javascript (google script).

    @mirkomassarutto ha detto in API di YouTube: riusciamo a fare un sito per piccole statistiche?:

    Ribadisco che non voglio fare tutta l'interfaccia, semplicemente il sistema di presa che genera il CSV che poi @giorgiotave di importa in excel e gestisce come preferisce

    È un lavoro doppio che va fatto manualmente ogni volta che si devono importare i dati, averli già in un foglio di calcolo che si popola da solo senza dover caricare nulla semplifica il processo, alla fine sempre nel foglio di calcolo vanno messi ma se si riesce a farlo con meno passaggi è meglio (DRY), e mancherebbe il sistema di iscrizione degli utenti.

    Lo scopo finale è aprire il foglio di calcolo una volta sola e avere già tutto pronto.


    giorgiotave 1 Risposta
  • Community Manager

    @overclokk vai, riusciamo a partire con questa disposizione?

    Così faccio partire la classe, appena ho almeno il form, faccio partire il tutto.

    @mirkomassarutto ti darà una mano nelle API giusto?

    L'idea che invece prospettare Mirko, è una figata. Ma ci facciamo un'altra cosa se lui ha voglia 🙂


    overclokk 1 Risposta
  • User Attivo

    Allora.. io ho fatto un test con Google Sheets

    Questo il codice dello script per importarsi i JSON

    /**
     * Retrieves all the rows in the active spreadsheet that contain data and logs the
     * values for each row.
     * For more information on using the Spreadsheet API, see
     * https://developers.google.com/apps-script/service_spreadsheet
     */
    function readRows() {
      var sheet = SpreadsheetApp.getActiveSheet();
      var rows = sheet.getDataRange();
      var numRows = rows.getNumRows();
      var values = rows.getValues();
    
      for (var i = 0; i <= numRows - 1; i++) {
        var row = values*;
        Logger.log(row);
      }
    };
    
    
    function onOpen() {
      var sheet = SpreadsheetApp.getActiveSpreadsheet();
      var entries = [{
        name : "Read Data",
        functionName : "readRows"
      }];
      sheet.addMenu("Script Center Menu", entries);
    };
    
    
    function ImportJSON(url, query, options) {
      return ImportJSONAdvanced(url, query, options, includeXPath_, defaultTransform_);
    }
    
    
    function ImportJSONAdvanced(url, query, options, includeFunc, transformFunc) {
      var jsondata = UrlFetchApp.fetch(url);
      var object   = JSON.parse(jsondata.getContentText());
      
      return parseJSONObject_(object, query, options, includeFunc, transformFunc);
    }
    
    
    function URLEncode(value) {
      return encodeURIComponent(value.toString());  
    }
    
    
    function parseJSONObject_(object, query, options, includeFunc, transformFunc) {
      var headers = new Array();
      var data    = new Array();
      
      if (query && !Array.isArray(query) && query.toString().indexOf(",") != -1) {
        query = query.toString().split(",");
      }
      
      if (options) {
        options = options.toString().split(",");
      }
        
      parseData_(headers, data, "", 1, object, query, options, includeFunc);
      parseHeaders_(headers, data);
      transformData_(data, options, transformFunc);
      
      return hasOption_(options, "noHeaders") ? (data.length > 1 ? data.slice(1) : new Array()) : data;
    }
    
    
    function parseData_(headers, data, path, rowIndex, value, query, options, includeFunc) {
      var dataInserted = false;
      
      if (isObject_(value)) {
        for (key in value) {
          if (parseData_(headers, data, path + "/" + key, rowIndex, value[key], query, options, includeFunc)) {
            dataInserted = true; 
          }
        }
      } else if (Array.isArray(value) && isObjectArray_(value)) {
        for (var i = 0; i < value.length; i++) {
          if (parseData_(headers, data, path, rowIndex, value*, query, options, includeFunc)) {
            dataInserted = true;
            rowIndex++;
          }
        }
      } else if (!includeFunc || includeFunc(query, path, options)) {
        // Handle arrays containing only scalar values
        if (Array.isArray(value)) {
          value = value.join(); 
        }
        
        // Insert new row if one doesn't already exist
        if (!data[rowIndex]) {
          data[rowIndex] = new Array();
        }
        
        // Add a new header if one doesn't exist
        if (!headers[path] && headers[path] != 0) {
          headers[path] = Object.keys(headers).length;
        }
        
        // Insert the data
        data[rowIndex][headers[path]] = value;
        dataInserted = true;
      }
      
      return dataInserted;
    }
    
    
    function parseHeaders_(headers, data) {
      data[0] = new Array();
    
      for (key in headers) {
        data[0][headers[key]] = key;
      }
    }
    
    
    function transformData_(data, options, transformFunc) {
      for (var i = 0; i < data.length; i++) {
        for (var j = 0; j < data*.length; j++) {
          transformFunc(data, i, j, options);
        }
      }
    }
    
    
    function isObject_(test) {
      return Object.prototype.toString.call(test) === '[object Object]';
    }
    
    
    function isObjectArray_(test) {
      for (var i = 0; i < test.length; i++) {
        if (isObject_(test*)) {
          return true; 
        }
      }  
    
      return false;
    }
    
    
    function includeXPath_(query, path, options) {
      if (!query) {
        return true; 
      } else if (Array.isArray(query)) {
        for (var i = 0; i < query.length; i++) {
          if (applyXPathRule_(query*, path, options)) {
            return true; 
          }
        }  
      } else {
        return applyXPathRule_(query, path, options);
      }
      
      return false; 
    };
    
    
    function applyXPathRule_(rule, path, options) {
      return path.indexOf(rule) == 0; 
    }
    
    
    function defaultTransform_(data, row, column, options) {
      if (!data[row][column]) {
        if (row < 2 || hasOption_(options, "noInherit")) {
          data[row][column] = "";
        } else {
          data[row][column] = data[row-1][column];
        }
      } 
    
      if (!hasOption_(options, "rawHeaders") && row == 0) {
        if (column == 0 && data[row].length > 1) {
          removeCommonPrefixes_(data, row);  
        }
        
        data[row][column] = toTitleCase_(data[row][column].toString().replace(/[\/\_]/g, " "));
      }
      
      if (!hasOption_(options, "noTruncate") && data[row][column]) {
        data[row][column] = data[row][column].toString().substr(0, 256);
      }
    
      if (hasOption_(options, "debugLocation")) {
        data[row][column] = "[" + row + "," + column + "]" + data[row][column];
      }
    }
    
    
    function removeCommonPrefixes_(data, row) {
      var matchIndex = data[row][0].length;
    
      for (var i = 1; i < data[row].length; i++) {
        matchIndex = findEqualityEndpoint_(data[row][i-1], data[row]*, matchIndex);
    
        if (matchIndex == 0) {
          return;
        }
      }
      
      for (var i = 0; i < data[row].length; i++) {
        data[row]* = data[row]*.substring(matchIndex, data[row]*.length);
      }
    }
    
    
    function findEqualityEndpoint_(string1, string2, stopAt) {
      if (!string1 || !string2) {
        return -1; 
      }
      
      var maxEndpoint = Math.min(stopAt, string1.length, string2.length);
      
      for (var i = 0; i < maxEndpoint; i++) {
        if (string1.charAt(i) != string2.charAt(i)) {
          return i;
        }
      }
      
      return maxEndpoint;
    }
      
    
    
    function toTitleCase_(text) {
      if (text == null) {
        return null;
      }
      
      return text.replace(/\w\S*/g, function(word) { return word.charAt(0).toUpperCase() + word.substr(1).toLowerCase(); });
    }
    
    
    function hasOption_(options, option) {
      return options && options.indexOf(option) >= 0;
    }
    

    A questo punto sono 3 i tipi di chiamata per ottenere i dati che ci interessano:

    =ImportJSON("https://youtube.googleapis.com/youtube/v3/channels?part=snippet%2CcontentDetails%2Cstatistics&forUsername=giorgiotave&key=MYAPYKEY")
    
    =ImportJSON("https://youtube.googleapis.com/youtube/v3/channels?part=snippet%2CcontentDetails%2Cstatistics&id=UCjL9qnLkh-tLCNHlHA80vkw&key=MYAPYKEY")
    
    =ImportJSON("https://youtube.googleapis.com/youtube/v3/search?part=snippet&channelId=UCjL9qnLkh-tLCNHlHA80vkw&maxResults=2&order=date&key=MYAPYKEY")
    
    =ImportJSON("https://youtube.googleapis.com/youtube/v3/videos?part=snippet%2CcontentDetails%2Cstatistics&id=1mMuAxzYM_0&key=MYAPYKEY")
    

    Il primo individua il ChannelID in base alla username (in alternativa, se conosciamo già il channel ID si può usare il secondo)
    Il Terzo serve per ottenere lD dei video pubblicati
    Il quarto fornisce i dettagli per ogni video


    overclokk 1 Risposta
  • Moderatore

    @giorgiotave ha detto in API di YouTube: riusciamo a fare un sito per piccole statistiche?:

    vai, riusciamo a partire con questa disposizione?
    Così faccio partire la classe, appena ho almeno il form, faccio partire il tutto.

    Riesco a guardarci questo fine settimana, non prima.


    mirkomassarutto giorgiotave 2 Risposte
  • User Attivo

    @overclokk ti ho mandato un link in chat 😉


  • Moderatore

    @mirkomassarutto In Google script c'è anche già lAPI per collegare con Youtube senza leggere il json e senza aggiungere app key, Giorgio aggiungerà lo script nel suo account e il gioco è fatto.


    mirkomassarutto 1 Risposta
  • User Attivo

  • Community Manager

    @overclokk ha detto in API di YouTube: riusciamo a fare un sito per piccole statistiche?:

    @giorgiotave ha detto in API di YouTube: riusciamo a fare un sito per piccole statistiche?:

    vai, riusciamo a partire con questa disposizione?
    Così faccio partire la classe, appena ho almeno il form, faccio partire il tutto.

    Riesco a guardarci questo fine settimana, non prima.

    Perfetto 🙂

    GRANDI


  • Moderatore

    Eccomi, allora procediamo così:

    @giorgiotave dovresti nel tuo google drive creare una cartella dedicata (serve solo per tenere separato il progetto dal resto dei tuoi file) e poi dare accesso a me e @mirkomassarutto (ti passiamo la mail in chat) così creiamo direttamente il form e lo spreadsheet collegato.

    I form sono pubblici conoscendone la url, quello che si può fare è limitare a 1 risposta per utente, gli utenti per rispondere devono avere un account google.

    Poi per limitare ulteriormente sarebbe utile metterlo visibile sono per gli iscritti del forum?

    Al momento l'unica validazione che ho scritto è quella se l'ID del canale è corretta.

    L'ID del canale è l'unico mezzo sicuro che ci consente di reperire le info (non cambia mai come il nome), ho fatto delle prove con il nome del canale e non funziona con tutti quelli che ho provato (non sono stato a impazzire per capire il perché), poi, raga, se uno vuole fare lo youtuber dovrà imparare anche quale sia l'ID del proprio canale 😀

    Nello spreadsheet collegato al form meglio salvare solo le informazioni attuali, tutti gli altri calcoli (i vari salti iscritti, video, ecc) si fanno in un'altro foglio perché poi si dovra creare il "database" con lo storico ma un passo alla volta.


    giorgiotave 1 Risposta
  • Community Manager

    @overclokk fatto, passatemi le email 🙂

    GRANDISSIMI


  • Moderatore

    Intanto ho caricato i file del form e il foglio di calcolo


  • Moderatore

    Al momento solo l'ID è obbligatorio, finché facciamo dei test va bene così che se no si impazzisce a compilare tutti i campi.

    Per eseguire lo script al submit ho aggiunto un "attivatore" nell'editor degli scripts

    Il modula salva nel foglio "iscritti", poi il resto dei dati usiamo gli altri fogli.

    Ora procediamo con i test per vedere come e cosa migliorare nel form.


    giorgiotave 1 Risposta