├── todo.md ├── README.md ├── public ├── js │ ├── app.js │ ├── index.js │ ├── controllers.js │ └── csv.min.js └── views │ └── index.html ├── test.csv ├── package.json ├── app └── helpers │ └── segment.js ├── .gitignore ├── server.js └── config └── routes.js /todo.md: -------------------------------------------------------------------------------- 1 | 1. all errors must propogate to client. 2 | 2. add error handling prior to server-side request being initiated. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Import 2 | 3 | You can now import a CSV directly into Segment for you to see what the data looks like. 4 | -------------------------------------------------------------------------------- /public/js/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Declare app level module which depends on services, etc. ==================== 4 | var segImport = angular.module('segImport', []); -------------------------------------------------------------------------------- /test.csv: -------------------------------------------------------------------------------- 1 | type,userId,traits.company,traits.email,traits.favorite_band 2 | identify,a3810,segment,andy@segment.io,null 3 | identify,b1238,mixpanel,steve@mixpanel.com,nickelback 4 | identify,b0099,01,bob@01.com,0.1 5 | identify,b8877,01,bob@02.com,.1 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "import", 3 | "description": "Import UI for Segment.", 4 | "version": "0.0.1", 5 | "author": "Andy Jiang", 6 | "main": "server.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/lambtron/segment-import" 10 | }, 11 | "engines": { 12 | "node": "0.11.14", 13 | "npm": "1.1.x" 14 | }, 15 | "private": true, 16 | "dependencies": { 17 | "express": "^4.0.0", 18 | "http-assert": "^1.5.0", 19 | "request": "^2.88.2", 20 | "underscore": "latest" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/helpers/segment.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Segment helper library. 3 | */ 4 | 5 | (function() { 6 | var request = require('request'); 7 | var path = 'https://api.segment.io/v1/import/'; 8 | 9 | module.exports = { 10 | batchImport: function batchImport(writeKey, batch, fn) { 11 | var auth = { 12 | user: writeKey || '', 13 | pass: '' 14 | }; 15 | var opts = { 16 | uri: path, 17 | method: 'POST', 18 | timeout: 50000, 19 | followRedirect: true, 20 | maxRedirects: 10, 21 | auth: auth, 22 | body: batch, 23 | json: true 24 | }; 25 | request(opts, fn); 26 | } 27 | }; 28 | 29 | }()); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Deployed apps should consider commenting this line out: 24 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 25 | node_modules 26 | 27 | ./config/config.js 28 | 29 | # Users Environment Variables 30 | .lock-wscript -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Initialize server =========================================================== 4 | var express = require('express') 5 | , http = require('http') 6 | , app = express() 7 | , server = http.createServer(app) 8 | , port = process.env.PORT || 3000; 9 | 10 | // Configuration =============================================================== 11 | app.set('views', __dirname + 'public/views'); 12 | app.use(express.static(__dirname + '/public')); 13 | app.use(express.bodyParser()); 14 | 15 | // Routes ====================================================================== 16 | require('./config/routes.js')(app); 17 | 18 | // New relic. 19 | // require('newrelic'); 20 | 21 | // Listen (start app with node server.js) ====================================== 22 | server.listen(port, function() { 23 | console.log("App is now listening on port " + port); 24 | }); -------------------------------------------------------------------------------- /config/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function() { 4 | 5 | /** 6 | * Import helpers ============================================================ 7 | */ 8 | var Segment = require('../app/helpers/segment.js'); 9 | var assert = require('http-assert'); 10 | var ok = require('assert'); 11 | 12 | // Public functions. ========================================================= 13 | module.exports = function(app) { 14 | // API routes ============================================================== 15 | app.post('/api/import', function(req, res) { 16 | // Send request to Segment. 17 | // Need writeKey 18 | var writeKey = req.body.writeKey; 19 | var batch = { 20 | batch: req.body.batch 21 | }; 22 | 23 | Segment.batchImport(writeKey, batch, function(err, http, body) { 24 | if (err) 25 | res.send(err, 400); 26 | 27 | console.log(body); 28 | 29 | res.send(body, 200); 30 | }); 31 | }); 32 | 33 | // Application routes ====================================================== 34 | app.get('/*', function(req, res) { 35 | res.sendfile('index.html', {'root': './public/views/'}); 36 | }); 37 | }; 38 | 39 | }()); -------------------------------------------------------------------------------- /public/js/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | window.onload = function() { 4 | var fileInput = document.getElementById('fileInput'); 5 | var fileDisplayArea = document.getElementById('fileDisplayArea'); 6 | var csvFile = {}; 7 | 8 | fileInput.addEventListener('change', function(e) { 9 | // Use DOM to get AngularJS root scope. 10 | var scope = angular.element(this).scope(); 11 | 12 | // Reset firebase and local data store. 13 | scope.$apply(function() { 14 | scope.csv.removeAll(); 15 | }); 16 | 17 | var file = fileInput.files[0]; 18 | var textType = /text.*/; 19 | 20 | if (file.type.match(textType)) { 21 | var reader = new FileReader(); 22 | 23 | reader.onload = function(e) { 24 | var csvString = reader.result; 25 | // Clean the headers. 26 | var firstLine = csvString.split('\n')[0]; 27 | var cleanedFirstLine = firstLine.replace(/\s+/g, '_'); 28 | csvString = csvString.replace(firstLine, cleanedFirstLine); 29 | 30 | // Parse csv. 31 | var csv = new CSV(csvString); 32 | scope.$apply(function() { 33 | scope.csv.addArray(csv); 34 | }); 35 | }; 36 | 37 | reader.readAsText(file); 38 | } else { 39 | fileDisplayArea.innerText = "File not supported!" 40 | }; 41 | }); 42 | } -------------------------------------------------------------------------------- /public/js/controllers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | segImport.controller('mainController', ['$scope', '$http', 4 | function($scope, $http) 5 | { 6 | var csv = $scope.csv = {}; 7 | 8 | csv.array = []; // These are the rows. 9 | csv.JSON = []; // JSON object 10 | csv.JSONString = ''; 11 | csv.writeKey = ''; 12 | 13 | // Take csv string and turn it into array. 14 | csv.addArray = function addArray(csv) { 15 | csv.forEach(function(row) { 16 | this.array.push(row); 17 | }.bind(this)); 18 | this.arrayToJSON(); 19 | }; 20 | 21 | // Empty rows in csv.array. 22 | csv.removeAll = function removeAll() { 23 | this.array.length = 0; 24 | }; 25 | 26 | // Convert csv.array to csv.JSON. 27 | csv.arrayToJSON = function arrayToJSON() { 28 | 29 | var headers = this.array[0]; 30 | this.JSON.length = 0; 31 | 32 | for (var i = 1; i < this.array.length; i ++) { 33 | var obj = {}; 34 | var currentLine = this.array[i]; 35 | for (var j = 0; j < headers.length; j ++) { 36 | // Keep Nulls, ints and floats 37 | switch (true) { 38 | case currentLine[j] == 'null': 39 | currentLine[j] = null 40 | break 41 | case /^[0-9]+$/.test(currentLine[j]): 42 | currentLine[j] = parseInt(currentLine[j]) 43 | break 44 | case /^[0-9]*\.[0-9]+$/.test(currentLine[j]): 45 | currentLine[j] = parseFloat(currentLine[j]) 46 | break 47 | } 48 | if (headers[j].indexOf('.') > 0) { 49 | var prefix = headers[j].substring(0, headers[j].indexOf('.')); 50 | var suffix = headers[j].substring(headers[j].indexOf('.') + 1); 51 | if (!obj[prefix]) 52 | obj[prefix] = {}; 53 | obj[prefix][suffix] = currentLine[j] ; 54 | } else { 55 | obj[headers[j]] = currentLine[j]; 56 | } 57 | } 58 | this.JSON.push(obj); 59 | } 60 | this.JSONString = JSON.stringify(this.JSON, null, 2); 61 | }; 62 | 63 | // Post csv.JSON to end point. 64 | csv.importJSON = function importJSON() { 65 | console.log(this.JSON); 66 | $http.post('/api/import', {batch: this.JSON, writeKey: this.writeKey}) 67 | .success(function(err, data) { 68 | console.log(err); 69 | console.log(data); 70 | }) 71 | .error(function(err, data) { 72 | console.log(err); 73 | console.log(data); 74 | }); 75 | }; 76 | }]); -------------------------------------------------------------------------------- /public/views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 || 55 | {{ header }} 56 | | 57 |
| 62 | {{ item }} 63 | | 64 |
{{ csv.JSONString }}
75 |