Skip to content

Instantly share code, notes, and snippets.

@Fountaincoder
Last active December 7, 2020 11:10
Show Gist options
  • Save Fountaincoder/bc52f37ba027aa1de5cc2f75b5fdc904 to your computer and use it in GitHub Desktop.
Save Fountaincoder/bc52f37ba027aa1de5cc2f75b5fdc904 to your computer and use it in GitHub Desktop.
Stuarts system
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