{ "kind": "open-flow-workflow", "version": 1, "name": "Email a Google Analytics Comparison Report", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_bc6ced8ef2eb9b8b": { "name": "Run the analytics report", "kind": "start", "inputDefs": [] }, "n_9f367c2aee9eb912": { "inputs": { "propertyId": { "kind": "value", "value": "YOUR_PROPERTY_ID" }, "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_9f367c2aee9eb912", "output": "state" } ] } }, "name": "Collect six analytics reports", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "propertyId" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previous" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" } ], "name": "Collect six analytics reports", "moduleId": "n_9864d5cdf1cbcabc", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [ { "action": "google_analytics.run_report" } ] } ] } }, "n_8e5e49c5a3845dec": { "inputs": {}, "name": "More report pages?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "read", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_9f367c2aee9eb912", "output": "more" } }, "operator": "isTrue" } ] } ] } ] }, "n_58df2d79957c11fe": { "inputs": { "state": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_9f367c2aee9eb912", "output": "state" } ] }, "recipient": { "kind": "value", "value": "YOUR_REPORT_EMAIL" } }, "name": "Format the comparison", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "recipient" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "to" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "subject" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "body" } ], "name": "Format the comparison", "moduleId": "n_ea342f805319f6e7", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_379b7b5316495d8f": { "inputs": { "to": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_58df2d79957c11fe", "output": "to" } ] }, "subject": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_58df2d79957c11fe", "output": "subject" } ] }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_58df2d79957c11fe", "output": "body" } ] }, "isHtml": { "kind": "value", "value": true } }, "name": "Email the report", "kind": "task", "task": { "inputs": [ { "description": "Primary recipient email address.", "jsonSchema": { "type": "string", "description": "Primary recipient email address." }, "nullable": true, "handle": "to", "value": null }, { "description": "Additional To recipients.", "jsonSchema": { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "Additional To recipients." }, "nullable": true, "handle": "extraRecipients", "value": null }, { "description": "Cc recipients.", "jsonSchema": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "A union variant." } ], "description": "Cc recipients." }, "nullable": true, "handle": "cc", "value": null }, { "description": "Bcc recipients.", "jsonSchema": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "A union variant." } ], "description": "Bcc recipients." }, "nullable": true, "handle": "bcc", "value": null }, { "description": "Email subject line.", "jsonSchema": { "type": "string", "description": "Email subject line." }, "nullable": true, "handle": "subject", "value": null }, { "description": "Email body content.", "jsonSchema": { "type": "string", "description": "Email body content." }, "nullable": true, "handle": "body", "value": null }, { "description": "Set to true when the body content is already HTML.", "jsonSchema": { "type": "boolean", "description": "Set to true when the body content is already HTML." }, "nullable": true, "handle": "isHtml", "value": null }, { "description": "Attachments and CID inline images. Up to 25,000,000 decoded bytes in total and 35,000,000 MIME bytes.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "filename": { "type": "string", "description": "Attachment filename; omit for an unnamed attachment." }, "mimeType": { "type": "string", "description": "MIME type without parameters; defaults to application/octet-stream." }, "contentBase64": { "type": "string", "description": "Standard padded Base64 content. Empty string represents a zero-byte file. Supply this or fileUrl." }, "fileUrl": { "type": "string", "format": "uri", "description": "Public URL to download the attachment. Supply this or contentBase64." }, "contentId": { "type": "string", "minLength": 1, "description": "Bare Content-ID without cid: or angle brackets; reference it as cid: in HTML." }, "disposition": { "type": "string", "enum": [ "inline", "attachment" ], "description": "Defaults to inline when contentId is set, otherwise attachment. Inline requires contentId." } }, "additionalProperties": false, "description": "An attachment or CID inline resource.", "oneOf": [ { "required": [ "contentBase64" ] }, { "required": [ "fileUrl" ] } ] }, "maxItems": 100, "description": "Attachments and CID inline images. Up to 25,000,000 decoded bytes in total and 35,000,000 MIME bytes." }, "nullable": true, "handle": "attachments", "value": null }, { "description": "Verified Gmail send-as alias to use in the From header.", "jsonSchema": { "type": "string", "description": "Verified Gmail send-as alias to use in the From header." }, "nullable": true, "handle": "fromEmail", "value": null } ], "outputs": [ { "description": "Gmail message ID.", "jsonSchema": { "type": "string", "description": "Gmail message ID." }, "nullable": false, "handle": "messageId" }, { "description": "Gmail thread ID.", "jsonSchema": { "type": "string", "description": "Gmail thread ID." }, "nullable": true, "handle": "threadId" } ], "name": "Email the report", "executor": { "kind": "connector", "action": "gmail.send_email" } } } }, "edges": [ { "source": "n_bc6ced8ef2eb9b8b", "target": "n_9f367c2aee9eb912" }, { "source": "n_9f367c2aee9eb912", "target": "n_8e5e49c5a3845dec" }, { "source": "n_8e5e49c5a3845dec", "target": "n_9f367c2aee9eb912", "sourceHandle": "read" }, { "source": "n_8e5e49c5a3845dec", "target": "n_58df2d79957c11fe", "sourceHandle": "otherwise" }, { "source": "n_58df2d79957c11fe", "target": "n_379b7b5316495d8f" } ] } }, "modules": { "n_9864d5cdf1cbcabc": { "name": "Collect six analytics reports", "imports": [], "source": "export default async function collect({propertyId,previous},context){\n const groups=[{dimension:'unifiedScreenName',metrics:['screenPageViews','activeUsers','screenPageViewsPerUser','eventCount']},{dimension:'landingPagePlusQueryString',metrics:['activeUsers','engagedSessions','engagementRate','eventCount','organicGoogleSearchAveragePosition','organicGoogleSearchClickThroughRate','organicGoogleSearchClicks','organicGoogleSearchImpressions']},{dimension:'country',metrics:['activeUsers','newUsers','engagementRate','engagedSessions','eventCount','totalUsers','sessions']}];\n if(typeof propertyId!=='string'||!/^\\d+$/.test(propertyId))throw Error('Configure a Google Analytics property ID');\n const day=new Date().toISOString().slice(0,10),d=(base,n)=>new Date(Date.parse(base+'T00:00:00Z')+n*86400000).toISOString().slice(0,10),s=previous??{index:0,offset:0,day,tables:[[],[],[],[],[],[]]};\n if(!Number.isSafeInteger(s.index)||s.index<0||s.index>=6||!Number.isSafeInteger(s.offset)||s.offset<0||!Array.isArray(s.tables)||s.tables.length!==6||typeof s.day!=='string')throw Error('Invalid report state');\n const g=groups[Math.floor(s.index/2)],recent=s.index%2===0,range={startDate:d(s.day,recent?-7:-14),endDate:d(s.day,recent?-1:-8)},result=await context.actions.call('google_analytics.run_report',{propertyId,dateRanges:[range],dimensions:[g.dimension],metrics:g.metrics,keepEmptyRows:true,limit:10000,offset:s.offset});\n const r=result?.report;if(!r||!Array.isArray(r.rows))throw Error('Invalid analytics response');const count=r.rowCount==null&&r.rows.length===0&&s.offset===0?0:r.rowCount;if(!Number.isSafeInteger(count)||count<0||s.offset+r.rows.length>count||(!r.rows.length&&s.offset{if(!Array.isArray(x.dimensionValues)||x.dimensionValues.length!==1||typeof x.dimensionValues[0]!=='string'||!Array.isArray(x.metricValues)||x.metricValues.length!==g.metrics.length||x.metricValues.some(v=>typeof v!=='string'))throw Error('Invalid analytics row');return [x.dimensionValues[0],...x.metricValues]});\n const tables=s.tables.map(x=>Array.isArray(x)?[...x]:(()=>{throw Error('Invalid report table')})());tables[s.index].push(...rows);const offset=s.offset+rows.length,finished=offset===count,index=finished?s.index+1:s.index;\n return {state:{index,offset:finished?0:offset,day:s.day,tables},more:index<6};\n}\n" }, "n_ea342f805319f6e7": { "name": "Format the comparison", "imports": [], "source": "export default function format({state,recipient}){\n if(typeof recipient!=='string'||!recipient.includes('@')||recipient.includes('YOUR_'))throw Error('Configure a report recipient');if(!state||state.index!==6||!Array.isArray(state.tables)||state.tables.length!==6)throw Error('Incomplete report');\n const labels=[['Page','Page views','Active users','Views per user','Event count'],['Landing page','Active users','Engaged sessions','Engagement rate','Event count','Average position','Click-through rate','Clicks','Impressions'],['Country','Active users','New users','Engagement rate','Engaged sessions','Event count','Total users','Sessions']],sections=['Page engagement','Google Search','Country views'],escape=v=>String(v).replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));let html='

Google Analytics report

';\n for(let i=0;i<6;i++){if(!Array.isArray(state.tables[i]))throw Error('Invalid report table');html+='

'+sections[Math.floor(i/2)]+' - '+(i%2?'previous seven days':'latest seven days')+'

';const rows=state.tables[i];if(!rows.length){html+='

No data.

';continue}html+=''+labels[Math.floor(i/2)].map(x=>'').join('')+'';for(const r of rows){if(!Array.isArray(r)||r.length!==labels[Math.floor(i/2)].length)throw Error('Invalid formatted row');html+=''+r.map(x=>'').join('')+''}html+='
'+escape(x)+'
'+escape(x)+'
'}return{to:recipient,subject:'Google Analytics report before '+state.day+' UTC',body:html};\n}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }