├── DF14Chart.apexp ├── DF14ReportChatter.apex ├── JSObject.apex ├── LICENSE ├── MassEmail ├── Dockerfile ├── nginx.conf └── src │ ├── index.js │ ├── index.mst │ ├── package.json │ └── table.mst ├── MultiOrgDashboard ├── index.js ├── package.json └── template.mst ├── PublicDashboard ├── Dockerfile └── src │ ├── d3Chart.mst │ ├── index.js │ └── package.json ├── README.md ├── ReportPDF ├── ReportPDFController.cls ├── ReportResultsTraversor.cls ├── ReportResultsVisitor.cls ├── logo.jpg ├── reportPDF.page └── sample.pdf ├── autoRefreshingChart.page ├── customDashboard.page ├── d3ReportChart.page ├── dotChartgRaphael.page ├── googleMapReportChart ├── googleTableReport ├── leafletUtil.js ├── ngAnalyticsApi.js ├── ngColorCode.js ├── ngSfdcChart.js ├── ngSfdcDashApiService.js ├── reportToChatter.apex └── scorecard.page /DF14Chart.apexp: -------------------------------------------------------------------------------- 1 | 2 | 3 | Report API Demo 4 | 5 | 6 | 7 | 40 | 41 | 42 | 43 | 46 | 47 | -------------------------------------------------------------------------------- /DF14ReportChatter.apex: -------------------------------------------------------------------------------- 1 | public class ReportAPI { 2 | public String repId { get; set; } 3 | public String msg { get; set; } 4 | 5 | public void post() { 6 | Id reportId = this.repId; 7 | String msg = this.msg; 8 | Reports.ReportResults results = Reports.ReportManager.runReport(reportId); 9 | List posts = new List(); 10 | for(Reports.GroupingValue user: (( Reports.Dimension)results.getGroupingsDown()).getGroupings()) { 11 | String factMapKey = user.getKey()+'!T'; 12 | Reports.ReportFact factMapEntry = ((Map)results.getFactMap()).get(factMapKey); 13 | String numCases = ((List)factMapEntry.getAggregates()).get(0).getLabel(); 14 | FeedItem post = new FeedItem(); 15 | post.parentId = (Id)user.getValue(); 16 | post.body = msg; 17 | posts.add(post); 18 | } 19 | insert posts; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /JSObject.apex: -------------------------------------------------------------------------------- 1 | public class JSObject { 2 | Map source; 3 | private JSObject() {} 4 | 5 | public static JSObject wrap(Object o) { 6 | JSObject ret = new JSObject(); 7 | ret.source = (Map) o; 8 | return ret; 9 | } 10 | 11 | public Object get(String key) { 12 | return source.get(key); 13 | } 14 | 15 | public JSObject getAsObject(String key) { 16 | return wrap(this.get(key)); 17 | } 18 | 19 | public List getAsList(String key) { 20 | List ret = new List(); 21 | for(Object item: (List)this.get(key)) { 22 | ret.add(JSObject.wrap(item)); 23 | } 24 | return ret; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 arun-sfdc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /MassEmail/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM dockerhubtrial/base5 2 | RUN mkdir -p /opt/app && cd /opt/app && git init && git pull https://github.com/arun-sfdc/Analytics-API.git 3 | WORKDIR /opt/app/MassEmail 4 | RUN cd src/ && npm install 5 | EXPOSE 443 6 | CMD service nginx start && nodejs ./src/index.js 7 | -------------------------------------------------------------------------------- /MassEmail/nginx.conf: -------------------------------------------------------------------------------- 1 | events { 2 | worker_connections 4096; ## Default: 1024 3 | } 4 | 5 | http { 6 | upstream nodejs { 7 | server localhost:9000; 8 | } 9 | 10 | server { 11 | 12 | listen 443 ssl; 13 | server_name nginx; 14 | 15 | ssl on; 16 | ssl_certificate /opt/app/MassEmail/server.crt; 17 | ssl_certificate_key /opt/app/MassEmail/server.key; 18 | ssl_session_cache shared:SSL:10m; 19 | 20 | location / { 21 | 22 | proxy_pass http://nodejs; # my existing apache instance 23 | proxy_set_header Host $host; 24 | 25 | # re-write redirects to http as to https, example: /home 26 | proxy_redirect http:// https://; 27 | } 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /MassEmail/src/index.js: -------------------------------------------------------------------------------- 1 | // DEPS 2 | var express = require('express'); 3 | var fs = require('fs'); 4 | var Mustache = require('mustache'); 5 | var jsforce = require('jsforce'); 6 | var morgan = require('morgan'); 7 | var _ = require('underscore'); 8 | var compression = require('compression') 9 | 10 | // Constants 11 | var PORT = 9000; 12 | var TEMPLATE = './src/index.mst'; 13 | var TEMPLATE_TABLE = './src/table.mst'; 14 | var PAGESIZE = 2000; 15 | var OAUTH2 = new jsforce.OAuth2({ 16 | clientId : process.env.CID, 17 | clientSecret : process.env.CS, 18 | redirectUri : process.env.URL 19 | }); 20 | 21 | // PATCH JSFORCE for Turbo Pilot 22 | var EXECUTE_ASYNC = function(options, callback) { 23 | options = options || {}; 24 | if (_.isFunction(options)) { 25 | callback = options; 26 | options = {}; 27 | } 28 | var url = [ this._conn._baseUrl(), "analytics", "reports", this.id, "instances" ].join('/'); 29 | url += "?includeDetails=true&queryable=true"; 30 | var params = { method : 'POST', url : url, body: "" }; 31 | if (options.metadata) { 32 | params.headers = { "Content-Type" : "application/json" }; 33 | params.body = JSON.stringify(options.metadata); 34 | } 35 | return this._conn.request(params).thenCall(callback); 36 | }; 37 | var RETRIEVE_PAGE = function(instance, mylastRowIdFetched, callback) { 38 | options = { 39 | "paginationSpec": { 40 | "lastRowIdFetched": mylastRowIdFetched, 41 | "numRows": PAGESIZE 42 | } 43 | }; 44 | var url = [ this._conn._baseUrl(), "analytics", "reports", instance._report.id, "instances", instance.id].join('/'); 45 | var params = { method : 'POST', url : url, body: "" }; 46 | params.headers = { "Content-Type" : "application/json" }; 47 | params.body = JSON.stringify(options); 48 | return this._conn.request(params).thenCall(callback); 49 | }; 50 | 51 | 52 | // App 53 | var app = express(); 54 | app.use(morgan()); 55 | app.use(compression()); 56 | app.get('/auth', function(req, res) { 57 | res.redirect(OAUTH2.getAuthorizationUrl({ scope : 'full' })); 58 | }); 59 | app.get('/', function (req, res) { 60 | var code = req.query.code; 61 | if(!code) { 62 | res.redirect('/auth'); 63 | } 64 | var conn = new jsforce.Connection({ oauth2: OAUTH2 }); 65 | conn.authorize(code, function(err, userInfo) { 66 | conn.analytics.reports(function(err, reports) { 67 | if (err) { return console.error(err); } 68 | var reportId = reports[0].id; 69 | fs.readFile(TEMPLATE, function (err, data) { 70 | res.write(Mustache.render(data.toString(), {reportid: reportId, refreshToken: conn.refreshToken, accessToken: conn.accessToken, instanceUrl: conn.instanceUrl})); 71 | res.end(); 72 | }); 73 | }); 74 | }); 75 | }); 76 | app.get('/report', function (req, res) { 77 | console.log(new Date()); 78 | var conn = new jsforce.Connection({ 79 | oauth2 : OAUTH2, 80 | instanceUrl : req.query.instanceUrl, 81 | accessToken : req.query.accessToken, 82 | refreshToken : req.query.refreshToken 83 | }); 84 | var report = conn.analytics.report(req.query.reportId); 85 | report.executeAsync = EXECUTE_ASYNC; 86 | report.executeAsync({turbo: true}, function(err, instance) { 87 | if (err) { return console.error(err); } 88 | var pollToComplete = function() { 89 | var myinstance = report.instance(instance.id); 90 | myinstance.retrieve(function(err, result) { 91 | if (err) { return console.error(err); } 92 | if(result.attributes.status == 'Success') { 93 | var rows = []; 94 | myinstance.retrievePage = RETRIEVE_PAGE; 95 | var getNextPage = function(lastRow) { 96 | myinstance.retrievePage(myinstance, lastRow, function(err, pageresult) { 97 | if (err) { return console.error(err); } 98 | rows.push(pageresult.factMap['T!T'].rows); 99 | if(lastRow >= 100000) { 100 | fs.readFile(TEMPLATE_TABLE, function (err, data) { 101 | res.write(Mustache.render(data.toString(), {data: rows})); 102 | res.end(); 103 | }); 104 | console.log(new Date()); 105 | } else { 106 | getNextPage(lastRow+PAGESIZE); 107 | } 108 | }); 109 | }; 110 | getNextPage(0); 111 | } else { 112 | pollToComplete(); 113 | } 114 | }); 115 | }; 116 | pollToComplete(); 117 | }); 118 | }); 119 | app.listen(PORT); 120 | app.on('connection', function(socket) { 121 | console.log("A new connection was made by a client."); 122 | socket.setTimeout(300 * 1000); 123 | }) 124 | -------------------------------------------------------------------------------- /MassEmail/src/index.mst: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Loading report {{reportid}} with Turbo Report API. 5 | 6 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /MassEmail/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SalesforceAnalyticsOnPublicSites", 3 | "version": "0.0.1", 4 | "description": "Built using Node.js, JSForce and Docker/CentOS", 5 | "author": "@arun_sfdc", 6 | "dependencies": { 7 | "express": "3.2.4", 8 | "mustache": "0.8.2", 9 | "morgan":"1.1.1", 10 | "jsforce":"1.4.1", 11 | "underscore":"*", 12 | "compression":"*" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MassEmail/src/table.mst: -------------------------------------------------------------------------------- 1 | 2 | {{#data}} 3 | {{#.}} 4 | 5 | {{#dataCells}} 6 | 7 | {{label}} 8 | 9 | {{/dataCells}} 10 | 11 | {{/.}} 12 | {{/data}} 13 | 14 | -------------------------------------------------------------------------------- /MultiOrgDashboard/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var fs = require('fs'); 3 | var Mustache = require('mustache'); 4 | var jsforce = require('jsforce'); 5 | var app = express(); 6 | app.listen(9000); 7 | app.get('/', function(req, res) { 8 | var METRICS = [{ 9 | reportid: '', username: '', password: '', loginurl: 'https://na1.salesforce.com', chart: 'pie'}, // chart = pie, bar or table 10 | { reportid: '', username: '', password: '', loginurl: 'https://na1.salesforce.com', chart: 'bar'} ]; 11 | 12 | function getReportResults(metric, callback) { 13 | var conn = new jsforce.Connection({ 14 | loginUrl: metric.loginurl 15 | }); 16 | conn.login(metric.username, metric.password, function(err, resp) { 17 | var report = conn.analytics.report(metric.reportid); 18 | report.executeAsync(function(err, instance) { 19 | var pollToComplete = function() { 20 | report.instance(instance.id).retrieve(function(err, result) { 21 | if (result.attributes.status == 'Success') 22 | callback(result); 23 | else 24 | pollToComplete(); 25 | }); 26 | }; 27 | pollToComplete(); 28 | }); 29 | }); 30 | } 31 | var result = []; 32 | var afterResult = function(reportResult) { 33 | reportResult.id = result.length; 34 | reportResult.chart = METRICS[result.length].chart; 35 | result.push(reportResult); 36 | if (result.length < METRICS.length) { 37 | getReportResults(METRICS[result.length], afterResult); 38 | } else { 39 | fs.readFile('./template.mst', function(err, data) { 40 | res.write(Mustache.render(data.toString(), { 41 | data: result, 42 | string: JSON.stringify(result) 43 | })); 44 | res.end(); 45 | }); 46 | } 47 | }; 48 | getReportResults(METRICS[0], afterResult); 49 | }); 50 | -------------------------------------------------------------------------------- /MultiOrgDashboard/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SalesforceMultiOrgDashboard", 3 | "version": "0.0.1", 4 | "description": "View all your Salesforce instances in a single Dashboard", 5 | "author": "@arun_sfdc", 6 | "dependencies": { 7 | "express": "3.2.4", 8 | "jsforce": "1.2.0", 9 | "mustache": "0.8.2" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /MultiOrgDashboard/template.mst: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | {{#data}} 10 | 11 | {{attributes.reportName}} 12 | 13 | 14 | {{/data}} 15 | 49 | 50 | -------------------------------------------------------------------------------- /PublicDashboard/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM dockerhubtrial/base 2 | RUN mkdir -p /opt/app && cd /opt/app && git init && git pull https://github.com/arun-sfdc/Analytics-API.git 3 | WORKDIR /opt/app/PublicDashboard 4 | RUN cd src/ && npm install 5 | EXPOSE 9000 6 | CMD ["nodejs", "./src/index.js"] 7 | -------------------------------------------------------------------------------- /PublicDashboard/src/d3Chart.mst: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 43 | 44 | 45 | 46 | 47 | 48 | 54 | 55 | -------------------------------------------------------------------------------- /PublicDashboard/src/index.js: -------------------------------------------------------------------------------- 1 | // DEPS 2 | var express = require('express'); 3 | var fs = require('fs'); 4 | var Mustache = require('mustache'); 5 | var jsforce = require('jsforce'); 6 | var morgan = require('morgan'); 7 | 8 | // Constants 9 | var REPORTID = process.env.SFDC_REPORTID; 10 | var USERNAME = process.env.SFDC_USERNAME; 11 | var PASSWORD = process.env.SFDC_PASSWORD; 12 | var LOGIN_URL = process.env.SFDC_URL||'https://salesforce.com'; 13 | var CACHE_TIME = (process.env.SFDC_CACHEMINS || 0) * 60000; 14 | var PORT = 9000; 15 | var CACHEFILE = './src/cache'; 16 | var TEMPLATE = './src/d3Chart.mst'; 17 | 18 | // App 19 | var app = express(); 20 | app.use(morgan()); 21 | app.get('/', function (req, res) { 22 | var writeResponse = function(result) { 23 | fs.readFile(TEMPLATE, function (err, data) { 24 | res.write(Mustache.render(data.toString(), {reportResult: result})); 25 | res.end(); 26 | }); 27 | }; 28 | fs.stat(CACHEFILE, function(err, stats) { 29 | if(!err && new Date().getTime() - new Date(stats.mtime).getTime() < CACHE_TIME) { 30 | fs.readFile(CACHEFILE, 'utf-8', function(err, result){ 31 | if(!err) { 32 | writeResponse(result); 33 | } 34 | }); 35 | } else { 36 | var conn = new jsforce.Connection({loginUrl: LOGIN_URL}); 37 | conn.login(USERNAME, PASSWORD, function(err, resp) { 38 | if (err) { return console.error(err); } 39 | conn.analytics.reports(function(err, reports) { 40 | if (err) { return console.error(err); } 41 | var reportId = REPORTID||reports[0].id; 42 | var report = conn.analytics.report(reportId); 43 | report.executeAsync(function(err, instance) { 44 | if (err) { return console.error(err); } 45 | var pollToComplete = function() { 46 | report.instance(instance.id).retrieve(function(err, result) { 47 | if (err) { return console.error(err); } 48 | if(result.attributes.status == 'Success') { 49 | result = JSON.stringify(result); 50 | if(CACHE_TIME > 0) fs.writeFile(CACHEFILE, result, 'utf-8'); 51 | writeResponse(result); 52 | } else { 53 | pollToComplete(); 54 | } 55 | }); 56 | }; 57 | pollToComplete(); 58 | }); 59 | }); 60 | }); 61 | } 62 | }); 63 | }); 64 | app.listen(PORT); 65 | -------------------------------------------------------------------------------- /PublicDashboard/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SalesforceAnalyticsOnPublicSites", 3 | "version": "0.0.1", 4 | "description": "Built using Node.js, JSForce and Docker/CentOS", 5 | "author": "@arun_sfdc", 6 | "dependencies": { 7 | "express": "3.2.4", 8 | "jsforce": "1.2.0", 9 | "mustache": "0.8.2", 10 | "morgan":"1.1.1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Analytics-API 2 | ============= 3 | -------------------------------------------------------------------------------- /ReportPDF/ReportPDFController.cls: -------------------------------------------------------------------------------- 1 | public class ReportPDFController { 2 | private List headers = null; 3 | private Reports.ReportResults results = null; 4 | 5 | public List>> getSections() { 6 | Id reportId = ApexPages.currentPage().getParameters().get('reportId'); 7 | this.results = Reports.ReportManager.runReport(reportId); 8 | LabelVisitor v = new LabelVisitor(this.results); 9 | ReportResultsTraversor t = new ReportResultsTraversor(this.results, v); 10 | t.process(); 11 | return v.getSections(); 12 | } 13 | 14 | public List getReportHeaders() { 15 | if(headers != null) { 16 | return headers; 17 | } 18 | this.headers = new List(); 19 | // Grouping Header 20 | for(Reports.GroupingInfo column:results.getReportMetadata().getGroupingsDown()) { 21 | headers.add(results.getReportExtendedMetadata().getGroupingColumnInfo().get(column.getName()).getLabel()); 22 | } 23 | // Aggregates Header 24 | for(String column:results.getReportMetadata().getAggregates()) { 25 | headers.add(results.getReportExtendedMetadata().getAggregateColumnInfo().get(column).getLabel()); 26 | } 27 | return headers; 28 | } 29 | 30 | public class Cell { 31 | String value; 32 | String bgcolor ='#D8F6CE'; 33 | 34 | public Cell(String value, String bgcolor) { 35 | this.value = value; 36 | this.bgcolor = bgcolor; 37 | } 38 | 39 | public Cell(String value) { 40 | this.value = value; 41 | } 42 | 43 | public String getValue() { 44 | return this.value; 45 | } 46 | 47 | public String getBgColor() { 48 | return this.bgcolor; 49 | } 50 | 51 | } 52 | 53 | private class LabelVisitor implements ReportResultsVisitor { 54 | Reports.ReportResults result = null; 55 | List>> sections = new List>>(); 56 | List> rows = new List>(); 57 | String groupingKey = null; 58 | 59 | LabelVisitor(Reports.ReportResults result) { 60 | this.result = result; 61 | } 62 | 63 | public void processSummary(List down, List across, Reports.ReportFact fact) { 64 | if(null == this.groupingKey) { 65 | this.groupingKey = down.get(down.size() - 1).getKey(); 66 | } 67 | if(down.size() == 1 && this.groupingKey != down.get(down.size() - 1).getKey()) { 68 | this.sections.add(this.rows); 69 | this.rows = new List>(); 70 | this.groupingKey = down.get(down.size() - 1).getKey(); 71 | } 72 | List row = new List(); 73 | if(null != down) { 74 | for(Reports.GroupingValue g:down) { 75 | row.add(new Cell(g.getLabel())); 76 | } 77 | for(Integer i=0;i 100000 ? '#FF6666' :'#D8F6CE')); 83 | } 84 | rows.add(row); 85 | } 86 | 87 | List>> getSections() { 88 | return sections; 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /ReportPDF/ReportResultsTraversor.cls: -------------------------------------------------------------------------------- 1 | public class ReportResultsTraversor { 2 | private List down = new List(); 3 | Reports.ReportResults result = null; 4 | ReportResultsVisitor visitor = null; 5 | 6 | public ReportResultsTraversor(Reports.ReportResults result, ReportResultsVisitor visitor) { 7 | this.result = result; 8 | this.visitor = visitor; 9 | } 10 | 11 | public void process() { 12 | for(Reports.GroupingValue grouping: result.getGroupingsDown().getGroupings()) { 13 | processGrouping(grouping); 14 | } 15 | } 16 | 17 | private void processGrouping(Reports.GroupingValue grouping) { 18 | down.add(grouping); 19 | visitor.processSummary(down, null, result.getFactMap().get(grouping.getKey()+'!T')); 20 | if(grouping.getGroupings() != null && grouping.getGroupings().size() > 0) { 21 | for(Reports.GroupingValue subgrouping:grouping.getGroupings()) { 22 | processGrouping(subgrouping); 23 | } 24 | } 25 | down.remove(down.size() - 1); 26 | } 27 | } -------------------------------------------------------------------------------- /ReportPDF/ReportResultsVisitor.cls: -------------------------------------------------------------------------------- 1 | public interface ReportResultsVisitor { 2 | void processSummary(List down, List across, Reports.ReportFact fact); 3 | } -------------------------------------------------------------------------------- /ReportPDF/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arun-sfdc/Analytics-API/2157ceeb2ff6aa264762ae661a46ecb61f3d6ae3/ReportPDF/logo.jpg -------------------------------------------------------------------------------- /ReportPDF/reportPDF.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Weekly Status Report - March 18, 2014 6 | 7 | 8 | 9 | 10 | 11 | {!header} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {!cell.value} 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /ReportPDF/sample.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arun-sfdc/Analytics-API/2157ceeb2ff6aa264762ae661a46ecb61f3d6ae3/ReportPDF/sample.pdf -------------------------------------------------------------------------------- /autoRefreshingChart.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /customDashboard.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | Live Dashboards 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 37 | 40 | 41 | 42 | 43 | 44 | 45 | {{component.metadata.title}} 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /d3ReportChart.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /dotChartgRaphael.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /googleMapReportChart: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /googleTableReport: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /leafletUtil.js: -------------------------------------------------------------------------------- 1 | 2 | // simple util method 3 | // arguments - Leaflet map object, zoom, lat-long for center, array of points[lat, long, radium(0-100)] 4 | function drawLeafletMap(map, zoom, base, points) { 5 | var baseRadius = 6000; 6 | var osmUrl='http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'; 7 | var osmAttrib='Map data © OpenStreetMap contributors'; 8 | var osm = new L.TileLayer(osmUrl, {minZoom: zoom, maxZoom: zoom, attribution: osmAttrib}); 9 | var baseLayer = L.tileLayer(osmUrl, { 10 | attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA', 11 | maxZoom: zoom 12 | }).addTo(map); 13 | map.setView(new L.LatLng(base[0], base[1]), zoom); 14 | map.addLayer(osm); 15 | points.forEach(function(e, i, arr) { 16 | setTimeout(function() { 17 | var circle = L.circle([e[0], e[1]], e[2]*baseRadius, { 18 | color: 'red', 19 | fillColor: '#f03', 20 | fillOpacity: 0.5 21 | }).bindPopup(e[3]).addTo(map); 22 | }, (i+1)*100); 23 | }); 24 | }; 25 | 26 | // simple util method to geocode results 27 | // input is array for rows, the first cell of each row is the location to be geocoded 28 | // uses the free Google Geocode API, hence the a bit slow 29 | // cached using localstorage for faster subsequent access 30 | var asyncGeoCode = function(input, output) { 31 | var getGeoCode = function(e, resp) { 32 | var loc = resp.results[0].geometry.location; 33 | output.push([loc.lat, loc.lng, e[1], e[0]]); 34 | }; 35 | input.forEach(function(e, i, arr) { 36 | if(localStorage.getItem(e[0])) { 37 | getGeoCode(e, JSON.parse(localStorage.getItem(e[0]))); 38 | } else { 39 | var d = new Date().getTime(); 40 | while(new Date().getTime() - d <= 100) {} 41 | $.ajax('https://maps.googleapis.com/maps/api/geocode/json?address='+e[0], { 42 | success: function(resp) { 43 | if(resp.status == "OK") { 44 | localStorage.setItem(e[0], JSON.stringify(resp)); 45 | getGeoCode(e, resp); 46 | } 47 | } 48 | }); 49 | } 50 | }); 51 | }; 52 | -------------------------------------------------------------------------------- /ngAnalyticsApi.js: -------------------------------------------------------------------------------- 1 | // Call the init method with sessionId 2 | 3 | angular.module('analyticsApi', [], function($provide) { 4 | $provide.factory('analyticsApiService', ['$http', '$timeout', 5 | function($http, $timeout){ 6 | var ret = {}; 7 | ret.init = function(sessionId, instanceUrl) { 8 | ret.sessionId = sessionId; 9 | ret.instanceUrl = instanceUrl || ''; 10 | }; 11 | ret.getReportUrl = function (reportId) { 12 | return ret.instanceUrl + '/services/data/v30.0/analytics/reports/' + reportId+'/instances'; 13 | }; 14 | ret.pollAndFetch = function(pollUrl, afterDataFetch) { 15 | var pollFn = function() { 16 | $http({ 17 | url: pollUrl, 18 | method: 'GET', 19 | headers: { 20 | 'Authorization': 'Bearer ' + ret.sessionId 21 | }, 22 | cache: false 23 | }).success(function(response) { 24 | if(response.attributes.status == "Success") { 25 | afterDataFetch(response); 26 | } else { 27 | $timeout(pollFn, 200); 28 | } 29 | }); 30 | }; 31 | pollFn(); 32 | }; 33 | ret.fetchData = function (reportId, afterDataFetch) { 34 | $http({ 35 | url: ret.getReportUrl(reportId), 36 | method: 'POST', 37 | headers: { 38 | 'Authorization': 'Bearer ' + ret.sessionId 39 | }, 40 | cache: true 41 | }).success(function (response) { 42 | var getResults = function() { 43 | ret.pollAndFetch(response.url, afterDataFetch); 44 | }; 45 | $timeout(getResults, 200); 46 | }); 47 | }; 48 | ret.fetchReports = function(reportIds, afterDataFetch) { 49 | var reportResults = new Array(reportIds.length); 50 | angular.forEach(reportIds, function(reportId, key) { 51 | ret.fetchData(reportId, function(response) { 52 | reportResults[key] = response; 53 | var complete = true; 54 | for(var i=0;i=low) || (value<=high && value>=low)) 8 | item.style.backgroundColor = color; 9 | }); 10 | } 11 | }; 12 | }]); 13 | }); 14 | -------------------------------------------------------------------------------- /ngSfdcChart.js: -------------------------------------------------------------------------------- 1 | // Depends 2 | // 4 | // 5 | // 6 | 7 | google.load("visualization", "1", {packages:["corechart"]}); 8 | angular.module('sfdcCharts', []).config(function($provide, $compileProvider, $filterProvider) { 9 | $compileProvider.directive('sfdcChart', function() { 10 | return { 11 | restrict: 'E', 12 | scope: { 13 | data: '=data', 14 | type: '=type' 15 | }, 16 | template: '', 17 | link: function(scope, elem, attrs) { 18 | if(scope.type === 'pie') { 19 | var chartPoints = []; 20 | chartPoints.push(['Grouping', 'Metric']); 21 | angular.forEach(scope.data.groupingsDown.groupings, function (value, index) { 22 | chartPoints.push([value.label, scope.data.factMap[value.key + "!T"].aggregates[0].value]); 23 | }); 24 | var myData = google.visualization.arrayToDataTable(chartPoints); 25 | new google.visualization.PieChart(elem.children()[0]).draw(myData, {}); 26 | } else if(scope.type === 'bar') { 27 | var chart = nv.models.multiBarChart(); 28 | var chartData = []; 29 | angular.forEach(scope.data.groupingsDown.groupings, function(de, di) { 30 | var values = []; 31 | chartData.push({"key":de.label, "values": values}); 32 | angular.forEach(de.groupings, function(ae, ai) { 33 | values.push({"x": ae.label, "y": scope.data.factMap[ae.key+"!T"].aggregates[0].value}); 34 | }); 35 | }); 36 | d3.select(elem.children()[0].children[0]).datum(chartData).transition().duration(500).call(chart); 37 | } 38 | } 39 | }; 40 | }); 41 | }); 42 | 43 | -------------------------------------------------------------------------------- /ngSfdcDashApiService.js: -------------------------------------------------------------------------------- 1 | // Call the init method with sessionId, dashboardId and afterDataFetchFn 2 | // Refresh will automatically poll to completion 3 | // Poll will call afterDataFetchFn after completion. 4 | 5 | angular.module('dashApi', [], function($provide) { 6 | $provide.factory('dashApiService', ['$http', '$timeout', 7 | function($http, $timeout){ 8 | var ret = {}; 9 | ret.init = function(sessionId, dashboardId, afterDataFetch, instanceUrl) { 10 | ret.sessionId = sessionId; 11 | ret.dashboardId = dashboardId; 12 | ret.afterDataFetch = afterDataFetch; 13 | ret.instanceUrl = instanceUrl || ''; 14 | }; 15 | ret.getDashboardUrl = function () { 16 | return ret.instanceUrl + '/services/data/v30.0/analytics/dashboards/' + ret.dashboardId; 17 | }; 18 | ret.poll = function () { 19 | $http({ 20 | url: ret.getDashboardUrl() + '/status', 21 | method: 'GET', 22 | headers: { 23 | 'Authorization': 'Bearer ' + ret.sessionId 24 | } 25 | }).success(function (response) { 26 | var done = true; 27 | angular.forEach(response.componentStatus, function (e, i) { 28 | done = done && (e.refreshStatus === "IDLE"); 29 | }); 30 | (done === true) ? ret.fetchData(true) : $timeout(ret.poll, 250); 31 | }); 32 | }; 33 | ret.refresh = function () { 34 | $http({ 35 | url: ret.getDashboardUrl(), 36 | method: 'PUT', 37 | headers: { 38 | 'Authorization': 'Bearer ' + ret.sessionId 39 | } 40 | }).success(function (response) { 41 | $timeout(ret.poll, 200); 42 | }); 43 | }; 44 | ret.fetchData = function (ignoreCache) { 45 | $http({ 46 | url: ret.getDashboardUrl(), 47 | method: 'GET', 48 | headers: { 49 | 'Authorization': 'Bearer ' + ret.sessionId 50 | }, 51 | cache: (true != ignoreCache) 52 | }).success(function (response) { 53 | ret.afterDataFetch(response); 54 | }); 55 | }; 56 | return ret; 57 | } 58 | ]) 59 | }); 60 | -------------------------------------------------------------------------------- /reportToChatter.apex: -------------------------------------------------------------------------------- 1 | // Run Report using Analytics API 2 | HttpRequest req = new HttpRequest(); 3 | req.setEndpoint('https://cs12.salesforce.com/services/data/v29.0/analytics/reports/00OV0000000OTyR'); 4 | req.setHeader('Authorization', 'Bearer '+ UserInfo.getSessionId()); 5 | req.setMethod('GET'); 6 | Http http = new Http(); 7 | HTTPResponse res = http.send(req); 8 | JSObject reportResult = JSObject.wrap(JSON.deserializeUntyped(res.getBody())); 9 | 10 | // Make a feed post for each user in the report 11 | List posts = new List(); 12 | for(JSObject user: reportResult.getAsObject('groupingsDown').getAsList('groupings')) { 13 | String factMapKey = user.get('key')+'!T'; // Refer to "Decode the FactMap" 14 | JSObject factMapEntry = reportResult.getAsObject('factMap').getAsObject(factMapKey); 15 | String numCases = (String) factMapEntry.getAsList('aggregates').get(0).get('label'); 16 | 17 | // Construct the Feed object 18 | FeedItem post = new FeedItem(); 19 | post.parentId = (Id)user.get('value'); 20 | post.body = 'Alert!! You have '+ numCases + ' open cases.'; 21 | posts.add(post); 22 | } 23 | insert posts; 24 | -------------------------------------------------------------------------------- /scorecard.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 91 | 92 | 93 | Table ViewCard View 94 | 95 | 96 | {{cell}} 97 | {{header[$index]}}..............{{cell}} 98 | 99 | 100 | 101 | {{col}} 102 | 103 | {{cell}} 104 | 105 | 106 | 107 | 108 | 123 | 124 | --------------------------------------------------------------------------------
96 | {{cell}} 97 | {{header[$index]}}..............{{cell}} 98 |