Last active
December 7, 2020 11:10
-
-
Save Fountaincoder/bc52f37ba027aa1de5cc2f75b5fdc904 to your computer and use it in GitHub Desktop.
Stuarts system
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Basic API call (TypeScript) | |
description: Performs a basic Excel API call using TypeScript. | |
host: EXCEL | |
api_set: {} | |
script: | |
content: | | |
$("#run").click(() => tryCatch(run)); | |
console.log("Ready"); | |
async function run() { | |
const days = Number($("#days").val()); | |
const no_sims = Number($("#sims").val()); | |
const gencheck = $("#gencheck").is(":checked"); | |
const scale = Number($("#genscale").val()) / 100.0; | |
const weekend_act = Number($("#weekend").val()); | |
const weekend = $("#wkdcheck").is(":checked"); | |
let schedule = []; | |
if ($("#ccg").is(":checked")) { | |
const ccgno = Number($("#ccgno").val()); | |
const ccgmin = Number($("#ccgmin").val()); | |
const ccgmax = Number($("#ccgmax").val()); | |
const ccglen = Number($("#ccglen").val()); | |
const ccgres = Number($("#ccgres").val()); | |
const e2 = Array.from(Array(ccgno).keys()).map((_) => ({ | |
func: spike, | |
peak: () => sim_rand(ccgmin, ccgmax), | |
length: () => sim_rand(1, ccglen), | |
net: () => sim_rand(1, ccgres), | |
when: () => sim_rand(1, days) | |
})); | |
schedule = schedule.concat(e2); | |
} | |
if ($("#ccg2").is(":checked")) { | |
const ccg2no = Number($("#ccg2no").val()); | |
const ccg2txt = Number($("#ccg2txt").val()); | |
const ccg2conv = Number($("#ccg2conv").val()); | |
const ccg2len = Number($("#ccg2len").val()); | |
const ccg2res = Number($("#ccg2res").val()); | |
const e2 = Array.from(Array(ccg2no).keys()).map((_) => ({ | |
func: spike, | |
peak: () => (ccg2conv / 100) * ccg2txt, | |
length: () => sim_rand(2, ccg2len), | |
net: () => ccg2res, | |
when: () => sim_rand(1, days) | |
})); | |
schedule = schedule.concat(e2); | |
} | |
if ($("#covid").is(":checked")) { | |
const cwhen = Number($("#cwhen").val()); | |
const cmin = Number($("#cmin").val()); | |
const cmax = Number($("#cmax").val()); | |
const cres = Number($("#cres").val()); | |
const e2 = [ | |
{ | |
func: spike, | |
peak: () => sim_rand(cmin, cmax), | |
length: () => 30, | |
net: () => cres, | |
when: () => cwhen | |
} | |
]; | |
schedule = schedule.concat(e2); | |
} | |
if ($("#covid2").is(":checked")) { | |
const cwhen = Number($("#c2when").val()); | |
const cmin = Number($("#c2min").val()); | |
const cmax = Number($("#c2max").val()); | |
const cres = Number($("#c2res").val()); | |
const e2 = [ | |
{ | |
func: spike, | |
peak: () => sim_rand(cmin, cmax), | |
length: () => 30, | |
net: () => cres, | |
when: () => cwhen | |
} | |
]; | |
schedule = schedule.concat(e2); | |
} | |
if ($("#flu").is(":checked")) { | |
const when = Number($("#wwhen").val()); | |
const len = Number($("#wlen").val()); | |
const extra = Number($("#wextra").val()); | |
const e2 = [ | |
{ | |
func: rise, | |
days: () => len, | |
net: () => extra, | |
when: () => when | |
} | |
]; | |
schedule = schedule.concat(e2); | |
} | |
if ($("#bckgnd").is(":checked")) { | |
const bfrom = Number($("#bfrm").val()); | |
const bperc = Number($("#bperc").val()); | |
const e2 = [ | |
{ | |
func: trend, | |
c: () => bfrom, | |
m: () => (bperc * (bfrom + 1)) / (100 * days), | |
when: () => 0 | |
} | |
]; | |
schedule = schedule.concat(e2); | |
} | |
if ($("#partners").is(":checked")) { | |
const pnums = Number($("#pnums").val()); | |
const pscale = Number($("#pscale").val()); | |
const pperc = Number($("#pperc").val()); | |
const e2 = Array.from(Array(pnums).keys()).map(function(_) { | |
let pfrom = pscale * 150; | |
return { | |
func: trend, | |
c: () => pfrom, | |
m: () => (pperc * (pfrom + 1)) / (100 * days), | |
when: () => sim_rand(1, days) | |
}; | |
}); | |
schedule = schedule.concat(e2); | |
} | |
console.log(schedule.length + " events scheduled"); | |
Excel.run(function(context) { | |
console.log("Starting Sims"); | |
let sims = []; | |
for (let i = 0; i < no_sims; i++) { | |
const s = run_sim(schedule, weekend, weekend_act, scale, gencheck, days); | |
const sync_s = s.arraySync(); | |
sims.push(sync_s); | |
} | |
console.log("Finished Sims"); | |
console.log("Calculate Statistics"); | |
const s2 = tf.tensor2d(sims); | |
const s3 = tf.moments(s2, 0); | |
const mean_s = s3["mean"].arraySync().map((x) => [x]); | |
const cummean_s = s3["mean"] | |
.cumsum() | |
.arraySync() | |
.map((x) => [x]); | |
const stddev_s = s3["variance"] | |
.sqrt() | |
.arraySync() | |
.map((x) => [x]); | |
console.log("Updating working sheet"); | |
var sheet = context.workbook.worksheets.getItem("Simulations"); | |
var simrange = sheet.getRange("A1:A" + String(days + 1)); | |
simrange.values = [["Submissions: Sample Sim"]].concat(sims[0].map((x) => [x])); | |
simrange.format.autofitColumns(); | |
var mrange = sheet.getRange("B1:B" + String(days + 1)); | |
mrange.values = [["Submissions: Mean of Sims"]].concat(mean_s); | |
mrange.format.autofitColumns(); | |
var stdrange = sheet.getRange("C1:C" + String(days + 1)); | |
stdrange.values = [["Stddev"]].concat(stddev_s); | |
stdrange.format.autofitColumns(); | |
var cumurange = sheet.getRange("D1:D" + String(days + 1)); | |
cumurange.values = [["Cumulative Submissions"]].concat(cummean_s); | |
cumurange.format.autofitColumns(); | |
var chart1 = sheet.charts.add("Line", simrange, "auto"); | |
chart1.title.text = "Single Simulation: Submissions"; | |
var chart2 = sheet.charts.add("Line", mrange, "auto"); | |
chart2.title.text = "Mean of Simulations: Submissions"; | |
var chart3 = sheet.charts.add("Line", cumurange, "auto"); | |
chart3.title.text = "Mean of Simulations: Cumulative Submissions"; | |
chart1.setPosition("E1", "J16"); | |
chart2.setPosition("E18", "J32"); | |
chart3.setPosition("E34", "J48"); | |
console.log("finished"); | |
//chart.legend.position = "right" | |
//chart.legend.format.fill.setSolidColor("white"); | |
//chart.dataLabels.format.font.size = 5; | |
//chart.dataLabels.format.font.color = "black"; | |
return context.sync(); | |
}); | |
} | |
function rise(ev, no_days = 365) { | |
const days = ev["days"](); | |
const net = ev["net"](); | |
const when = ev["when"](); | |
const y = tf.fill([days], net); | |
return y.pad([[when, no_days - (when + days)]]); | |
} | |
function weekend_adjustment(no_days = 365, weekend) { | |
//}, minScale = 0.6, maxScale = 0.7) { | |
const minScale = weekend / 100 - 0.1; | |
const maxScale = weekend / 100 + 0.1; | |
const weeks = Math.floor(no_days / 7); | |
const weekdays = tf.ones([weeks, 5]); | |
const weekends = tf.randomUniform([weeks, 2], minScale, maxScale); | |
const all_days = tf.concat([weekdays, weekends], (axis = 1)).flatten(); | |
const no_act_days = all_days.shape[0]; | |
return all_days.pad([[0, no_days - no_act_days]], 1); | |
} | |
function spike(ev, no_days = 365) { | |
const peak = ev["peak"](); | |
const len = ev["length"](); | |
const net = ev["net"](); | |
const when = ev["when"](); | |
const l0 = (Math.log(peak) + 1) / len; | |
const x = tf.range(0, len); | |
const spike_out = tf.scalar(peak - net).mul(tf.exp(tf.scalar(-l0).mul(x))); | |
const ele_spike_out = spike_out.add(tf.scalar(net)); | |
const start_padded_spike = ele_spike_out.pad([[when, 0]]); | |
return start_padded_spike.pad([[0, no_days - (when + len)]], net); | |
} | |
function trend(ev, no_days = 365) { | |
const m = ev["m"](); | |
const c = ev["c"](); | |
const when = ev["when"](); | |
const x = tf.range(0, no_days); | |
const trend_out = tf | |
.scalar(m) | |
.mul(x) | |
.add(tf.scalar(c)); | |
return position(trend_out, when, no_days); | |
} | |
function position(data, when, no_days) { | |
return data.pad([[when, no_days - (data.shape[0] + when)]]); | |
} | |
function run_sim(evts, wkdcheck, weekends, scale, gencheck, no_days = 365) { | |
let yrs = tf.zeros([no_days]); | |
for (var e in evts) { | |
const event_days = evts[e]["func"](evts[e], no_days); | |
yrs = yrs.add(event_days); | |
} | |
if (wkdcheck) { | |
yrs = yrs.mul(weekend_adjustment(no_days, weekends)); | |
} | |
if (gencheck) { | |
yrs = tf.scalar(scale).mul(yrs); | |
} | |
return yrs; | |
} | |
function sim_rand(mn, mx) { | |
const shell_out = (x) => x.arraySync()[0]; | |
return Math.floor(shell_out(tf.randomUniform([1], mn, mx))); | |
} | |
/** Default helper for invoking an action and handling errors. */ | |
async function tryCatch(callback) { | |
try { | |
await callback(); | |
} catch (error) { | |
// Note: In a production add-in, you'd want to notify the user through your add-in's UI. | |
console.error(error); | |
} | |
} | |
language: typescript | |
template: | |
content: "<section class=\"samples ms-font-m\">\n\t<h1>NHS-Login Forecasting</h1>\n\t<p> Please fill in the details of the simulation below (it scrolls, and has 7 Scenario types) and press the\n\t\t<b>Generate Forecast</b> button.</p>\n\t<label> No. Simulations to aggregate: </label> <input id=\"sims\" type=\"text\" size=\"3\" value=\"10\"><br><br>\n\t<button id=\"run\" class=\"ms-Button\"> <span class=\"ms-Button-label\">Generate Forecast</span></button>\n\n\t<h2> Settings </h2>\n\n\t<h3> General </h3>\n\t<label> Length of forecast: </label>\n\t<input id=\"days\" type=\"text\" size=\"3\" value=\"365\"><label><em> (days)</em></label><br>\n\t<input type=\"checkbox\" id=\"wkdcheck\"> <label> Weekend as % of weekday: </label>\n\t<input id=\"weekend\" type=\"text\" size=\"3\" value=\"60\"><em> (1-100)+-10%</em><br>\n\t<input type=\"checkbox\" id=\"gencheck\"> <label> general scaling: </label>\n\t<input id=\"genscale\" type=\"text\" size=\"3\" value=\"100\"><em> (1-500)</em><br>\n\t<p> <em>General scaling </em> is a multiplying factor for the entire forecast. It's a way of upgrading/downgrading\n\t\tthe values to reflect things such as a more postive view of NHS-app numbers. </p>\n\n\t<h3> <label>1. Spike Events </label> <input type=\"checkbox\" id=\"ccg\"> </h3>\n\t\t<p><em> Spikes, randomly throughout period</em></p>\n\t\t<label> Number: </label> <input id=\"ccgno\" type=\"text\" size=\"3\" value=\"10\"><em> (events)</em><br>\n\t\t<label> Min Extra Submissions: </label>\n\t\t<input id=\"ccgmin\" type=\"text\" size=\"5\" value=\"4000\"><em> (persons)</em><br>\n\t\t<label> Max Extra Submissions: </label>\n\t\t<input id=\"ccgmax\" type=\"text\" size=\"5\" value=\"6000\"><em> (persons)</em><br>\n\t\t<label> Length: </label> <input id=\"ccglen\" type=\"text\" size=\"3\" value=\"2\"><em> (days)</em><br>\n\t\t<label> Residual after event up to: </label> <input id=\"ccgres\" type=\"text\" size=\"5\" value=\"500\">\n\t\t<em> (persons)</em>\n\n\t\t<h3> <label>2. CCG Events </label> <input type=\"checkbox\" id=\"ccg2\"> </h3>\n\t\t\t<p><em> Notts-style CCG event spikes, randomly throughout period</em></p>\n\t\t\t<label> Number: </label> <input id=\"ccg2no\" type=\"text\" size=\"3\" value=\"10\"><em> (events)</em><br>\n\t\t\t<label> Txts: </label>\n\t\t\t<input id=\"ccg2txt\" type=\"text\" size=\"6\" value=\"100000\"><em> (persons)</em><br>\n\t\t\t<label> % Conversion: </label>\n\t\t\t<input id=\"ccg2conv\" type=\"text\" size=\"3\" value=\"6\"><em> (1-100)</em><br>\n\t\t\t<label> Length: </label> <input id=\"ccg2len\" type=\"text\" size=\"3\" value=\"2\"><em> (days)</em><br>\n\t\t\t<label> Residual after event up to: </label> <input id=\"ccg2res\" type=\"text\" size=\"5\" value=\"500\">\n\t\t\t<em> (persons)</em>\n\n\t\t\t<h3> <label>3. Partners </label> <input type=\"checkbox\" id=\"partners\"> </h3>\n\t\t\t\t<p><em>How partners will grow over period. Assumption is linear growth. Co-op is \n\t\t\t\t\tone unit and equates to 150 submissions per day. We assume that these partners will have\n\t\t\t\t\ta significant impact as per co-op</em> </p>\n\t\t\t\t<label>Number of New significant partners in year: </label>\n\t\t\t\t<input id=\"pnums\" type=\"text\" size=\"3\" value=\"20\"><em> (companies)</em><br>\n\t\t\t\t<label>Max Company size when compared to the Co-op: </label>\n\t\t\t\t<input id=\"pscale\" type=\"text\" size=\"3\" value=\"1.0\"><em> </em><br>\n\t\t\t\t<label>% growth over year: </label> <input id=\"pperc\" type=\"text\" size=\"3\" value=\"10\"><em> (0-100)</em>\n\n\n\t\t\t\t<h3> <label>4. Background Growth </label> <input type=\"checkbox\" id=\"bckgnd\"> </h3>\n\t\t\t\t\t<p><em>General background growth over period. Assumption is linear growth. </em></p>\n\t\t\t\t\t<label>Starting value: </label>\n\t\t\t\t\t<input id=\"bfrm\" type=\"text\" size=\"5\" value=\"10000\"><em> (persons)</em><br>\n\t\t\t\t\t<label>% growth over year: </label>\n\t\t\t\t\t<input id=\"bperc\" type=\"text\" size=\"3\" value=\"10\"><em> (0-100)</em><br>\n\n\n\t\t\t\t\t<h3> <label>5. Covid Lockdown</label> <input type=\"checkbox\" id =\"covid\"> </h3>\n\t\t\t\t\t\t<p><em>A 1st major spikes at a period in the future</em></p>\n\t\t\t\t\t\t<label> When:</label> <input id=\"cwhen\" type=\"text\" size=\"3\" value=\"0\"><em> (day num.)</em><br>\n\t\t\t\t\t\t<label> Min Extra Submissions: </label>\n\t\t\t\t\t\t<input id=\"cmin\" type=\"text\" size=\"5\" value=\"18000\"><em> (persons)</em><br>\n\t\t\t\t\t\t<label> Max Extra Submissions:</label>\n\t\t\t\t\t\t<input id=\"cmax\" type=\"text\" size=\"5\" value=\"20000\"><em> (persons)</em><br>\n\t\t\t\t\t\t<label> Residual after event:</label>\n\t\t\t\t\t\t<input id=\"cres\" type=\"text\" size=\"5\" value=\"7500\"><em> (persons)</em><br>\n\n\t\t\t\t\t\t<h3> <label>6. Covid 2nd Lockdown</label> <input type=\"checkbox\" id =\"covid2\"> </h3>\n\t\t\t\t\t\t\t<p><em>A 2nd major spikes at a period in the future</em></p>\n\t\t\t\t\t\t\t<label> When:</label>\n\t\t\t\t\t\t\t<input id=\"c2when\" type=\"text\" size=\"3\" value=\"100\"><em> (day num.)</em><br>\n\t\t\t\t\t\t\t<label> Min Extra Submissions: </label>\n\t\t\t\t\t\t\t<input id=\"c2min\" type=\"text\" size=\"5\" value=\"18000\"><em> (persons)</em><br>\n\t\t\t\t\t\t\t<label> Max Extra Submissions:</label>\n\t\t\t\t\t\t\t<input id=\"c2max\" type=\"text\" size=\"5\" value=\"20000\"><em> (persons)</em><br>\n\t\t\t\t\t\t\t<label> Residual after event:</label>\n\t\t\t\t\t\t\t<input id=\"c2res\" type=\"text\" size=\"5\" value=\"7500\"><em> (persons)</em><br>\n\n\t\t\t\t\t\t\t<h3> <label>7. Winter Rise</label> <input type=\"checkbox\" id=\"flu\"> </h3>\n\t\t\t\t\t\t\t\t<p> <em>A general rise in usage typically associated with a november normal flu epidemic</em>\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t<label> When: </label>\n\t\t\t\t\t\t\t\t<input id=\"wwhen\" type=\"text\" size=\"3\" value=\"100\"><label><em> (day num.)</em></label><br>\n\t\t\t\t\t\t\t\t<label> For how long: </label>\n\t\t\t\t\t\t\t\t<input id=\"wlen\" type=\"text\" size=\"3\" value=\"50\"><em> (days)</em><br>\n\t\t\t\t\t\t\t\t<label> Extra Submissions: </label>\n\t\t\t\t\t\t\t\t<input id=\"wextra\" type=\"text\" size=\"5\" value=\"1000\"><em> (persons)</em><br><br>\n\n\n</section>" | |
language: html | |
style: | |
content: |- | |
section.samples { | |
margin-top: 20px; | |
} | |
section.samples .ms-Button, section.setup .ms-Button { | |
display: block; | |
margin-bottom: 5px; | |
margin-left: 20px; | |
min-width: 80px; | |
} | |
language: css | |
libraries: |+ | |
https://appsforoffice.microsoft.com/lib/1/hosted/office.js | |
@types/office-js | |
https://cdn.jsdelivr.net/npm/@tensorflow/tfjs/dist/tf.min.js | |
https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-vis/dist/tfjs-vis.umd.min.js | |
office-ui-fabric-js@1.4.0/dist/css/fabric.min.css | |
office-ui-fabric-js@1.4.0/dist/css/fabric.components.min.css | |
core-js@2.4.1/client/core.min.js | |
@types/core-js | |
jquery@3.1.1 | |
@types/jquery@3.3.1 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment