From 3bda4c229b3073b4be5c52ef254af77c9a755a48 Mon Sep 17 00:00:00 2001
From: youralien <ryanlouie2021@u.northwestern.edu>
Date: Fri, 29 Nov 2019 20:30:21 -0600
Subject: [PATCH] Changed references of testing folder to Testing

---
 .../server/strategizer.js                     |   1 -
 .../api/{testing => Testing}/ce_examples.js   |   0
 imports/api/Testing/chi20experiences.js       | 358 ++++++++++++
 imports/api/{testing => Testing}/cn.js        |   0
 .../api/{testing => Testing}/conversion.js    |   0
 imports/api/{testing => Testing}/mincn.js     |   0
 imports/api/testing/createNewExperiences.js   | 385 -------------
 imports/api/testing/endToEndSimple.test.js    | 251 ---------
 imports/api/testing/testingconstants.js       | 515 ------------------
 imports/startup/server/fixtures.js            |   2 +-
 imports/startup/server/register-api.js        |   2 +-
 11 files changed, 360 insertions(+), 1154 deletions(-)
 rename imports/api/{testing => Testing}/ce_examples.js (100%)
 create mode 100644 imports/api/Testing/chi20experiences.js
 rename imports/api/{testing => Testing}/cn.js (100%)
 rename imports/api/{testing => Testing}/conversion.js (100%)
 rename imports/api/{testing => Testing}/mincn.js (100%)
 delete mode 100644 imports/api/testing/createNewExperiences.js
 delete mode 100644 imports/api/testing/endToEndSimple.test.js
 delete mode 100644 imports/api/testing/testingconstants.js

diff --git a/imports/api/OpportunisticCoordinator/server/strategizer.js b/imports/api/OpportunisticCoordinator/server/strategizer.js
index f5bb8c7b..87165e83 100644
--- a/imports/api/OpportunisticCoordinator/server/strategizer.js
+++ b/imports/api/OpportunisticCoordinator/server/strategizer.js
@@ -6,7 +6,6 @@ import {Assignments} from "../databaseHelpers";
 import {getNeedObject} from "./identifier";
 import {Experiences} from "../../OCEManager/OCEs/experiences";
 import {createIncidentFromExperience, startRunningIncident} from "../../OCEManager/OCEs/methods";
-import {CONSTANTS} from "../../testing/testingconstants";
 // import {Meteor} from "meteor/meteor";
 import {numberSubmissionsRemaining, usersAlreadyAssignedToNeed, usersAlreadySubmittedToNeed} from "../strategizer";
 
diff --git a/imports/api/testing/ce_examples.js b/imports/api/Testing/ce_examples.js
similarity index 100%
rename from imports/api/testing/ce_examples.js
rename to imports/api/Testing/ce_examples.js
diff --git a/imports/api/Testing/chi20experiences.js b/imports/api/Testing/chi20experiences.js
new file mode 100644
index 00000000..dd935421
--- /dev/null
+++ b/imports/api/Testing/chi20experiences.js
@@ -0,0 +1,358 @@
+import {getDetectorId} from "./testingconstants";
+import { CONSTANTS } from "./testingconstants";
+import {Submissions} from "../OCEManager/currentNeeds";
+import {notify} from "../OpportunisticCoordinator/server/noticationMethods";
+import {Meteor} from "meteor/meteor";
+import {addContribution} from "../OCEManager/OCEs/methods";
+
+let DETECTORS = CONSTANTS.DETECTORS;
+
+// current model
+// 0. to create a new experience, we write a fully defined CE API spec as as static definition
+// 1. on file load, the detectors and experiences JSON are created [by accessing the current static definition of DETECTORS or the existing Detectors database]
+// 2. on updateOCE, we simply access this static definition and make the database reflect this static definition
+
+// current obstacle with current model
+// 0. across different branches, it's hard to manage all the experiences we are creating
+// updateOCE finds on experience name, but the experiences for different groups are named the same
+
+// findings with using current model
+// 0. all the linking logic happens at static definition time, in one file
+// 0a. this means we need to make sure on file load, all the links are sorted out
+
+// what if we used factory method pattern? to create objects not on file load, but based on runtime calls
+// 0. this is less of a "fixtures" model, but rather building blocks that can be used at run time
+// 1. there could be factory methods for creation/updating of OCEs
+// 2. definitions for different studies or deployments are activated through runtime e.g., meteor method calls
+// 3. that would allow deployments to differ not based on code, but purely on the database?
+
+
+
+let CHI20_Olin_EXPERIENCES =  {
+  halfhalf_sunny_knowsOlin: {
+    _id: Random.id(),
+    name: 'Hand Silhouette',
+    group: 'Olin',
+    participateTemplate: 'halfhalfParticipate',
+    resultsTemplate: 'halfhalfResults',
+    contributionTypes: addStaticAffordanceToNeeds('knowsOlin', [{
+      // needName MUST have structure "My Need Name XYZ"
+      needName: 'Hand Silhouette 1',
+      situation: {
+        detector: getDetectorId(DETECTORS.sunny),
+        number: '1'
+      },
+      toPass: {
+        instruction: 'Is the <span style="color: #0351ff">weather clear and sunny</span> where you are? Take a photo, <span style="color: #0351ff">holding your hand towards the sky, covering the sun.</span>',
+        exampleImage: 'https://s3.us-east-2.amazonaws.com/ce-platform/oce-example-images/half-half-embodied-mimicry-hands-in-front.jpg'
+      },
+      numberNeeded: 2,
+      numberAllowedToParticipateAtSameTime: 1,
+      notificationDelay: 1,
+    }]),
+    description: 'Use the sun to make a silhouette of your hand',
+    notificationText: 'View this and other available experiences',
+    callbacks: [{
+      trigger: '(cb.numberOfSubmissions() % 2) === 0',
+      function: halfhalfRespawnAndNotify('A hand silhouette was completed','View the photo').toString()
+    }]
+  },
+  halfhalf_grocery_knowsOlin: {
+    _id: Random.id(),
+    name: 'Grocery Buddies',
+    group: 'Olin',
+    participateTemplate: 'halfhalfParticipate',
+    resultsTemplate: 'halfhalfResults',
+    contributionTypes: addStaticAffordanceToNeeds('knowsOlin', [{
+      // needName MUST have structure "My Need Name XYZ"
+      needName: 'Grocery Buddies 1',
+      notificationSubject: 'Inside a grocery store?',
+      notificationText: 'Share an experience with others who are also grocery shopping',
+      situation: {
+        detector: getDetectorId(DETECTORS.grocery),
+        number: '1'
+      },
+      toPass: {
+        instruction: 'Are you at the <span style="color: #0351ff">grocery store</span>? Take a photo, <span style="color: #0351ff">holding a fruit or vegetable</span> outstretched with your hands.',
+        exampleImage: 'https://s3.us-east-2.amazonaws.com/ce-platform/oce-example-images/half-half-embodied-mimicry-fruit-in-hand.jpg'
+      },
+      numberNeeded: 2,
+      numberAllowedToParticipateAtSameTime: 1,
+      notificationDelay: 90,
+    }]),
+    description: 'While shopping for groceries, create a half half photo.',
+    notificationText: 'View this and other available experiences',
+    callbacks: [{
+      trigger: '(cb.numberOfSubmissions() % 2) === 0',
+      function: halfhalfRespawnAndNotify('A Grocery Buddies photo completed','View the photo').toString()
+    }]
+  },
+  halfhalf_bar_knowsOlin: {
+    _id: Random.id(),
+    name: 'Cheers',
+    group: 'Olin',
+    participateTemplate: 'halfhalfParticipate',
+    resultsTemplate: 'halfhalfResults',
+    contributionTypes: addStaticAffordanceToNeeds('knowsOlin', [{
+      // needName MUST have structure "My Need Name XYZ"
+      needName: 'Cheers 1',
+      notificationSubject: 'Drinking at a bar?',
+      notificationText: 'Share an experience with others who are also drinking at a bar',
+      situation: {
+        detector: getDetectorId(DETECTORS.bar),
+        number: '1'
+      },
+      toPass: {
+        instruction: 'What are you <span style="color: #0351ff">drinking at the bar</span>? Take a photo, while <span style="color: #0351ff">raising your glass or bottle</span> in front of you.',
+        exampleImage: 'https://s3.us-east-2.amazonaws.com/ce-platform/oce-example-images/half-half-embodied-mimicry-cheers.jpg'
+      },
+      numberNeeded: 2,
+      numberAllowedToParticipateAtSameTime: 1,
+      notificationDelay: 90
+    }]),
+    description: 'While enjoying your drink, create a half half photo.',
+    notificationText: 'View this and other available experiences',
+    callbacks: [{
+      trigger: '(cb.numberOfSubmissions() % 2) === 0',
+      function: halfhalfRespawnAndNotify('A Cheers photo completed','View the photo').toString()
+    }]
+  },
+  halfhalf_religious_knowsOlin: {
+    _id: Random.id(),
+    name: 'Religious Architecture',
+    group: 'Olin',
+    participateTemplate: 'halfhalfParticipate',
+    resultsTemplate: 'halfhalfResults',
+    contributionTypes: addStaticAffordanceToNeeds('knowsOlin', [{
+      // needName MUST have structure "My Need Name XYZ"
+      needName: 'Religious Architecture 1',
+      notificationSubject: 'Visiting a place of worship?',
+      notificationText: 'Share an experience with others who are also visiting a place of worship',
+      situation: {
+        detector: getDetectorId(DETECTORS.castle),
+        number: '1'
+      },
+      toPass: {
+        instruction: 'Do you notice the <span style="color: #0351ff">details of the religious building</span> near you? Do so now, by outstretching your hand and pointing out of the elements that stick out to you most.',
+        exampleImage: 'https://s3.us-east-2.amazonaws.com/ce-platform/oce-example-images/half-half-embodied-mimicry-religious-building.jpg'
+      },
+      numberNeeded: 2,
+      numberAllowedToParticipateAtSameTime: 1,
+      notificationDelay: 90,
+    }]),
+    description: 'While visiting a place of worship, create a half half photo.',
+    notificationText: 'View this and other available experiences',
+    callbacks: [{
+      trigger: '(cb.numberOfSubmissions() % 2) === 0',
+      function: halfhalfRespawnAndNotify('A Religious Architecture photo completed','View the photo').toString()
+    }]
+  },
+  halfhalf_sunset_knowsOlin: {
+    _id: Random.id(),
+    name: 'Sunset Together',
+    group: 'Olin',
+    participateTemplate: 'halfhalfParticipate',
+    resultsTemplate: 'halfhalfResults',
+    contributionTypes: addStaticAffordanceToNeeds('knowsOlin', [{
+      // needName MUST have structure "My Need Name XYZ"
+      needName: 'Sunset Together 1',
+      notificationSubject: 'Can you see the sunset?',
+      notificationText: 'Share an experience with others who are also watching the sunset',
+      situation: {
+        detector: getDetectorId(DETECTORS.sunset),
+        number: '1'
+      },
+      toPass: {
+        instruction: 'What does the <span style="color: #0351ff">sunset</span> look like where you are? Find a good view; then, take a photo, with your hands outstretched towards the sun.',
+        exampleImage: 'https://s3.us-east-2.amazonaws.com/ce-platform/oce-example-images/half-half-embodied-mimicry-sunset-heart.jpg'
+      },
+      numberNeeded: 2,
+      numberAllowedToParticipateAtSameTime: 1,
+      notificationDelay: 1,
+    }]),
+    description: 'While looking up at the sunset, create a half half photo.',
+    notificationText: 'View this and other available experiences',
+    callbacks: [{
+      trigger: '(cb.numberOfSubmissions() % 2) === 0',
+      function: halfhalfRespawnAndNotify('A Sunset Together photo completed','View the photo').toString()
+    }]
+  },
+  halfhalf_asian_knowsOlin: {
+    _id: Random.id(),
+    name: 'Eating with Chopsticks',
+    group: 'Olin',
+    participateTemplate: 'halfhalfParticipate',
+    resultsTemplate: 'halfhalfResults',
+    contributionTypes: addStaticAffordanceToNeeds('knowsOlin', [{
+      // needName MUST have structure "My Need Name XYZ"
+      needName: 'Eating with Chopsticks 1',
+      notificationSubject: 'Eating at an asian restaurant?',
+      notificationText: 'Share an experience with others who are also eating asian food',
+      situation: {
+        detector: getDetectorId(DETECTORS.eating_with_chopsticks),
+        number: '1'
+      },
+      toPass: {
+        instruction: 'Are you eating <span style="color: #0351ff">asian food</span> right now? Take a photo of what you are eating, <span style="color: #0351ff">holding your chopsticks.</span>',
+        exampleImage: 'https://s3.us-east-2.amazonaws.com/ce-platform/oce-example-images/half-half-embodied-mimicry-holding-chopsticks.jpg'
+      },
+      numberNeeded: 2,
+      numberAllowedToParticipateAtSameTime: 1,
+      notificationDelay: 90
+    }]),
+    description: 'While eating asian food, create a half half photo.',
+    notificationText: 'View this and other available experiences',
+    callbacks: [{
+      trigger: '(cb.numberOfSubmissions() % 2) === 0',
+      function: halfhalfRespawnAndNotify('An Eating with Chopsticks photo completed','View the photo').toString()
+    }]
+  },
+  halfhalf_books_knowsOlin: {
+    _id: Random.id(),
+    name: 'Book Buddies',
+    group: 'Olin',
+    participateTemplate: 'halfhalfParticipate',
+    resultsTemplate: 'halfhalfResults',
+    contributionTypes: addStaticAffordanceToNeeds('knowsOlin', [{
+      // needName MUST have structure "My Need Name XYZ"
+      needName: 'Book Buddies 1',
+      notificationSubject: 'Are you at a library?',
+      notificationText: 'Share an experience with others who are also at the library',
+      situation: {
+        detector: getDetectorId(DETECTORS.library),
+        number: '1'
+      },
+      toPass: {
+        instruction: 'Sorry to interrupt your <span style="color: #0351ff">reading</span>! Find the nearest book, and take a photo <span style="color: #0351ff">holding up the book to your face.</span>',
+        exampleImage: 'https://s3.us-east-2.amazonaws.com/ce-platform/oce-example-images/half-half-embodied-mimicry-book-face.jpg'
+      },
+      numberNeeded: 2,
+      numberAllowedToParticipateAtSameTime: 1,
+      notificationDelay: 90,
+    }]),
+    description: 'While reading a book, create a half half photo.',
+    notificationText: 'View this and other available experiences',
+    callbacks: [{
+      trigger: '(cb.numberOfSubmissions() % 2) === 0',
+      function: halfhalfRespawnAndNotify('A Book Buddies photo completed','View the photo').toString()
+    }]
+  },
+  halfhalf_leakmask_knowsOlin: {
+    _id: Random.id(),
+    name: 'Leaf Mask',
+    group: 'Olin',
+    participateTemplate: 'halfhalfParticipate',
+    resultsTemplate: 'halfhalfResults',
+    contributionTypes: addStaticAffordanceToNeeds('knowsOlin', [{
+      // needName MUST have structure "My Need Name XYZ"
+      needName: 'Leaf Mask 1',
+      notificationSubject: 'Are you at a park?',
+      notificationText: 'Share an experience with others who are also at a park',
+      situation: {
+        detector: getDetectorId(DETECTORS.forest),
+        number: '1'
+      },
+      toPass: {
+        instruction: 'Find a <span style="color: #0351ff">leaf in the park</span>. Take a photo of the <span style="color: #0351ff">leaf covering your face, like it was a mask.</span>',
+        exampleImage: 'https://s3.us-east-2.amazonaws.com/ce-platform/oce-example-images/half-half-embodied-mimicry-leaf-face.jpg'
+      },
+      numberNeeded: 2,
+      numberAllowedToParticipateAtSameTime: 1,
+      notificationDelay: 90
+    }]),
+    description: 'While in the park, create a half half photo.',
+    notificationText: 'View this and other available experiences',
+    callbacks: [{
+      trigger: '(cb.numberOfSubmissions() % 2) === 0',
+      function: halfhalfRespawnAndNotify('A Feet to the trees photo completed','View the photo').toString()
+    }]
+  },
+  halfhalf_puddles_knowsOlin: {
+    _id: Random.id(),
+    name: 'Puddle Feet',
+    group: 'Olin',
+    participateTemplate: 'halfhalfParticipate',
+    resultsTemplate: 'halfhalfResults',
+    contributionTypes: addStaticAffordanceToNeeds('knowsOlin', [{
+      // needName MUST have structure "My Need Name XYZ"
+      needName: 'Puddle Feet 1',
+      notificationSubject: 'Are you outside while its raining?',
+      notificationText: 'Share an experience with others who are enjoying or enduring the rain',
+      situation: {
+        detector: getDetectorId(DETECTORS.rainy),
+        number: '1'
+      },
+      toPass: {
+        instruction: 'Is it <span style="color: #0351ff">raining</span> today? Find a <span style="color: #0351ff">puddle</span> on the ground. Take a photo of yourself, stomping on the puddle!',
+        exampleImage: 'https://s3.us-east-2.amazonaws.com/ce-platform/oce-example-images/half-half-embodied-mimicry-puddle-feet.jpg'
+      },
+      numberNeeded: 2,
+      numberAllowedToParticipateAtSameTime: 1,
+      notificationDelay: 1,
+    }]),
+    description: 'With the puddles on a rainy day, create a half half photo.',
+    notificationText: 'View this and other available experiences',
+    callbacks: [{
+      trigger: '(cb.numberOfSubmissions() % 2) === 0',
+      function: halfhalfRespawnAndNotify('A "Puddle Feet" photo completed','View the photo').toString()
+    }]
+  },
+  halfhalf_coffeesideoflaughs_knowsOlin: {
+    _id: Random.id(),
+    name: "Coffee with a side of Laughs",
+    group: 'Olin',
+    participateTemplate: 'halfhalfParticipate',
+    resultsTemplate: 'halfhalfResults',
+    contributionTypes: addStaticAffordanceToNeeds('knowsOlin', [{
+      // needName MUST have structure "My Need Name XYZ"
+      needName: "Coffee with a side of Laughs 1",
+      notificationSubject: 'Inside a coffee shop?',
+      notificationText: 'Share an experience with others who are also at a coffee shop',
+      situation: {
+        detector: getDetectorId(DETECTORS.coffee), // any place that has cups (cafes + bars + restaurants)
+        number: '1'
+      },
+      toPass: {
+        instruction: `Do you have <span style="color: #0351ff">a cup or glass</span> you are drinking? Take a photo with it in the middle of the picture. You can even try to <span style="color: #0351ff">pour some extra "cream"</span> into it too!`,
+        exampleImage: 'https://s3.us-east-2.amazonaws.com/ce-platform/oce-example-images/half-half-teasing-lotion-in-a-cup.jpg'
+      },
+      numberNeeded: 2,
+      notificationDelay: 90,
+    }]),
+    description: 'While drinking coffee at a cafe, create a half half photo.',
+    notificationText: 'View this and other available experiences',
+    callbacks: [{
+      trigger: '(cb.numberOfSubmissions() % 2) === 0',
+      function: halfhalfRespawnAndNotify("A 'Coffee with a side of Laughs' photo completed",'View the photo').toString()
+    }]
+  },
+  halfhalf_bigbites_knowsOlin: {
+    _id: Random.id(),
+    name: "Big Bites",
+    group: 'Olin',
+    participateTemplate: 'halfhalfParticipate',
+    resultsTemplate: 'halfhalfResults',
+    contributionTypes: addStaticAffordanceToNeeds('knowsOlin', [{
+      needName: "Big Bites 1", // Any restaurant that would serve something you'd eat with your hands (burrito, tacos, hotdogs, sandwiches, wraps, burgers, tradamerican, newamerican )
+      notificationSubject: 'Eating at a restaurant?',
+      notificationText: 'Share an experience with others who are enjoying big bites of their meal',
+      situation: {
+        detector: getDetectorId(DETECTORS.big_bite_restaurant),
+        number: '1'
+      },
+      toPass: {
+        instruction: `Are you <span style="color: #0351ff">eating food that would require a big bite</span> right now? Take a photo of yourself <span style="color: #0351ff">holding up your food</span> to the middle of the screen.`,
+        exampleImage: 'https://s3.us-east-2.amazonaws.com/ce-platform/oce-example-images/half-half-embodied-mimicry-big-bite.jpg'
+      },
+      numberNeeded: 2,
+      numberAllowedToParticipateAtSameTime: 1,
+      notificationDelay: 90, // https://www.quora.com/Whats-the-average-time-that-customers-wait-between-entering-a-restaurant-and-getting-served
+    }]),
+    description: 'While eating some non-trivially sized food, create a half half photo.',
+    notificationText: 'View this and other available experiences',
+    callbacks: [{
+      trigger: '(cb.numberOfSubmissions() % 2) === 0',
+      function: halfhalfRespawnAndNotify("A 'Big Bites' photo completed",'View the photo').toString()
+    }]
+  },
+};
diff --git a/imports/api/testing/cn.js b/imports/api/Testing/cn.js
similarity index 100%
rename from imports/api/testing/cn.js
rename to imports/api/Testing/cn.js
diff --git a/imports/api/testing/conversion.js b/imports/api/Testing/conversion.js
similarity index 100%
rename from imports/api/testing/conversion.js
rename to imports/api/Testing/conversion.js
diff --git a/imports/api/testing/mincn.js b/imports/api/Testing/mincn.js
similarity index 100%
rename from imports/api/testing/mincn.js
rename to imports/api/Testing/mincn.js
diff --git a/imports/api/testing/createNewExperiences.js b/imports/api/testing/createNewExperiences.js
deleted file mode 100644
index 7d660ed3..00000000
--- a/imports/api/testing/createNewExperiences.js
+++ /dev/null
@@ -1,385 +0,0 @@
-// import { Meteor } from "meteor/meteor";
-
-import { Submissions } from "../OCEManager/currentNeeds";
-import { Detectors } from "../UserMonitor/detectors/detectors";
-import { Experiences, Incidents } from "../OCEManager/OCEs/experiences";
-
-import { CONSTANTS } from "./testingconstants";
-import {
-  addContribution, createIncidentFromExperience, startRunningIncident,
-  updateExperienceCollectionDocument, updateIncidentFromExperience, updateRunningIncident
-} from "../OCEManager/OCEs/methods";
-
-
-Meteor.methods({
-  /** createOCE - general function to start OCEs through a Meteor RPC, i.e. through the browser console
-   *
-   * @param name [String] A key in CONSTANTS.EXPERIENCES object found in testingconstants.js
-   */
-  createOCE({name}) {
-    new SimpleSchema({
-      name: { type: String }
-    }).validate({name});
-
-    if (!(name in CONSTANTS.EXPERIENCES)) {
-      throw new Meteor.Error('createOCE.keynotfound',
-        `OCE by the name '${name}' was not found in CONSTANTS.EXPERIENCES`);
-    }
-
-    let exp = CONSTANTS.EXPERIENCES[name];
-    Experiences.insert(exp);
-    let incident = createIncidentFromExperience(exp);
-    startRunningIncident(incident);
-  },
-  updateOCE({name}) {
-    // FIXME(rlouie): NOTE: you must run Meteor method call updateSubmissionNeedName in addition to this function
-    new SimpleSchema({
-      name: { type: String }
-    }).validate({name});
-
-    if (!(name in CONSTANTS.EXPERIENCES)) {
-      throw new Meteor.Error('updateOCE.keynotfound',
-        `OCE by the name '${name}' was not found in CONSTANTS.EXPERIENCES`);
-    }
-
-    let exp = CONSTANTS.EXPERIENCES[name];
-    let old_exp = Experiences.findOne({name: exp.name});
-    if (!old_exp) {
-      throw new Meteor.Error('updateOCE.experiencenotfound',
-        `Experience with name '${exp.name}' was not found in Experiences collection`);
-    }
-
-    let eid = old_exp._id;
-    let old_incident = Incidents.findOne({eid: eid});
-    if (!old_incident) {
-      throw new Meteor.Error('updateOCE.incidentnotfound',
-        `Incident with eid = '${eid}' was not found in Incidents collection`);
-    }
-
-    updateExperienceCollectionDocument(eid, exp);
-    let incident = updateIncidentFromExperience(eid, exp);
-    updateRunningIncident(incident);
-    // FIXME(rlouie): NOTE: you must run Meteor method call updateSubmissionNeedName in addition to this function
-  },
-  /** upsertDetector - general function to update or insert detectors through a Meteor RPC
-   *
-   * @param name [String] A key in CONSTANTS.DETECTORS object found in testingconstants.js
-   */
-  upsertDetector({name}) {
-    new SimpleSchema({
-      name: { type: String }
-    }).validate({name});
-
-    if (!(name in CONSTANTS.DETECTORS)) {
-      throw new Meteor.Error('upsertDetector.keynotfound',
-        `Detector by the name '${name}' was not found in CONSTANTS.DETECTORS`);
-    }
-
-    let {numberAffected, insertedId} = Detectors.upsert({
-        // Selector
-        // Note: Don't select on _id, since _id in CONSTANTS.DETECTORS changes from deployments cuz of Random.id()
-        description: CONSTANTS.DETECTORS[name].description,
-      }, {
-        // Modifier
-        $set : {
-          variables: CONSTANTS.DETECTORS[name].variables,
-          rules: CONSTANTS.DETECTORS[name].rules
-        }
-      }
-    );
-    console.log(`Method call to "upsertDetector"! numberAffected: ${numberAffected}, insertedId: ${insertedId}`);
-  }
-});
-
-/* FIXME: Experiences are not workable, as the detector ids have changed
- * TODO: Write a storytime construction helper / macro which can generate different versions of storytime structure
-function createNewSpookyStorytime() {
-  let storytimeCallback = function(sub) {
-    Meteor.users.update(
-      {
-        _id: sub.uid
-      },
-      {
-        $set: {
-          "profile.staticAffordances.participatedInSpookyStorytime": true
-        }
-      }
-    );
-
-    let affordance = sub.content.affordance;
-
-    let options = [
-      ["Wolves howling at a full moon", "Dw9z8eTBvvF6EeqaR"],
-      ["Ominous clouds swirling above", "eqsBY5BBRZsFWfsS4"],
-      ["Deserted neighborhood businesses", "Hewrfn8R87Z9EfjKh"],
-      ["Eerily quiet day", "eqsBY5BBRZsFWfsS4"],
-      ["Night falling a little too quickly", "3EML6ZvzjiKTK3Myy"],
-      ["Haunted coffee shop", "vj4M9wajY9HzgmM48"]
-    ];
-
-    // remove options just chosen
-    options = options.filter(function(x) {
-      return x[1] !== affordance;
-    });
-
-    let needName = "page" + Random.id(3);
-
-    // done so you can use a different callback on last page
-    if (cb.numberOfSubmissions() === 7) {
-      needName = "pageFinal";
-    }
-
-    // need = contribution
-    let contribution = {
-      needName: needName,
-      situation: { detector: affordance, number: "1" },
-      toPass: {
-        instruction: sub.content.sentence,
-        dropdownChoices: { name: "affordance", options: options }
-      },
-      numberNeeded: 1
-    };
-    addContribution(sub.iid, contribution);
-  };
-
-  let places = ["night", "niceish_day", "restaurant", "sunset", "coffee"];
-  let detectorIds = [
-    "Dw9z8eTBvvF6EeqaR",
-    "eqsBY5BBRZsFWfsS4",
-    "Hewrfn8R87Z9EfjKh",
-    "XHj47XpSWEE6Yrmm4",
-    "3EML6ZvzjiKTK3Myy",
-    "vj4M9wajY9HzgmM48"
-  ];
-  let i = 0;
-  _.forEach(places, place => {
-    let newVars = JSON.parse(
-      JSON.stringify(CONSTANTS.DETECTORS[place]["variables"])
-    );
-    newVars.push("var participatedInSpookyStorytime;");
-
-    let det = {
-      _id: detectorIds[i],
-      description: CONSTANTS.DETECTORS[place].description + "_SpookyStorytime",
-      variables: newVars,
-      rules: [
-        "(" +
-          CONSTANTS.DETECTORS[place].rules[0] +
-          " ) && !participatedInSpookyStorytime;"
-      ]
-    };
-    Detectors.insert(det);
-
-    i++;
-  });
-
-  let dropdownOptions = [
-    ["Wolves howling at a full moon", "Dw9z8eTBvvF6EeqaR"],
-    ["Ominous clouds swirling above", "eqsBY5BBRZsFWfsS4"],
-    ["Deserted neighborhood businesses", "Hewrfn8R87Z9EfjKh"],
-    ["Eerily quiet day", "XHj47XpSWEE6Yrmm4"],
-    ["Night falling a little too quickly", "3EML6ZvzjiKTK3Myy"],
-    ["Haunted coffee shop", "vj4M9wajY9HzgmM48"]
-  ];
-
-  let firstSentence =
-    "Something was happening in this small town of seemingly happy people.";
-
-  let sendNotification = function(sub) {
-    let uids = Submissions.find({ iid: sub.iid }).fetch().map(function(x) {
-      return x.uid;
-    });
-    notify(
-      uids,
-      sub.iid,
-      "Our spooky story is finally complete. Click here to read it!",
-      "",
-      "/apicustomresults/" + sub.iid + "/" + sub.eid
-    );
-  };
-
-  let exp = {
-    _id: "Qeeb9pTQDviBuv5Dd", //Random.id(),
-    name: "Spooky Storytime",
-    participateTemplate: "storyPage",
-    resultsTemplate: "storybook",
-    contributionTypes: [
-      {
-        needName: "pageOne",
-        situation: { detector: "x7EgLErQx3qmiemqt", number: "1" },
-        toPass: {
-          instruction: firstSentence,
-          firstSentence: firstSentence,
-          dropdownChoices: {
-            name: "affordance",
-            options: dropdownOptions
-          }
-        },
-        numberNeeded: 1
-      }
-    ],
-    description: "We're writing a spooky story",
-    notificationText: "Help write a spooky story!",
-    callbacks: [
-      {
-        trigger: "cb.newSubmission() && (cb.numberOfSubmissions() <= 7)",
-        function: storytimeCallback.toString()
-      },
-      {
-        trigger: "cb.incidentFinished()",
-        function: sendNotification.toString()
-      }
-    ]
-  };
-
-  Experiences.insert(exp);
-  let incident = createIncidentFromExperience(exp);
-  startRunningIncident(incident);
-}
-
-function createNewSpookyNevilleStorytime() {
-  let storytimeCallback = function(sub) {
-    Meteor.users.update(
-      {
-        _id: sub.uid
-      },
-      {
-        $set: {
-          "profile.staticAffordances.participatedInSpookyHarryStorytime": true
-        }
-      }
-    );
-
-    let affordance = sub.content.affordance;
-
-    let options = [
-      [
-        "Sneaking around in the invisibility cloak after hours",
-        "F8YqP3AEbyguQMJ9i"
-      ],
-      ["Werewolves howling at the moon", "F8YqP3AEbyguQMJ9i"],
-      ["Getting food in Diagon Alley", "yxQP8QrCdAWakjMaY"],
-      ["Eating in the Great Hall", "yxQP8QrCdAWakjMaY"],
-      ["Exploring the Hogwarts grounds", "ueBZrF5mCRrcFBc8g"],
-      ["Drinking coffee while studying for O.W.L.S.", "DPxfkTQQFggzNJBXD"],
-      ["Looking for magical beasts flying overhead", "ueBZrF5mCRrcFBc8g"]
-    ];
-
-    options = options.filter(function(x) {
-      return x[1] !== affordance;
-    });
-
-    let needName = "page" + Random.id(3);
-    if (cb.numberOfSubmissions() === 7) {
-      needName = "pageFinal";
-    }
-    let contribution = {
-      needName: needName,
-      situation: { detector: affordance, number: "1" },
-      toPass: {
-        instruction: sub.content.sentence,
-        dropdownChoices: { name: "affordance", options: options }
-      },
-      numberNeeded: 1
-    };
-    addContribution(sub.iid, contribution);
-  };
-
-  let places = ["night", "niceish_day", "restaurant", "coffee"];
-  let detectorIds = [
-    "F8YqP3AEbyguQMJ9i",
-    "ueBZrF5mCRrcFBc8g",
-    "yxQP8QrCdAWakjMaY",
-    "DPxfkTQQFggzNJBXD"
-  ];
-  let i = 0;
-  _.forEach(places, place => {
-    let newVars = JSON.parse(
-      JSON.stringify(CONSTANTS.DETECTORS[place]["variables"])
-    );
-    newVars.push("var participatedInSpookyHarryStorytime;");
-
-    let det = {
-      _id: detectorIds[i],
-      description:
-        CONSTANTS.DETECTORS[place].description + "_SpookyHarryStorytime",
-      variables: newVars,
-      rules: [
-        "(" +
-          CONSTANTS.DETECTORS[place].rules[0] +
-          " ) && !participatedInSpookyHarryStorytime;"
-      ]
-    };
-    Detectors.insert(det);
-
-    i++;
-  });
-
-  let dropdownOptions = [
-    [
-      "Sneaking around in the invisibility cloak after hours",
-      "F8YqP3AEbyguQMJ9i"
-    ],
-    ["Werewolves howling at the moon", "F8YqP3AEbyguQMJ9i"],
-    ["Getting food in Diagon Alley", "yxQP8QrCdAWakjMaY"],
-    ["Eating in the Great Hall", "yxQP8QrCdAWakjMaY"],
-    ["Exploring the Hogwarts grounds", "ueBZrF5mCRrcFBc8g"],
-    ["Drinking coffee while studying for O.W.L.S.", "DPxfkTQQFggzNJBXD"],
-    ["Looking for magical beasts flying overhead", "ueBZrF5mCRrcFBc8g"]
-  ];
-
-  let firstSentence =
-    "Neville Longbottom looked out the castle into the darkness of the night as he snuck out of the common room";
-
-  let sendNotification = function(sub) {
-    let uids = Submissions.find({ iid: sub.iid }).fetch().map(function(x) {
-      return x.uid;
-    });
-    notify(
-      uids,
-      sub.iid,
-      "Our spooky Neville Longbottom story is finally complete. Click here to read it!",
-      "",
-      "/apicustomresults/" + sub.iid + "/" + sub.eid
-    );
-  };
-
-  let exp = {
-    _id: "QC7LGdoDCZqCY8mWb", //Random.id(),
-    name: "Spooky Storytime",
-    participateTemplate: "storyPage",
-    resultsTemplate: "storybook",
-    contributionTypes: [
-      {
-        needName: "pageOne",
-        situation: { detector: "F8YqP3AEbyguQMJ9i", number: "1" },
-        toPass: {
-          instruction: firstSentence,
-          firstSentence: firstSentence,
-          dropdownChoices: {
-            name: "affordance",
-            options: dropdownOptions
-          }
-        },
-        numberNeeded: 1
-      }
-    ],
-    description: "We're writing a spooky Neville Longbottom spin-off story",
-    notificationText: "Help write a spooky Neville Longbottom story!",
-    callbacks: [
-      {
-        trigger: "cb.newSubmission() && (cb.numberOfSubmissions() <= 7)",
-        function: storytimeCallback.toString()
-      },
-      {
-        trigger: "cb.incidentFinished()",
-        function: sendNotification.toString()
-      }
-    ]
-  };
-
-  Experiences.insert(exp);
-  let incident = createIncidentFromExperience(exp);
-  startRunningIncident(incident);
-}
-*/
diff --git a/imports/api/testing/endToEndSimple.test.js b/imports/api/testing/endToEndSimple.test.js
deleted file mode 100644
index 62f64419..00000000
--- a/imports/api/testing/endToEndSimple.test.js
+++ /dev/null
@@ -1,251 +0,0 @@
-import { resetDatabase } from 'meteor/xolvio:cleaner';
-import { Users } from '../UserMonitor/users/users';
-import { Incidents } from "../OCEManager/OCEs/experiences";
-import { CONSTANTS } from './testingconstants';
-import { onLocationUpdate } from '../UserMonitor/locations/methods';
-import { findUserByUsername } from "../UserMonitor/users/methods";
-import { Assignments} from "../OpportunisticCoordinator/databaseHelpers";
-import { Random } from 'meteor/random'
-import { Detectors } from "../UserMonitor/detectors/detectors";
-import { updateSubmission} from "../OCEManager/progressor";
-import "../OCEManager/progressorHelper";
-import {insertTestUser, startTestOCE} from "../OpportunisticCoordinator/populateDatabase";
-
-
-
-describe('Simple End To End', function () {
-  this.timeout(5*60*1000);
-
-  let second = false;
-  let OCE_NAME = 'sameSituationAwareness';
-  let NEEDNAME = 'Shopping for groceries';
-  let USERNAME = 'garrett';
-  let DETECTOR = CONSTANTS.DETECTORS.grocery;
-  let LOCATION = CONSTANTS.LOCATIONS.grocery;
-  let LOCATION2 = CONSTANTS.LOCATIONS.grocery2;
-  let LOCATION_NOMATCH = CONSTANTS.LOCATIONS.sushi;
-
-  beforeEach((done) => {
-
-    if (second) {
-      done();
-
-    } else {
-      second = true;
-      resetDatabase();
-      insertTestUser(USERNAME);
-      Detectors.insert(DETECTOR);
-      startTestOCE(OCE_NAME);
-
-      let uid = findUserByUsername(USERNAME)._id;
-      let bgLocationObj = {
-        "coords": {
-          "latitude": LOCATION.lat,
-          "longitude": LOCATION.lng
-        },
-        "activity": {"type": "unknown", "confidence": 100}
-      };
-      onLocationUpdate(uid, bgLocationObj, function() {
-        done();
-      });
-    }
-  });
-
-  it('user gets added to experience', (done) => {
-    const contributionForNeed = CONSTANTS.EXPERIENCES[OCE_NAME].contributionTypes.find(function(x) {
-      return x.needName === NEEDNAME;
-    });
-    const notificationDelay = contributionForNeed.notificationDelay;
-
-    // Wait to check if user.profile and assignments has changed, > notificationDelay seconds after first matching
-    Meteor.setTimeout(function() {
-      try {
-        let incident = Incidents.findOne({ eid: CONSTANTS.EXPERIENCES[OCE_NAME]._id });
-        let iid = incident._id;
-        let user = findUserByUsername(USERNAME);
-
-        //user has incident as an active incident
-        chai.assert(user.activeIncidents().includes(iid), 'active incident not added to user profile');
-
-        //assignments has user assigned
-        let assignmentEntry = Assignments.findOne({ _id: iid });
-
-        let needUserMap = assignmentEntry.needUserMaps.find((x) => {
-          return x.needName ===  NEEDNAME;
-        });
-
-        chai.assert.typeOf(needUserMap.users, 'array', 'no needUserMap in Assignment DB');
-        chai.assert(needUserMap.users.find(usermeta => usermeta.uid), 'uid not in needUserMap in Assignment DB');
-
-        done();
-      } catch (err) { done(err); }
-    }, (notificationDelay + 5) * 1000);
-  });
-
-  it('user steps away from vicinity but returns within delay time', function(done) {
-
-    // FIXME(rlouie): Because we are smoothing location updates, it does not make sense to suddenly
-    // transport someone to another place in this model
-
-    // move to a location on same block, but that should be outside of affordance radius
-    let uid = findUserByUsername(USERNAME)._id;
-    let bgLocationObj = {
-      "coords": {
-        "latitude": LOCATION_NOMATCH.lat,
-        "longitude": LOCATION_NOMATCH.lng
-      },
-      "activity": {"type": "unknown", "confidence": 100}
-    };
-    onLocationUpdate(uid, bgLocationObj, function() {
-      console.log("Temporarily moved outside of vicinity of grocery store");
-    });
-
-    const contributionForNeed = CONSTANTS.EXPERIENCES[OCE_NAME].contributionTypes.find(function(x) {
-      return x.needName === NEEDNAME;
-    });
-    const notificationDelay = contributionForNeed.notificationDelay;
-
-    // Wait some time less than notification Delay before moving back in
-    Meteor.setTimeout(function() {
-
-      console.log("Checking if decommissioned prematurely with first non-match");
-      // SAME CHECKS AS FIRST TIME, JUST HOPING NOW YOU DIDNT GET REMOVED
-      let incident = Incidents.findOne({ eid: CONSTANTS.EXPERIENCES[OCE_NAME]._id });
-      let iid = incident._id;
-      let user = findUserByUsername(USERNAME);
-
-      //user has incident as an active incident
-      chai.assert(user.activeIncidents().includes(iid), 'decommissioned prematurely - active incident not added to user profile');
-
-      //assignments has user assigned
-      let assignmentEntry = Assignments.findOne({ _id: iid });
-
-      let needUserMap = assignmentEntry.needUserMaps.find((x) => {
-        return x.needName ===  NEEDNAME;
-      });
-
-      chai.assert.typeOf(needUserMap.users, 'array', 'decommissioned prematurely - no needUserMap in Assignment DB');
-      chai.assert(needUserMap.users.find(usermeta => usermeta.uid), 'decommissioned prematurely - uid not in needUserMap in Assignment DB');
-
-      // Move back to location
-      onLocationUpdate(
-        uid, {
-          "coords": {
-            "latitude": LOCATION.lat,
-            "longitude": LOCATION.lng
-          },
-          "activity": {"type": "unknown", "confidence": 100}
-        }, function() {
-          console.log("Returned to the vicinity of Grocery Store")
-        });
-
-      Meteor.setTimeout(function() {
-        try {
-          // Users still active, now that they have moved back
-          let incident = Incidents.findOne({ eid: CONSTANTS.EXPERIENCES[OCE_NAME]._id });
-          let iid = incident._id;
-          let user = findUserByUsername(USERNAME);
-
-          //user has incident as an active incident
-          chai.assert(user.activeIncidents().includes(iid), 'remain assigned while back in vicinity -- active incident not added to user profile');
-
-          //assignments has user assigned
-          let assignmentEntry = Assignments.findOne({ _id: iid });
-
-          let needUserMap = assignmentEntry.needUserMaps.find((x) => {
-            return x.needName ===  NEEDNAME;
-          });
-
-          chai.assert.typeOf(needUserMap.users, 'array', 'remain assigned while back in vicinity -- no needUserMap in Assignment DB');
-          chai.assert(needUserMap.users.find(usermeta => usermeta.uid), 'remain assigned while back in vicinity -- uid not in needUserMap in Assignment DB');
-
-          done();
-        } catch (err) { done(err); }
-      }, 2 * 1000);
-
-    }, (notificationDelay) * 0.2 * 1000);
-  });
-
-  it('user participates in experience', (done) => {
-    let incident = Incidents.findOne({ eid: CONSTANTS.EXPERIENCES[OCE_NAME]._id });
-    let iid = incident._id;
-    let uid = findUserByUsername(USERNAME)._id;
-
-    let submission = {
-      uid: uid,
-      eid: CONSTANTS.EXPERIENCES[OCE_NAME]._id,
-      iid: iid,
-      needName: NEEDNAME,
-      content: {},
-      timestamp: Date.now(),
-      lat: LOCATION.lat,
-      lng: LOCATION.lng,
-    };
-
-    updateSubmission(submission);
-
-    // Wait several seconds so the observe changes of Submissions collection can run
-    Meteor.setTimeout(function () {
-      try {
-        let user = findUserByUsername(USERNAME);
-        chai.assert.isFalse(user.activeIncidents().includes(iid), 'active incident not removed from user profile');
-        chai.assert(user.profile.pastIncidents.includes(iid), 'past incident not added to user profile');
-        done();
-      } catch (err) { done(err); }
-    }, 5 * 1000);
-
-  });
-
-  it('user ALSO SHOULD be able to participate again, if need.allowRepeatContributions: true', (done) => {
-
-    // wait for userParticipatedTooRecently check to expire
-    Meteor.setTimeout(() => {
-
-      // FIXME(rlouie): Because we are smoothing location updates, it does not make sense to suddenly
-      // transport someone to another place in this model
-
-      // move to another situation that matches the same need
-      let uid = findUserByUsername(USERNAME)._id;
-      let bgLocationObj = {
-        "coords": {
-          "latitude": LOCATION2.lat,
-          "longitude": LOCATION2.lng
-        },
-        "activity": {"type": "unknown", "confidence": 100}
-      };
-      onLocationUpdate(uid, bgLocationObj, function() {
-      });
-
-      const contributionForNeed = CONSTANTS.EXPERIENCES[OCE_NAME].contributionTypes.find(function(x) {
-        return x.needName === NEEDNAME;
-      });
-      const notificationDelay = contributionForNeed.notificationDelay;
-
-      // Wait to check if user.profile and assignments has changed, > notificationDelay seconds after first matching
-      Meteor.setTimeout(function() {
-        try {
-          let incident = Incidents.findOne({ eid: CONSTANTS.EXPERIENCES[OCE_NAME]._id });
-          let iid = incident._id;
-          let user = findUserByUsername(USERNAME);
-
-          //user has incident as an active incident
-          chai.assert(user.activeIncidents().includes(iid), 'active incident not added to user profile');
-
-          //assignments has user assigned
-          let assignmentEntry = Assignments.findOne({ _id: iid });
-
-          let needUserMap = assignmentEntry.needUserMaps.find((x) => {
-            return x.needName ===  NEEDNAME;
-          });
-
-          chai.assert.typeOf(needUserMap.users, 'array', 'no needUserMap in Assignment DB');
-          chai.assert(needUserMap.users.find(usermeta => usermeta.uid), 'uid not in needUserMap in Assignment DB');
-
-          done();
-        } catch (err) { done(err); }
-      }, (notificationDelay + 5) * 1000);
-
-    // for value to wait on local, @see userParticipatedTooRecently in imports/api/UserMonitor/locations/methods.js
-    }, 60 * 1000);
-  });
-});
diff --git a/imports/api/testing/testingconstants.js b/imports/api/testing/testingconstants.js
deleted file mode 100644
index 18e6c08f..00000000
--- a/imports/api/testing/testingconstants.js
+++ /dev/null
@@ -1,515 +0,0 @@
-// import { Meteor } from "meteor/meteor";
-import { Submissions } from "../OCEManager/currentNeeds";
-import { addContribution } from '../OCEManager/OCEs/methods';
-import { Detectors } from "../UserMonitor/detectors/detectors";
-import { Incidents } from "../OCEManager/OCEs/experiences";
-import { cn } from "./cn";
-
-let LOCATIONS = {
-  'park': {
-    lat: 42.056838,
-    lng: -87.675940
-  },
-  'lakefill': {
-    lat: 42.054902,
-    lng: -87.670197
-  },
-  'burgers': {
-    lat: 42.046131,
-    lng: -87.681559
-  },
-  'grocery': {
-    lat: 42.047621,
-    lng: -87.679488
-  },
-  'sushi': { // sashimi sashimi near whole foods
-    lat: 42.048068,
-    lng: -87.6811262
-  },
-  'grocery2': {
-    lat: 42.039818,
-    lng: -87.680088
-  }
-};
-
-let USERS = {
-  meg: {
-    username: 'meg',
-    email: 'meg@email.com',
-    password: 'password',
-    profile: {
-      firstName: 'Meg',
-      lastName: 'Grasse'
-    }
-  },
-  andrew: {
-    username: 'andrew',
-    email: 'andrew@email.com',
-    password: 'password',
-    profile: {
-      firstName: 'Andrew',
-      lastName: 'Finke'
-    }
-  },
-  josh: {
-    username: 'josh',
-    email: 'josh@email.com',
-    password: 'password',
-    profile: {
-      firstName: 'Josh',
-      lastName: 'Shi'
-    }
-  }
-};
-
-let DETECTORS = {
-  daytime: {
-    _id: 'tyZMZvPKkkSPR4FpG',
-    description: 'places where it\'s daytime,',
-    variables: ['var daytime;'],
-    rules: ['daytime;']
-  },
-  coffee: {
-    _id: 'saxQsfSaBiHHoSEYK',
-    description: 'coffee',
-    variables: ['var coffeeroasteries;',
-    'var coffee;',
-    'var cafes;',
-    'var coffeeshops;',
-    'var coffeeteasupplies;'
-    ],
-    rules: ['(coffeeroasteries || coffee) || ((coffeeshops || coffeeteasupplies) || cafes)']
-  },
-  busy: {
-    _id: 'saxQsfSaBiHHoSEZX',
-    description: 'user reports to be busy',
-    variables: ['var busy;'],
-    rules: ['busy;']
-  },
-  train: {
-    _id: '2wH5bFr77ceho5BgF',
-    description: 'trains',
-    variables: ['var publictransport;', 'var trainstations;', 'var trains;'],
-    rules: ['(trainstations || trains) || publictransport;']
-  },
-  sunny: {
-    _id: '6vyrBtdDAyRArMasj',
-    description: 'clear',
-    variables: ['var clear;', 'var daytime;'],
-    rules: ['(clear && daytime);']
-  },
-  cloudy: {
-    _id: 'sorCvK53fyi5orAmj',
-    description: 'clouds',
-    variables: ['var clouds;', 'var daytime;'],
-    rules: ['(clouds && daytime);']
-  },
-  hour0: {
-    _id: "v2ANTJr1I7wle3Ek8",
-    description: "during 00:00",
-    variables: ["var hour;"],
-    rules: ["hour == 0"]
-  }
-};
-
-export const getDetectorId = (detector) => {
-  let db_detector = Detectors.findOne({description: detector.description});
-  if (db_detector) {
-    console.log('getting db detector for', detector.description, 'which is', db_detector._id);
-    console.log(db_detector)
-    return db_detector._id;
-  } else {
-    console.log('getting detector for', detector.description, 'which is', detector._id);
-
-    return detector._id;
-  }
-};
-
-Meteor.methods({
-  getDetectorId({name}) {
-    new SimpleSchema({
-      name: { type: String }
-    }).validate({name});
-
-    if (!(name in CONSTANTS.DETECTORS)) {
-      throw new Meteor.Error('getDetectorId.keynotfound',
-        `Detector by the name '${name}' was not found in CONSTANTS.DETECTORS`);
-    }
-  }
-});
-
-//the CN compiler; takes in author-defined syntax
-const convertCNtoCE = function(script) {
-  
-  let storyName = script[0]
-  let storyDescription = script[1]
-  let storyNotification = script[2]
-  let generalContext = script[3]
-  let templates = script[4]
-  let preStoryInfo = script[5]
-  let characterRoles = script[6]
-  let prompts = script[7]
-  let questions = []
-
-  //create series of questions based on what information the author specified they wanted preStoryInfo
-  for (info in preStoryInfo) {
-    let temp = {};
-    temp.question = info.question;
-    //if the question requires a short answer response
-    if (info.responseType == "text") {
-      temp.responseType = "text"
-      temp.responseData = info.responseData
-    //if the question has a list of choices to choose from
-    } else {
-      temp.responseData = []
-      for (choice in info.responseData) {
-        temp.responseData.push(choice)
-      }
-    }
-    questions.push(temp);
-  }
-
-  //console.log("questions: " + questions)
-
-  let values = [
-  'not busy at all',
-  'a little busy',
-  'somewhat busy',
-  'pretty busy',
-  'very busy'
-  ];
-
-  let dropdownText = [
-  'not busy at all',
-  'a little busy',
-  'somewhat busy',
-  'pretty busy',
-  'busy'
-  ];
-
-  let DROPDOWN_OPTIONS = _.zip(dropdownText);
-
-  //callback function, occurs once the pre-story contexts have been submitted
-  const MurderMysteryCallback = function (sub) {
-    let submissions = Submissions.find({
-      iid: sub.iid,
-      needName: sub.needName
-    }).fetch();
-
-    let contribution = Incidents.findOne({ _id: sub.iid});
-
-    //console.log("in callback")
-    //console.log(submissions.length)
-
-    //update the UI to fit the one after the callback
-    experience = Experiences.update({
-      "_id": sub.eid
-    }, {
-      "$set": {
-        "participateTemplate": contribution.contributionTypes[0].toPass.template
-      }
-    })
-
-    //iterate through each submission, casting a character and creating its respective detector for each one
-    for (let i = 0; i < submissions.length; i++) {
-      console.log("busyness: " + submissions[i].content.busy)
-      var key = submissions[i].content.busy;
-      var affordances = {}
-      affordances[key] = true;
-      console.log("staticAffordances: " + affordances.key)
-      Meteor.users.update({
-        _id: submissions[i].uid
-      }, {
-        $set: {
-          ['profile.staticAffordances.' + key]: true
-          
-        }
-      });
-
-      //find instance of CN that the submission came from
-      let instance = Incidents.findOne(submissions[i].iid);
-      console.log("detector ID: " + instance.contributionTypes[0].situation.detector)
-      let detector_id = instance.contributionTypes[0].situation.detector
-      console.log("detector rules: " + Detectors.findOne(detector_id).rules)
-      let rules = Detectors.findOne(detector_id).rules;
-
-      let participant = Meteor.users.findOne(submissions[i].uid);
-
-      let others = Meteor.users.find({
-        "profile.pastIncidents": submissions[i].iid
-      }).fetch()
-
-      let other_participants = []
-
-      console.log("others length: " + others.length)
-
-      //find every other participant in the experience
-      for (let i = 0; i < others.length; i++) {
-        let other = others[i]
-        console.log("other: " + other.profile.firstName)
-        console.log("participant: " + participant.profile.firstName)
-        //in the future, need to account for when participants have the same first name (use UID instead)
-        if (other.profile.firstName != participant.profile.firstName) {
-          other_participants.push(other.profile.firstName)
-          console.log("other: " + other.profile.firstName + " " + other_participants.length)
-        }
-      }
-
-      console.log("participants: " + participant);
-
-
-
-      //check to see how busy the user is
-      let characterRoles = contribution.contributionTypes[0].toPass.characterRoles;
-      let character = []
-      let cast = false;
-      //iterate through each character role, and find the role that best fits the current submission or participant
-      for (let k = 0; k < characterRoles.length; k++) {
-        for (let j = 0; j < characterRoles[k].context.length; j++){
-          console.log("within inner loop " + j + " " + k)
-          if (participant.profile.staticAffordances[characterRoles[k].context[j]]) {
-            console.log("Casting a " + characterRoles[k].roleName);
-            rules = submissions[i].content.busy + " && " + rules;
-            character.push([rules, characterRoles[k].roleName, contribution.contributionTypes[0].toPass.template, characterRoles[k].instruction, participant._id, other_participants])
-            cast = true;
-            break;
-          }
-        }
-        if (cast) {
-          break;
-        };
-      }
-
-      console.log("character length" + character.length)
-
-      let extraAffordances = []
-
-      extraAffordances.push(submissions[i].content.busy)
-
-      //create the detector for the character
-      _.forEach(character, (charac) => {
-
-        let [detectorName, role, template, instruction, user, others] = charac;
-
-        var need = {
-          needName: "Murder Mystery" + role,
-          situation: {
-            detector: detectorName,
-            number: 2
-          },
-          participateTemplate: template,
-          toPass: {
-            role: role,
-            instruction: instruction,
-            user: user,
-            dropdownChoices: {
-              name: others,
-              options: others
-            }
-          },
-          numberNeeded: 2
-        };
-        //next_experience.contributionTypes.push(need)
-        addContribution(sub.iid, need);
-        console.log("more needs" + instance.contributionTypes.length)
-
-        Meteor.call("sendWhisper", role, user, instruction, (error, response) => {
-          if (error) {
-            alert(error.reason);
-          } else {
-            //Cookie.set("name", response.name);
-            //$input.val("");
-          }
-        })
-
-        let prompts = contribution.contributionTypes[0].toPass.prompts;
-
-        //when casting the murderer, set up the series of hints that will be sent out as verious points
-        if (submissions[i].content.busy == "very busy") {
-
-          for (let z = 0; z < prompts.length-1; z++) {
-            if (prompts[z].info != "") {
-              prompts[z].info = submissions[i].content[prompts[z].info] + "!";
-            }
-            let info = prompts[z].prompt + prompts[z].info;
-            Meteor.setTimeout(function() {Meteor.call("sendPrompt", info, (error, response) => {
-            if (error) {
-              alert(error.reason);
-            } else {
-              //Cookie.set("name", response.name);
-              //$input.val("");
-            }
-          })}, prompts[z].timing * 1000)
-          }
-
-          //the last prompt is the ending of the story, which will reveal who the murderer is 
-          let ending = prompts[prompts.length-1].prompt + participant.profile.firstName + "!";
-
-          Meteor.setTimeout(function() {Meteor.call("sendPrompt", ending, (error, response) => {
-            if (error) {
-              alert(error.reason);
-            } else {
-              //Cookie.set("name", response.name);
-              //$input.val("");
-            }
-            })}, prompts[prompts.length-1].timing * 1000)  
-        }
-      });
-    }    
-  }
-
-  //create a general experience instance, specify how many people are needed before the story begins
-  let experience = {
-    name: storyName,
-    participateTemplate: templates[0],
-    resultsTemplate: templates[1],
-    contributionTypes: [
-    ],
-    description: storyDescription,
-    notificationText: storyNotification,
-    callbacks: [{
-      trigger: 'cb.newSubmission() && (cb.numberOfSubmissions() == 3)',
-        // substitute any variables used outside of the callback function scope
-        function: eval('`' + MurderMysteryCallback.toString() + '`'),
-      }]
-    };
-
-    const staticAffordances = ['cn'];
-    const places = [
-    [generalContext[0], generalContext[1], storyNotification],
-    ];
-
-//create the initial detector/contribution type for people to submit their pre-story context
-staticAffordances.forEach(affordance => {
-  experience.contributionTypes = [...experience.contributionTypes, ...addStaticAffordanceToNeeds(affordance, ((places) => 
-    places.map(place => {
-      const [detectorName, situationDescription, instruction] = place;
-      return {
-        needName: `${experience.name} ${detectorName}`,
-        situation: {
-          detector: getDetectorId(DETECTORS[detectorName]),
-          number: 3
-        },
-        participateTemplate: templates[0],
-        toPass: {
-          prompts: prompts,
-          characterRoles: characterRoles,
-          template: templates[1],
-          situationDescription: `Having a good time ${situationDescription}?`,
-          instruction: `${instruction}`,
-          questions: preStoryInfo,
-          dropdownChoices: {
-            name: values,
-            options: DROPDOWN_OPTIONS
-          }
-        },
-        numberNeeded: 3,
-          // notificationDelay: 90 uncomment for testing
-        }
-      })
-    )(places))];
-});
-return experience;
-}
-
-/**
- * Following is the code that an author has to write to create a CN.
- * This is the concise syntax that is then compiled into a working CN.
- * 
- */
-
-
-
-/**
- * Following are CE helper functions:
- *
- * Side effect: Changes the global DETECTORS object, adding another detector with key "detectorKey_staticAffordance"
- *
- * @param staticAffordance
- * @param detectorKey
- * @returns newDetectorKey
- */
-
- const addStaticAffordanceToDetector = function(staticAffordance, detectorKey) {
-  let newVars = JSON.parse(JSON.stringify(DETECTORS[detectorKey]['variables']));
-  newVars.push(`var ${staticAffordance};`);
-  let newRules = JSON.parse(JSON.stringify(DETECTORS[detectorKey]['rules']));
-  // modify last detector rule
-  // when rules has a flat structure where rules.length == 1, last rule is the predicate
-  // i.e. ['(diners || restaurants || cafeteria || food_court);']
-  // when rules have a nested structure where rules.length > 1, last rule is the predicate
-  // i.e. ['worship_places = (buddhist_temples || churches);', '(worship_places || landmarks);']
-  let lastRule = newRules.pop();
-  // each rule has a `;` at end, i.e. (rain && park);
-  // in order to modify the rule, must add predicate preceding the rule
-  let lastRuleNoSemicolon = lastRule.split(';')[0];
-  lastRule = `(${staticAffordance} && (${lastRuleNoSemicolon}));`;
-  newRules.push(lastRule);
-
-  let newDetectorKey = `${detectorKey}_${staticAffordance}`;
-  // Change DETECTORS if newDetectorKey does not already exist (some experiences might have already created coffee_mechanismRich, for example)
-  if (!(newDetectorKey in DETECTORS)) {
-    DETECTORS[newDetectorKey] = {
-      '_id': Random.id(),
-      'description': `${DETECTORS[detectorKey].description} ${staticAffordance}`,
-      'variables': newVars,
-      'rules': newRules
-    };
-  }
-  console.log( DETECTORS[newDetectorKey].description,  DETECTORS[newDetectorKey]._id);
-  return newDetectorKey;
-};
-
-/**
- *
- * @param staticAffordances [String] the affordance to add
- *        i.e. 'mechanismRich'
- * @param contributionTypes [Array] list of all the needs by which to modify
- * @return
- */
- const addStaticAffordanceToNeeds = function(staticAffordance, contributionTypes) {
-  return _.map(contributionTypes, (need) => {
-    let detectorKey;
-    _.forEach(_.keys(DETECTORS), (key) => {
-      if (DETECTORS[key]._id === need.situation.detector) {
-        detectorKey = key;
-      }
-    });
-    // WILL THROW ERROR if we don't find the matching detector id
-
-    let newDetectorKey = addStaticAffordanceToDetector(staticAffordance, detectorKey);
-    need.situation.detector = DETECTORS[newDetectorKey]._id;
-    console.log('adding to need', newDetectorKey, DETECTORS[newDetectorKey]._id);
-    return need;
-  });
-};
-
-/**
- * @param contributionTypes
- * @param triggerTemplate [String] should be written as a string, with ES6 templating syntax
- *        i.e. "cb.newSubmission(\"${need.needName}\")"
- *        If using templating syntax, you have access to the each individual need object
- * @param sendNotificationFn
- */
- const notifCbForMultiNeeds = function(contributionTypes, triggerTemplate, sendNotificationFn) {
-  return contributionTypes.map((need) => {
-    return {
-      trigger: eval('`' + triggerTemplate + '`'),
-      function: sendNotificationFn.toString()
-    };
-  });
-};
-
-let EXPERIENCES = {
-  cn: convertCNtoCE(cn())
-};
-
-export const CONSTANTS = {
-  'LOCATIONS': LOCATIONS,
-  'USERS': USERS,
-  // Comment out if you would like to only test specific experiences
-  // 'EXPERIENCES': (({ halfhalfEmbodiedMimicry }) => ({ halfhalfEmbodiedMimicry }))(EXPERIENCES),
-  'EXPERIENCES': EXPERIENCES,
-  // 'EXPERIENCES': TRIADIC_EXPERIENCES,
-  'DETECTORS': DETECTORS
-};
diff --git a/imports/startup/server/fixtures.js b/imports/startup/server/fixtures.js
index 484887fe..cf98071b 100644
--- a/imports/startup/server/fixtures.js
+++ b/imports/startup/server/fixtures.js
@@ -11,7 +11,7 @@ import { Assignments, Availability } from "../../api/OpportunisticCoordinator/da
 import { Images, Avatars } from '../../api/ImageUpload/images.js';
 import { log } from '../../api/logs.js';
 
-import { CONSTANTS } from "../../api/testing/testingconstants";
+import { CONSTANTS } from "../../api/Testing/testingconstants";
 import { createIncidentFromExperience, startRunningIncident } from "../../api/OCEManager/OCEs/methods.js";
 import { findUserByUsername } from '../../api/UserMonitor/users/methods';
 import { Detectors } from "../../api/UserMonitor/detectors/detectors";
diff --git a/imports/startup/server/register-api.js b/imports/startup/server/register-api.js
index aeea8706..4baf5519 100644
--- a/imports/startup/server/register-api.js
+++ b/imports/startup/server/register-api.js
@@ -15,6 +15,6 @@ import '../../api/OpportunisticCoordinator/server/publications.js';
 import '../../api/OCEManager/progressorHelper.js';
 import '../../api/OCEManager/server/publications.js';
 import '../../api/OpportunisticCoordinator/server/noticationMethods.js';
-import '../../api/testing/createNewExperiences.js';
+import '../../api/Testing/createNewExperiences.js';
 import '../../api/Logging/page_log/methods.js';
 import '../../api/Logging/groundtruth_log/methods.js';