Skip to content

Commit

Permalink
debug panel
Browse files Browse the repository at this point in the history
  • Loading branch information
squidgetx committed Dec 17, 2022
1 parent 57e96a7 commit 0a392ae
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 151 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ secrets.sh

server/db/pgdata/*
server/db/logfile
server/node_modules


.venv
Expand Down
56 changes: 26 additions & 30 deletions extension/src/js/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,20 @@ import * as facebook from "./facebook";
import { getLogger } from "./log";

chrome.storage.sync.get(
["workerID", "treatment_group", "install_code"],
function (result) {
const workerID = result.workerID;
const installCode = result.install_code;
const treatment_group = result.treatment_group;
[
"workerID",
"install_code",
"install_time",
"treatment_group",
"mock_ideo",
"inject_rate",
"debug_mode",
],
function (exp_config) {
//todo typescript this mofo
const workerID = exp_config.workerID;
const installCode = exp_config.install_code;
const treatment_group = exp_config.treatment_group;
if (
treatment_group == undefined ||
workerID == undefined ||
Expand All @@ -21,46 +30,33 @@ chrome.storage.sync.get(
/*
* Set up observer
*/
setupFeedObserver(treatment_group, workerID, installCode, logger);
setupFeedObserver(exp_config, logger);
// Re attach the observer when the back button is used, or
// when a link is clicked
window.addEventListener("popstate", function () {
setupFeedObserver(treatment_group, workerID, installCode, logger);
setupFeedObserver(exp_config, logger);
});
window.addEventListener("click", function () {
setupFeedObserver(treatment_group, workerID, installCode, logger);
setupFeedObserver(exp_config, logger);
});
}
);

const setupFeedObserver = function (
treatment_group,
workerID,
install_code,
logger
) {
const setupFeedObserver = function (exp_config, logger) {
// The timeline is loaded with async JS
// So, we want to trigger filtering logic whenever its modified
console.log(
`Timeline Extension Loaded, respondent ID ${workerID}, treatment group ${treatment_group}, install_code ${installCode}`
);
const config = {
attributes: false,
childList: true,
subtree: true,
characterData: true,
};
console.log(`Timeline Extension Loaded, ${exp_config}`);
const container = document.documentElement || document.body;
let observer;
if (window.location.hostname.includes("twitter")) {
observer = twitter.getObserver(
treatment_group,
workerID,
install_code,
logger
);
observer = twitter.getObserver(exp_config, logger);
} else {
observer = facebook.getObserver(treatment_group, logger);
}
observer.observe(container, config);
observer.observe(container, {
attributes: false,
childList: true,
subtree: true,
characterData: true,
});
};
9 changes: 5 additions & 4 deletions extension/src/js/fetch_tweets.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ chrome.runtime.onMessage.addListener((message) => {
});

// Non-blocking func returns undefined if the fresh_tweets pool is empty
export const fetch_tweet = function (username, install_code) {
export const fetch_tweet = function (exp_config) {
if (!waiting && fresh_tweets.length < 4) {
console.log(`requesting more tweets... for ${username}|${install_code}`);
console.log(`requesting more tweets... for ${exp_config}`);
chrome.runtime.sendMessage({
message: "fetch",
username: username,
install_code: install_code,
username: exp_config.workerID,
install_code: exp_config.install_code,
ideo: exp_config.mock_ideo,
});
waiting = true;
}
Expand Down
156 changes: 92 additions & 64 deletions extension/src/js/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,61 @@ import { CONFIG } from "./config";
import { intervalToDuration } from "date-fns";

window.onload = function () {
let renderRegisteredPopup = function (result) {
if (result) {
const contentDiv = document.getElementById("content");
contentDiv.innerHTML = `<p>Thank you for participating in this study. Your registration code is <b>${result.install_code}</b>.</p>`;
const sinceInstall = intervalToDuration({
start: result.install_time,
end: Date.now(),
});
const inRecontactPeriod =
sinceInstall.days >= CONFIG.recontactIntervalDays;
const days = CONFIG.recontactIntervalDays - sinceInstall.days;
let recontactHTML = `<p>Please keep this extension installed for the next ${days} days.`;
const setupDebugConsole = function (exp_config) {
document.getElementById("debug-mode").checked = exp_config.debug_mode;
document.getElementById("debug-mode").addEventListener("change", (ev) => {
chrome.storage.sync.set({ debug_mode: ev.currentTarget.checked });
});

if (inRecontactPeriod) {
if (result.eligible) {
recontactHTML = `<p>Thank you and congratulations! You are eligible for a followup survey! Please click <b><a href=${CONFIG.recontactURL} target='_blank'>here</a></b> for the link to the final survey and a $${CONFIG.rewardDollars} reward!`;
} else {
recontactHTML = `<p>Thank you for your participation. You are now free to uninstall this extension at any time.`;
}
}
contentDiv.innerHTML += recontactHTML;
const ideo_buttons = document.querySelectorAll(
'input[type=radio][name="ideo"]'
);
const ideo_map = {
0: 0,
"-1": 1,
1: 2,
};
ideo_buttons[ideo_map[exp_config.mock_ideo]].checked = true;
ideo_buttons.forEach((radio) =>
radio.addEventListener("change", () =>
chrome.storage.sync.set({ mock_ideo: radio.value })
)
);

const treat_buttons = document.querySelectorAll(
'input[type=radio][name="treat"]'
);
treat_buttons[exp_config.treatment_group].checked = true;
treat_buttons.forEach((radio) =>
radio.addEventListener("change", () =>
chrome.storage.sync.set({ treatment_group: radio.value })
)
);

document.getElementById("inject-rate").value = exp_config.inject_rate;
document.getElementById("inject-rate").addEventListener("change", (ev) => {
chrome.storage.sync.set({ inject_rate: ev.value });
});
};

const renderRegisteredPopup = function (exp_config) {
const contentDiv = document.getElementById("content");
contentDiv.innerHTML = `<p>Thank you for participating in this study. Your registration code is <b>${exp_config.install_code}</b>.</p>`;
const sinceInstall = intervalToDuration({
start: exp_config.install_time,
end: Date.now(),
});
const daysLeft = CONFIG.studyLengthDays - sinceInstall.days;
const timer = document.createElement("p");
if (daysLeft > 0) {
timer.innerHTML = `Please keep this extension installed for the next ${daysLeft} days.`;
} else {
timer.innerHTML = `Thank you for your participation. You are now free to uninstall this extension at any time.`;
}
contentDiv.appendChild(timer);
};

let error = function (err_msg) {
const error = function (err_msg) {
console.log(err_msg);
document.getElementById("error_msg").innerHTML = err_msg;
};
Expand All @@ -46,62 +76,60 @@ window.onload = function () {
if (workerID.startsWith("@")) {
workerID = workerID.slice(1);
}
let treatment_group = Math.floor(Math.random() * 3);

// Hardcode treatment group N % 4 for test accounts with the format TEST_N
const match = workerID.match(/TEST_(\d+)/);
if (match) {
treatment_group = match[1] % 4;
}
const treatment_group = Math.floor(Math.random() * 3);

// validation
if (!workerID) {
error("You must enter a worker ID");
return;
}
const install_code = getCompleteCode(workerID);
const init_exp_config = {
workerID: workerID,
install_code: install_code,
install_time: Date.now(),
treatment_group: treatment_group,
mock_ideo: 0,
debug_mode: false,
inject_rate: CONFIG.inject_rate,
};

chrome.storage.sync.set(
{
workerID: workerID,
treatment_group: treatment_group,
install_code: install_code,
install_time: Date.now(),
last_recontact: Date.now(),
},
function () {
console.log(`Registration completed: ${install_code}`);
chrome.storage.sync.set(init_exp_config, function () {
console.log(`Registration completed: ${install_code}`);

// Write an installation event to S3 and immediately flush it
fetch(CONFIG.serverEndpoint + "/register", {
method: "POST",
body: JSON.stringify({
username: workerID,
treatment_group: treatment_group,
install_code: install_code,
}),
headers: {
"Content-Type": "application/json",
},
}).then(() => {});
// TODO error handle?

renderRegisteredPopup({
workerID: workerID,
// Tell the server about the new user
fetch(CONFIG.serverEndpoint + "/register", {
method: "POST",
body: JSON.stringify({
username: workerID,
treatment_group: treatment_group,
install_code: install_code,
install_time: Date.now(),
eligible: false,
});
}
);
}),
headers: {
"Content-Type": "application/json",
},
}).then(() => {});
// TODO error handle?

renderRegisteredPopup(init_exp_config);
setupDebugConsole(init_exp_config);
});
});

chrome.storage.sync.get(
["workerID", "install_code", "install_time", "eligible"],
function (result) {
console.log("Default result is ", result);
if (result.workerID) {
renderRegisteredPopup(result);
[
"workerID",
"install_code",
"install_time",
"treatment_group",
"mock_ideo",
"inject_rate",
"debug_mode",
],
function (exp_config) {
if (exp_config.workerID) {
renderRegisteredPopup(exp_config);
setupDebugConsole(exp_config);
}
}
);
Expand Down
44 changes: 13 additions & 31 deletions extension/src/js/twitter.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ const dlog = function (str) {
* the parent div preserved, so we can log
* "counterfactual" impressions of removed tweets
*/
const filterTweets = function (tweets, treatment_group) {
const filterTweets = function (tweets, exp_config) {
const treatment_group = exp_config.treatment_group;
for (const tweet of tweets) {
if (tweet == null) {
continue;
Expand Down Expand Up @@ -346,7 +347,8 @@ const isEligible = function (tweet) {
* Given the array of tweet objects and users' treatment group,
* inject tweets into the timeline by transforming eligible tweets
*/
let injectTweets = function (tweets, workerID, install_code, treatment_group) {
let injectTweets = function (tweets, exp_config) {
const treatment_group = exp_config.treatment_group;
if (treatment_group == 0 || treatment_group == 1) {
return;
}
Expand All @@ -356,8 +358,8 @@ let injectTweets = function (tweets, workerID, install_code, treatment_group) {
continue;
}

if (Math.random() < INJECTION_RATE) {
const new_tweet = fetch_tweet(workerID, install_code);
if (Math.random() < exp_config.inject_rate) {
const new_tweet = fetch_tweet(exp_config);
if (new_tweet) {
transformTweet(tweets[i], new_tweet);
tweets[i].data.injectedTweet = new_tweet;
Expand Down Expand Up @@ -413,14 +415,7 @@ const monitorTweets = function (tweets, logger) {
* Where the magic happens. Parse the twitter feed, remove tweets if needed, add tweets if needed,
* and log the whole thing
*/
const processFeed = function (
document,
observer,
treatment_group,
workerID,
install_code,
logger
) {
const processFeed = function (observer, exp_config, logger) {
let timelineDiv = document.querySelector(
'[aria-label="Timeline: Your Home Timeline"]'
);
Expand All @@ -435,38 +430,25 @@ const processFeed = function (
const children = parentNode.childNodes;
const tweets = parseTweets(children);

filterTweets(tweets, treatment_group);
injectTweets(tweets, workerID, install_code, treatment_group);
filterTweets(tweets, exp_config);
injectTweets(tweets, exp_config);
monitorTweets(tweets, logger);
disableInjectedContextMenus();
removeInjectedMedia();

// re-register, for when the user scrolls
const config = {
observer.observe(timelineDiv, {
attributes: false,
childList: true,
subtree: true,
characterData: true,
};
observer.observe(timelineDiv, config);
});
}
};

export const getObserver = function (
treatment_group,
workerID,
install_code,
logger
) {
export const getObserver = function (exp_config, logger) {
const observer = new MutationObserver(function (mutations) {
processFeed(
document,
observer,
treatment_group,
workerID,
install_code,
logger
);
processFeed(observer, exp_config, logger);
});
return observer;
};
Loading

0 comments on commit 0a392ae

Please sign in to comment.