Example #1
0
File: app.js Project: okize/trendie
exports.getTrends = function (profileId, username, password, jsonOut) {

  // local vars
  var dateFormat = 'YYYY-MM-DD';
  var dateToday = moment().format(dateFormat);
  var dateOneMonthAgo = moment(dateToday).subtract(28, 'days').format(dateFormat);
  var dateSixMonthsAgo = moment(dateToday).subtract(183, 'days').format(dateFormat);
  var dateSevenMonthsAgo = moment(dateSixMonthsAgo).subtract(28, 'days').format(dateFormat);
  var dateYearAgo = moment(dateToday).subtract(365, 'days').format(dateFormat);
  var dateYearAndMonthAgo = moment(dateYearAgo).subtract(28, 'days').format(dateFormat);
  var gaRequestOpts = {
    lastMonthVisits: getGaRequestOpts(profileId, dateOneMonthAgo, dateToday, 'ga:visits', ''),
    lastMonthBrowsers: getGaRequestOpts(profileId, dateOneMonthAgo, dateToday, 'ga:visits', 'ga:browser,ga:browserVersion'),
    sixMonthsAgoVisits: getGaRequestOpts(profileId, dateSevenMonthsAgo, dateSixMonthsAgo, 'ga:visits', ''),
    sixMonthsAgoBrowsers: getGaRequestOpts(profileId, dateSevenMonthsAgo, dateSixMonthsAgo, 'ga:visits', 'ga:browser,ga:browserVersion'),
    yearAgoVisits: getGaRequestOpts(profileId, dateYearAndMonthAgo, dateYearAgo, 'ga:visits', ''),
    yearAgoBrowsers: getGaRequestOpts(profileId, dateYearAndMonthAgo, dateYearAgo, 'ga:visits', 'ga:browser,ga:browserVersion')
  };
  var errMsgSent = false;
  var progress = new Progger();
  var ga;

  // @todo: this sucks
  // I think the only uncaught is with GA logins failing (ie. credentials are incorrect)
  process.on('uncaughtException', function (err) {

    // stop progress ticker if it's running
    if (typeof progress === 'object' && progress.isRunning()) {
      progress.stop();
    }

    if (err.message) {
      return console.error('\n' + err.message.red);
    }

    return console.error('\nAn unknown error has occurred!'.red);

  });

  // returns a promise for a GA request
  function gaRequest(description) {

    var d = Q.defer();

    ga.get(gaRequestOpts[description], function (err, entries) {

      // reject promise or resolve with entries
      if (err) {

        // this is a hacky way of preventing error message displaying multiple times
        if (err.message === 'User does not have sufficient permissions for this profile.') {

          if (!errMsgSent) {
            progress.stop();
            errMsgSent = true;
            console.log('\n' + 'This GA user does not have permissions to read profile: '.red + profileId);
          }

          d.reject('');

        } else {

          d.reject(err);

        }

      } else {

        d.resolve({description: description, entries: entries});

      }

    });

    return d.promise;

  }

  // start timer
  console.time('time-to-run');

  if (!jsonOut) {
    process.stdout.write('\nDownloading data from Google Analytics');
  }

  // start progress ticker
  progress.start();

  // create new google analytics object
  ga = new GA.GA({'user': username, 'password': password});

  // start GA session
  ga.login(function (err, token) {

    if (err) {
      return console.log('error logging in to Google Analytics'.error);
    }

    // complete a queue of promise-returning functions
    Q.allSettled([
      gaRequest('lastMonthVisits'),
      gaRequest('lastMonthBrowsers'),
      gaRequest('sixMonthsAgoVisits'),
      gaRequest('sixMonthsAgoBrowsers'),
      gaRequest('yearAgoVisits'),
      gaRequest('yearAgoBrowsers')
    ])
    .then(function (results) {

      var passed = [];

      results.forEach(function (result) {
        if (result.state === "fulfilled") {
          passed.push(result.value);
        } else {
          console.error(result.reason);
        }
      });

      return processResults(passed);

    })
    .then(function (processed) {

      if (jsonOut) {
        process.stdout.write('\n\n' + JSON.stringify(processed, null, '  '));
      }

      // get table for printing to the command line
      return getCliTable(processed);

    })
    .then(function (table) {

      // stop progress ticker
      progress.stop();
      console.log('\n');

      // if there were errors getting the data don't output empty table
      if (table.length <= 1) {
        return console.error('No data available!'.red);
      }

      if (!jsonOut) {
        console.log(table.toString());
      }

    })
    .fail(function (error) {

      // stop progress ticker
      progress.stop();

      // if any errors happened in the promise-chain, output here
      console.log('\n');
      console.error('fail: ', error.stack);

    })
    .finally(function () {

      // end timer
      if (!jsonOut) {
        console.log('\n');
        console.timeEnd('time-to-run');
      }

    });

  });

};
Example #2
0
module.exports = function (userUrl, pretty) {

  var requestOpts, progress;

  requestOpts = {
    uri: userUrl,
    headers: {
      'User-Agent': userAgentMap['ie']
    }
  };

  // progress ticker
  progress = new Progger();

  process.stdout.write('\nRetrieving CSS data from ' + userUrl);

  // start progress ticker
  progress.start();

  // request url and look for linked stylesheets and inline styles
  request(requestOpts, function (error, response, body) {

    var $, inlinestyles, inlinestylesTotal, stylesheets, stylesheetTotal,
        fakeRequest, stylesheetList = [], requests = [];

    if (error) {

      // stop progress ticker
      progress.stop();

      return console.error(error);

    }

    if (response.statusCode !== 200) {

      // stop progress ticker
      progress.stop();

      return console.error('Error connecting to ' + userUrl + '. Status code: ' + response.statusCode);

    }

    // load response body into variable and use jquery to parse it for styles
    $ = cheerio.load(body),
    stylesheets = $('link[rel="stylesheet"]'),
    stylesheetTotal = stylesheets.length,
    inlinestyles = $('style'),
    inlinestylesTotal = inlinestyles.length;

    // check inline styles, then check linked stylesheets, then parse, then output
    async.waterfall([

      // if there are inline styles, 'fake' a request object
      // there's a better way to do this...
      function (callback) {

        if (inlinestylesTotal > 0) {

          for (var i = 0; i < inlinestylesTotal; i++) {

            fakeRequest = { request: {} }; // super kludge
            fakeRequest.cssType = 'inline';
            fakeRequest.request.href = 'inline-style-block-' + (i + 1);
            fakeRequest.body = inlinestyles[i].children[0] ? inlinestyles[i].children[0].data : '';
            requests.push(fakeRequest);

          }

          callback(null, requests);

        } else {
          callback(null, requests);
        }

      },

      // if there are linked stylesheets populate an array with their urls
      function (requests, callback) {

        if (stylesheetTotal > 0) {

          for (var i = 0; i < stylesheetTotal; i++) {
            stylesheetList.push( utils.normalizeUrl( stylesheets[i].attribs.href, userUrl ) );
          }

          // get linked stylesheets
          async.map(stylesheetList, utils.getUrl, function (error, results) {

            if (error) {

              // stop progress ticker
              progress.stop();

              console.error('error requesting stylesheets');

            } else {

              for (var j = 0, len = results.length; j < len; j++) {
                requests.push(results[j]);
              }

              callback(null, requests);

            }

          });

        } else {
          callback(null, requests);
        }

      },

      // pipe each style request into parseCss and pass along results
      function (requests, callback) {

        async.map(requests, parseCss, function (error, results) {

          if (error) {

            // stop progress ticker
            progress.stop();

            console.error('error parsing styles');

          } else {

            callback(null, results);

          }

        });

      },

      // add a properties to track the original url
      function (results, callback) {

        var result = {
          page: userUrl,
          result: results
        };

        // return json
        callback(null, result);

      }

    ],

    // all done
    function (error, result) {

      // stop progress ticker
      progress.stop();

      if (error) {
        return console.error(error);
      }

      if (pretty) {

        // pretty print results
        prettyPrint(result);

      } else {

        // convert to json
        console.log(JSON.stringify(result, null, 2));

      }

    });

  });

};