UNPKG

planout

Version:

PlanOut is a framework and programming language for online field experimentation

303 lines (249 loc) 7.75 kB
import { range, isObject, forEach, getParameterByName, hasKey, extend } from "./lib/utils.js"; import { getExperimentInputs } from './experimentSetup'; export default function provideNamespace(Random, Assignment, Experiment) { class DefaultExperiment extends Experiment { configureLogger() { return; } setup() { this.name = 'test_name'; } log(data) { return; } getParamNames() { return this.getDefaultParamNames(); } previouslyLogged() { return true; } assign(params, args) { return; } } class Namespace { addExperiment(name, obj, segments) { throw "IMPLEMENT addExperiment"; } removeExperiment(name) { throw "IMPLEMENT removeExperiment"; } setAutoExposureLogging(value) { throw "IMPLEMENT setAutoExposureLogging"; } inExperiment() { throw "IMPLEMENT inExperiment"; } get(name, defaultVal) { throw "IMPLEMENT get"; } logExposure(extras) { throw "IMPLEMENT logExposure"; } logEvent(eventType, extras) { throw "IMPLEMENT logEvent"; } requireExperiment() { if (!this._experiment) { this._assignExperiment(); } } requireDefaultExperiment() { if (!this._defaultExperiment) { this._assignDefaultExperiment(); } } } class SimpleNamespace extends Namespace { constructor(args) { super(args); this.inputs = args || {}; this.numSegments = 1; this.segmentAllocations = {}; this.currentExperiments = {}; this._experiment = null; this._defaultExperiment = null; this.defaultExperimentClass = DefaultExperiment; this._inExperiment = false; this.setupDefaults(); this.setup(); if (!this.name) { throw "setup() must set a namespace name via this.setName()"; } this.availableSegments = range(this.numSegments); this.setupExperiments(); } setupDefaults() { return; } setup() { throw "IMPLEMENT setup"; } setupExperiments() { throw "IMPLEMENT setupExperiments"; } getPrimaryUnit() { return this._primaryUnit; } allowedOverride() { return false; } getOverrides() { return {}; } setPrimaryUnit(value) { this._primaryUnit = value; } addExperiment(name, expObject, segments) { var numberAvailable = this.availableSegments.length; if (numberAvailable < segments) { return false; } else if (this.currentExperiments[name] !== undefined) { return false; } var a = new Assignment(this.name); a.set('sampled_segments', new Random.Sample({'choices': this.availableSegments, 'draws': segments, 'unit': name})); var sample = a.get('sampled_segments'); for(var i = 0; i < sample.length; i++) { this.segmentAllocations[sample[i]] = name; var currentIndex = this.availableSegments.indexOf(sample[i]); this.availableSegments[currentIndex] = this.availableSegments[numberAvailable - 1]; this.availableSegments.splice(numberAvailable - 1, 1); numberAvailable -= 1; } this.currentExperiments[name] = expObject } removeExperiment(name) { if (this.currentExperiments[name] === undefined) { return false; } forEach(Object.keys(this.segmentAllocations), (cur) => { if(this.segmentAllocations[cur] === name) { delete this.segmentAllocations[cur]; this.availableSegments.push(cur); } }); delete this.currentExperiments[name]; return true; } getSegment() { var a = new Assignment(this.name); var segment = new Random.RandomInteger({'min': 0, 'max': this.numSegments-1, 'unit': this.inputs[this.getPrimaryUnit()]}); a.set('segment', segment); return a.get('segment'); } _assignExperiment() { this.inputs = extend(this.inputs, getExperimentInputs(this.getName())); var segment = this.getSegment(); if (this.segmentAllocations[segment] !== undefined) { var experimentName = this.segmentAllocations[segment]; this._assignExperimentObject(experimentName); } } _assignExperimentObject(experimentName) { var experiment = new this.currentExperiments[experimentName](this.inputs); experiment.setName(`${this.getName()}-${experimentName}`); experiment.setSalt(`${this.getName()}-${experimentName}`); this._experiment = experiment; this._inExperiment = experiment.inExperiment(); if (!this._inExperiment) { this._assignDefaultExperiment(); } } _assignDefaultExperiment() { this._defaultExperiment = new this.defaultExperimentClass(this.inputs); } defaultGet(name, default_val) { super.requireDefaultExperiment(); return this._defaultExperiment.get(name, default_val); } getName() { return this.name; } setName(name) { this.name = name; } previouslyLogged() { if (this._experiment) { return this._experiment.previouslyLogged(); } return null; } inExperiment() { super.requireExperiment(); return this._inExperiment; } setAutoExposureLogging(value) { this._autoExposureLoggingSet = value; if (this._defaultExperiment) { this._defaultExperiment.setAutoExposureLogging(value); } if (this._experiment) { this._experiment.setAutoExposureLogging(value); } } setGlobalOverride(name) { var globalOverrides = this.getOverrides(); if(globalOverrides && hasKey(globalOverrides, name)) { var overrides = globalOverrides[name]; if (overrides && hasKey(this.currentExperiments, overrides.experimentName)) { this._assignExperimentObject(overrides.experimentName); this._experiment.addOverride(name, overrides.value); } } } setLocalOverride(name) { var experimentName = getParameterByName('experimentOverride'); if (experimentName && hasKey(this.currentExperiments, experimentName)) { this._assignExperimentObject(experimentName); if (getParameterByName(name)) { this._experiment.addOverride(name, getParameterByName(name)); } } } getParams(experimentName) { super.requireExperiment(); if (this._experiment && this.getOriginalExperimentName() === experimentName) { return this._experiment.getParams(); } else { return null; } } getOriginalExperimentName() { if (this._experiment) { return this._experiment.getName().split('-')[1]; } return null; } get(name, defaultVal) { super.requireExperiment(); if (this.allowedOverride()) { this.setGlobalOverride(name); } this.setLocalOverride(name); if (!this._experiment) { return this.defaultGet(name, defaultVal); } else { if (this._autoExposureLoggingSet !== undefined) { this._experiment.setAutoExposureLogging(this._autoExposureLoggingSet); } return this._experiment.get(name, this.defaultGet(name, defaultVal)); } } logExposure(extras) { super.requireExperiment(); if (!this._experiment) { return; } this._experiment.logExposure(extras); } logEvent(eventType, extras) { super.requireExperiment(); if (!this._experiment) { return; } this._experiment.logEvent(eventType, extras); } } return { Namespace: Namespace, SimpleNamespace: SimpleNamespace }; }