v1.1.0/release

This commit is contained in:
2021-04-21 13:04:00 -04:00
parent 36674febe4
commit abb00ef27b
7 changed files with 452 additions and 227 deletions
+3
View File
@@ -54,12 +54,15 @@
}, },
"emulators": { "emulators": {
"functions": { "functions": {
"host": "192.168.86.46",
"port": 5001 "port": 5001
}, },
"firestore": { "firestore": {
"host": "192.168.86.46",
"port": 8080 "port": 8080
}, },
"hosting": { "hosting": {
"host": "192.168.86.46",
"port": 5000 "port": 5000
}, },
"ui": { "ui": {
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"name": "functions", "name": "vaxandme-api",
"scripts": { "scripts": {
"lint": "eslint --ext .js,.ts .", "lint": "eslint --ext .js,.ts .",
"build": "tsc", "build": "tsc",
+218 -134
View File
@@ -19,24 +19,39 @@ admin.initializeApp({
credential: admin.credential.cert(params), credential: admin.credential.cert(params),
}); });
const VAX_NAMES : Record<string, string> = { const VAX_NAMES: Record<string, string> = {
PB: "Pfizer-BioNTech", PB: "Pfizer-BioNTech",
MD: "Moderna", MD: "Moderna",
JJ: "J&J Janssen", JJ: "J&J Janssen",
}; };
const VAX_DOSES : Record<string, string> = { const VAX_DOSES: Record<string, string> = {
ONE: "Dose 1", ONE: "Dose 1",
TWO: "Dose 2", TWO: "Dose 2",
}; };
const VAX_AGES: Record<string, Record<string, string>> = {
"Pfizer-BioNTech": {
YOUNGER: "18 - 55",
OLDER: "56 & Older",
},
"Moderna": {
YOUNGER: "18 - 64",
OLDER: "65 & Older",
},
"J&J Janssen": {
YOUNGER: "18 - 59",
OLDER: "60 & Older",
},
};
type SaveResultRequestData = { type SaveResultRequestData = {
name: string; name: string;
age: string; age: string;
dose: string; dose: string;
gid: string; gid: string;
} & { } & {
[x: string] : boolean; [x: string]: boolean;
}; };
const serializeGaId = (_gaid: string): string => { const serializeGaId = (_gaid: string): string => {
@@ -46,86 +61,90 @@ const serializeGaId = (_gaid: string): string => {
return ""; return "";
}; };
const addResultToStats = async (data : SaveResultRequestData) => { const addResultToStats = async (data: SaveResultRequestData) => {
const cleanData : Record<string, any> = const cleanData: Record<string, any> = Object.assign({}, data);
Object.assign({}, data);
delete cleanData.name; delete cleanData.name;
delete cleanData.dose; delete cleanData.dose;
delete cleanData.age; delete cleanData.age;
delete cleanData.gid; delete cleanData.gid;
const updateDoc : Record<string, admin.firestore.FieldValue> = { const updateDoc: Record<string, admin.firestore.FieldValue> = {
count: admin.firestore.FieldValue.increment(1), count: admin.firestore.FieldValue.increment(1),
}; };
if (data.name === VAX_NAMES.PB || data.name === VAX_NAMES.MD) { if (data.name === VAX_NAMES.PB || data.name === VAX_NAMES.MD) {
if (data.dose === VAX_DOSES.ONE) { if (data.dose === VAX_DOSES.ONE) {
updateDoc[`${VAX_DOSES.ONE}.${data.age}.count`] = updateDoc[
admin.firestore.FieldValue.increment(1); `${VAX_DOSES.ONE}.${data.age}.count`
] = admin.firestore.FieldValue.increment(1);
} else if (data.dose === VAX_DOSES.TWO) { } else if (data.dose === VAX_DOSES.TWO) {
updateDoc[`${VAX_DOSES.ONE}.${data.age}.count`] = updateDoc[
admin.firestore.FieldValue.increment(1); `${VAX_DOSES.ONE}.${data.age}.count`
updateDoc[`${VAX_DOSES.TWO}.${data.age}.count`] = ] = admin.firestore.FieldValue.increment(1);
admin.firestore.FieldValue.increment(1); updateDoc[
`${VAX_DOSES.TWO}.${data.age}.count`
] = admin.firestore.FieldValue.increment(1);
} }
Object.keys(VAX_DOSES).forEach((doseKey : string) => { Object.keys(VAX_DOSES).forEach((doseKey: string) => {
const dose : string = VAX_DOSES[doseKey]; const dose: string = VAX_DOSES[doseKey];
Object.keys(cleanData[dose]).forEach((symptom : string) => { Object.keys(cleanData[dose]).forEach((symptom: string) => {
if (cleanData[dose][symptom]) { if (cleanData[dose][symptom]) {
let updateDocSymptom = symptom.replace("/", "OR"); let updateDocSymptom = symptom.replace("/", "OR");
if (symptom === "DIARRHEA") { if (symptom === "DIARRHEA") {
updateDocSymptom = "DIRRHEA"; updateDocSymptom = "DIRRHEA";
} }
updateDoc[`${dose}.${data.age}.${updateDocSymptom}`] = updateDoc[
admin.firestore.FieldValue.increment(1); `${dose}.${data.age}.${updateDocSymptom}`
] = admin.firestore.FieldValue.increment(1);
} }
}); });
}); });
} else { } else {
updateDoc[`${data.age}.count`] = updateDoc[`${data.age}.count`] = admin.firestore.FieldValue.increment(1);
admin.firestore.FieldValue.increment(1); Object.keys(cleanData).forEach((symptom: string) => {
Object.keys(cleanData).forEach((symptom : string) => {
let updateDocSymptom = symptom.replace("/", "OR"); let updateDocSymptom = symptom.replace("/", "OR");
if (symptom === "DIARRHEA") { if (symptom === "DIARRHEA") {
updateDocSymptom = "DIRRHEA"; updateDocSymptom = "DIRRHEA";
} }
updateDoc[`${data.age}.${updateDocSymptom}`] = updateDoc[
admin.firestore.FieldValue.increment(1); `${data.age}.${updateDocSymptom}`
] = admin.firestore.FieldValue.increment(1);
}); });
} }
admin.firestore().collection("stats").doc(data.name).update(updateDoc); admin.firestore().collection("stats").doc(data.name).update(updateDoc);
}; };
const removeResultFromStats = async (data : FirebaseFirestore.DocumentData) => { const removeResultFromStats = async (data: FirebaseFirestore.DocumentData) => {
const cleanData : Record<string, any> = const cleanData: Record<string, any> = Object.assign({}, data);
Object.assign({}, data);
delete cleanData.name; delete cleanData.name;
delete cleanData.dose; delete cleanData.dose;
delete cleanData.age; delete cleanData.age;
delete cleanData.gid; delete cleanData.gid;
const updateDoc : Record<string, admin.firestore.FieldValue> = { const updateDoc: Record<string, admin.firestore.FieldValue> = {
count: admin.firestore.FieldValue.increment(-1), count: admin.firestore.FieldValue.increment(-1),
}; };
if (data.name === VAX_NAMES.PB || data.name === VAX_NAMES.MD) { if (data.name === VAX_NAMES.PB || data.name === VAX_NAMES.MD) {
Object.keys(VAX_DOSES).forEach((doseKey : string) => { Object.keys(VAX_DOSES).forEach((doseKey: string) => {
const dose : string = VAX_DOSES[doseKey]; const dose: string = VAX_DOSES[doseKey];
Object.keys(cleanData[dose]).forEach((symptom : string) => { Object.keys(cleanData[dose]).forEach((symptom: string) => {
if (cleanData[dose][symptom]) { if (cleanData[dose][symptom]) {
let updateDocSymptom = symptom.replace("/", "OR"); let updateDocSymptom = symptom.replace("/", "OR");
if (symptom === "DIARRHEA") { if (symptom === "DIARRHEA") {
updateDocSymptom = "DIRRHEA"; updateDocSymptom = "DIRRHEA";
} }
updateDoc[`${dose}.${data.age}.${updateDocSymptom}`] = updateDoc[
admin.firestore.FieldValue.increment(-1); `${dose}.${data.age}.${updateDocSymptom}`
] = admin.firestore.FieldValue.increment(-1);
} }
}); });
}); });
} else { } else {
Object.keys(cleanData).forEach((symptom : string) => { Object.keys(cleanData).forEach((symptom: string) => {
let updateDocSymptom = symptom.replace("/", "OR"); let updateDocSymptom = symptom.replace("/", "OR");
if (symptom === "DIARRHEA") { if (symptom === "DIARRHEA") {
updateDocSymptom = "DIRRHEA"; updateDocSymptom = "DIRRHEA";
} }
updateDoc[`${data.age}.${updateDocSymptom}`] = updateDoc[
admin.firestore.FieldValue.increment(-1); `${data.age}.${updateDocSymptom}`
] = admin.firestore.FieldValue.increment(-1);
}); });
} }
admin.firestore().collection("stats").doc(data.name).update(updateDoc); admin.firestore().collection("stats").doc(data.name).update(updateDoc);
@@ -134,18 +153,67 @@ const removeResultFromStats = async (data : FirebaseFirestore.DocumentData) => {
/** /**
* FUNCTIONS FROM V1.1.0 TO CURRENT * FUNCTIONS FROM V1.1.0 TO CURRENT
*/ */
export const getStatsV1 = functions.https.onCall( export const getStatsV1 = functions.https.onCall(async () => {
async () => { const stats = await admin.firestore().collection("stats").get();
const stats = await admin.firestore().collection("stats").get(); const docs = stats.docs.map((docSnap) => ({
const docs = stats.docs name: docSnap.id,
.map((docSnap) => ({name: docSnap.id, result: docSnap.data()})); result: docSnap.data(),
const returnData : Record<string, unknown> = {}; }));
docs.forEach((vaxResult) => { const returnData: Record<string, unknown> = {};
returnData[vaxResult.name] = {}; docs.forEach((vaxResult) => {
returnData[vaxResult.name] = vaxResult.result; if (vaxResult.name === VAX_NAMES.PB || vaxResult.name === VAX_NAMES.MD) {
Object.keys(VAX_DOSES).forEach((doseKey) => {
const dose: string = VAX_DOSES[doseKey];
Object.keys(VAX_AGES[vaxResult.name]).forEach((ageKey) => {
const age: string = VAX_AGES[vaxResult.name][ageKey];
if (vaxResult.result[dose][age]["NAUSEA / VOMITING"] >= 0) {
if (vaxResult.result[dose][age]["NAUSEA OR VOMITING"] >= 0) {
vaxResult.result[dose][age]["NAUSEA OR VOMITING"] +=
vaxResult.result[dose][age]["NAUSEA / VOMITING"];
} else {
vaxResult.result[dose][age]["NAUSEA OR VOMITING"] =
vaxResult.result[dose][age]["NAUSEA / VOMITING"];
}
}
if (vaxResult.result[dose][age]["DIRRHEA"] >= 0) {
if (vaxResult.result[dose][age]["DIARRHEA"] >= 0) {
vaxResult.result[dose][age]["DIARRHEA"] +=
vaxResult.result[dose][age]["DIRRHEA"];
} else {
vaxResult.result[dose][age]["DIARRHEA"] =
vaxResult.result[dose][age]["DIRRHEA"];
}
}
});
}); });
return returnData; } else {
}); Object.keys(VAX_AGES[vaxResult.name]).forEach((ageKey) => {
const age: string = VAX_AGES[vaxResult.name][ageKey];
if (vaxResult.result[age]["NAUSEA / VOMITING"] >= 0) {
if (vaxResult.result[age]["NAUSEA OR VOMITING"] >= 0) {
vaxResult.result[age]["NAUSEA OR VOMITING"] +=
vaxResult.result[age]["NAUSEA / VOMITING"];
} else {
vaxResult.result[age]["NAUSEA OR VOMITING"] =
vaxResult.result[age]["NAUSEA / VOMITING"];
}
}
if (vaxResult.result[age]["DIRRHEA"] >= 0) {
if (vaxResult.result[age]["DIARRHEA"] >= 0) {
vaxResult.result[age]["DIARRHEA"] +=
vaxResult.result[age]["DIRRHEA"];
} else {
vaxResult.result[age]["DIARRHEA"] =
vaxResult.result[age]["DIRRHEA"];
}
}
});
}
returnData[vaxResult.name] = {};
returnData[vaxResult.name] = vaxResult.result;
});
return returnData;
});
export const saveResultV1 = functions.https.onCall( export const saveResultV1 = functions.https.onCall(
async (data: SaveResultRequestData, context) => { async (data: SaveResultRequestData, context) => {
@@ -159,8 +227,7 @@ export const saveResultV1 = functions.https.onCall(
.get() .get()
.then((docSnap) => docSnap.data()); .then((docSnap) => docSnap.data());
// Update user result // Update user result
const cleanData : Record<string, any> = const cleanData: Record<string, any> = Object.assign({}, data);
Object.assign({}, data);
delete cleanData.gid; delete cleanData.gid;
await admin await admin
.firestore() .firestore()
@@ -175,73 +242,80 @@ export const saveResultV1 = functions.https.onCall(
await addResultToStats(data); await addResultToStats(data);
} }
} }
}); }
);
export const getPastResultV1 = export const getPastResultV1 = functions.https.onCall(
functions.https.onCall(async (data : {gid:string}, context) => { async (data: { gid: string }, context) => {
if (!!data && !!context) { if (!!data && !!context) {
const {gid} = data; const {gid} = data;
let hasToUpdateDb = false; let hasToUpdateDb = false;
const existingData : admin.firestore.DocumentData | undefined = const existingData: admin.firestore.DocumentData | undefined =
await admin await admin
.firestore() .firestore()
.collection("results") .collection("results")
.doc(serializeGaId(gid)) .doc(serializeGaId(gid))
.get() .get()
.then((doc) => doc.data()); .then((doc) => doc.data());
if (existingData) { if (existingData) {
const returnData : admin.firestore.DocumentData | undefined = const returnData:
Object.assign({}, existingData); | admin.firestore.DocumentData
if (existingData.name === VAX_NAMES.PB || | undefined = Object.assign({}, existingData);
existingData.name === VAX_NAMES.MD) { if (
Object.keys(VAX_DOSES).forEach((doseKey : string) => { existingData.name === VAX_NAMES.PB ||
const dose : string = VAX_DOSES[doseKey]; existingData.name === VAX_NAMES.MD
if (!existingData[dose]) { ) {
hasToUpdateDb = true; Object.keys(VAX_DOSES).forEach((doseKey: string) => {
const temp : Record<string, unknown> = {}; const dose: string = VAX_DOSES[doseKey];
Object.keys(existingData).forEach((symptom) => { if (!existingData[dose]) {
if (symptom !== "name" && hasToUpdateDb = true;
const temp: Record<string, unknown> = {};
Object.keys(existingData).forEach((symptom) => {
if (
symptom !== "name" &&
symptom !== "age" && symptom !== "age" &&
symptom !== "dose") { symptom !== "dose"
if (existingData.dose === dose) { ) {
temp[symptom] = existingData[symptom]; if (existingData.dose === dose) {
delete returnData[symptom]; temp[symptom] = existingData[symptom];
} else { delete returnData[symptom];
temp[symptom] = false; } else {
temp[symptom] = false;
}
} }
} });
}); returnData[dose] = temp;
returnData[dose] = temp; }
} if (returnData[dose]["NAUSEA / VOMITING"]) {
if (returnData[dose]["NAUSEA / VOMITING"]) { returnData[dose]["NAUSEA OR VOMITING"] =
returnData[dose]["NAUSEA / VOMITING"];
}
if (returnData[dose]["DIRRHEA"]) {
returnData[dose]["DIARRHEA"] = returnData["DIRRHEA"];
}
});
} else {
if (returnData["NAUSEA / VOMITING"]) {
returnData["NAUSEA OR VOMITING"] = returnData["NAUSEA OR VOMITING"] =
returnData["NAUSEA / VOMITING"]; returnData["NAUSEA / VOMITING"];
} }
if (returnData["DIRRHEA"]) { if (returnData["DIRRHEA"]) {
returnData["DIARRHEA"] = returnData["DIRRHEA"]; returnData["DIARRHEA"] = returnData["DIRRHEA"];
} }
});
} else {
if (returnData["NAUSEA / VOMITING"]) {
returnData["NAUSEA OR VOMITING"] =
returnData["NAUSEA / VOMITING"];
} }
if (returnData["DIRRHEA"]) { if (hasToUpdateDb) {
returnData["DIARRHEA"] = returnData["DIRRHEA"]; await admin
.firestore()
.collection("results")
.doc(serializeGaId(gid))
.set(returnData);
} }
return returnData;
} }
if (hasToUpdateDb) {
await admin
.firestore()
.collection("results")
.doc(serializeGaId(gid))
.set(returnData);
}
return returnData;
} }
return undefined;
} }
return undefined; );
});
/** /**
* FUNCTIONS FROM V1.0.0 TO V1.0.1 * FUNCTIONS FROM V1.0.0 TO V1.0.1
@@ -252,7 +326,7 @@ export const saveResult = functions.https.onCall(
const {gid} = data; const {gid} = data;
const docId: string = serializeGaId(gid); const docId: string = serializeGaId(gid);
const docData: Record<string, string | boolean> = const docData: Record<string, string | boolean> =
Object.assign({}, data); Object.assign({}, data);
delete docData.gid; delete docData.gid;
const existingData = await admin const existingData = await admin
.firestore() .firestore()
@@ -260,16 +334,19 @@ export const saveResult = functions.https.onCall(
.doc(docId) .doc(docId)
.get() .get()
.then((doc) => doc.data()); .then((doc) => doc.data());
if (existingData !== undefined && existingData !== docData && if (
existingData.name === docData.name) { existingData !== undefined &&
existingData !== docData &&
existingData.name === docData.name
) {
// DATA EXISTS FOR USER // DATA EXISTS FOR USER
// BUT DIFF DATA SENT FOR UPDATE // BUT DIFF DATA SENT FOR UPDATE
await admin.firestore().collection("results").doc(docId).set(docData); await admin.firestore().collection("results").doc(docId).set(docData);
delete docData.age; delete docData.age;
delete docData.name; delete docData.name;
delete docData.dose; delete docData.dose;
const updateDoc : Record<string, admin.firestore.FieldValue> = {}; const updateDoc: Record<string, admin.firestore.FieldValue> = {};
Object.keys(docData).forEach((symptom : string) => { Object.keys(docData).forEach((symptom: string) => {
// IF WAS TRUE, NOW FALSE = -1 // IF WAS TRUE, NOW FALSE = -1
// IF WAS TRUE, NOW TRUE = 0 // IF WAS TRUE, NOW TRUE = 0
// IF WAS FALSE, NOW TRUE = +1 // IF WAS FALSE, NOW TRUE = +1
@@ -277,17 +354,20 @@ export const saveResult = functions.https.onCall(
let incrementValue = 0; let incrementValue = 0;
if (existingData[symptom] === true && docData[symptom] === false) { if (existingData[symptom] === true && docData[symptom] === false) {
incrementValue = -1; incrementValue = -1;
} else if (existingData[symptom] === false && } else if (
docData[symptom] === true) { existingData[symptom] === false &&
docData[symptom] === true
) {
incrementValue = 1; incrementValue = 1;
} }
if (data.dose) { if (data.dose) {
updateDoc[ updateDoc[
`${data.dose}.${data.age}.${symptom.replace("/", "OR")}`] = `${data.dose}.${data.age}.${symptom.replace("/", "OR")}`
admin.firestore.FieldValue.increment(incrementValue); ] = admin.firestore.FieldValue.increment(incrementValue);
} else { } else {
updateDoc[`${data.age}.${symptom.replace("/", "OR")}`] = updateDoc[
admin.firestore.FieldValue.increment(incrementValue); `${data.age}.${symptom.replace("/", "OR")}`
] = admin.firestore.FieldValue.increment(incrementValue);
} }
}); });
admin admin
@@ -295,25 +375,27 @@ export const saveResult = functions.https.onCall(
.collection("stats") .collection("stats")
.doc(data.name) .doc(data.name)
.update(updateDoc); .update(updateDoc);
} else if (existingData !== undefined && existingData !== docData && } else if (
existingData.name !== docData.name) { existingData !== undefined &&
existingData !== docData &&
existingData.name !== docData.name
) {
// UNDO DATA FOR OLD VAX // UNDO DATA FOR OLD VAX
// DECREMENT VALUES FOR EACH SYMPTOM ON OLD VAX // DECREMENT VALUES FOR EACH SYMPTOM ON OLD VAX
const undoDoc : Record<string, admin.firestore.FieldValue> = { const undoDoc: Record<string, admin.firestore.FieldValue> = {
count: admin.firestore.FieldValue.increment(-1), count: admin.firestore.FieldValue.increment(-1),
}; };
Object.keys(existingData).forEach((symptom : string) => { Object.keys(existingData).forEach((symptom: string) => {
if (symptom !== "name" && symptom !== "dose" && if (symptom !== "name" && symptom !== "dose" && symptom !== "age") {
symptom !== "age" ) {
if (existingData[symptom] === true) { if (existingData[symptom] === true) {
if (existingData.dose) { if (existingData.dose) {
undoDoc[ undoDoc[`${existingData.dose}.${existingData.age}.${
`${existingData.dose}.${existingData.age}.${ symptom.replace("/", "OR")}`
symptom.replace("/", "OR")}`] = ] = admin.firestore.FieldValue.increment(-1);
admin.firestore.FieldValue.increment(-1);
} else { } else {
undoDoc[`${existingData.age}.${symptom.replace("/", "OR")}`] = undoDoc[
admin.firestore.FieldValue.increment(-1); `${existingData.age}.${symptom.replace("/", "OR")}`
] = admin.firestore.FieldValue.increment(-1);
} }
} }
} }
@@ -330,19 +412,20 @@ export const saveResult = functions.https.onCall(
delete docData.name; delete docData.name;
delete docData.dose; delete docData.dose;
// INCREMENT TOTAL RESPONSE COUNT // INCREMENT TOTAL RESPONSE COUNT
const updateDoc : Record<string, admin.firestore.FieldValue> = { const updateDoc: Record<string, admin.firestore.FieldValue> = {
count: admin.firestore.FieldValue.increment(1), count: admin.firestore.FieldValue.increment(1),
}; };
// INCREMENT VALUES FOR EACH SYMPTOM // INCREMENT VALUES FOR EACH SYMPTOM
Object.keys(docData).forEach((symptom : string) => { Object.keys(docData).forEach((symptom: string) => {
if (docData[symptom] === true) { if (docData[symptom] === true) {
if (data.dose) { if (data.dose) {
updateDoc[ updateDoc[
`${data.dose}.${data.age}.${symptom.replace("/", "OR")}`] = `${data.dose}.${data.age}.${symptom.replace("/", "OR")}`
admin.firestore.FieldValue.increment(1); ] = admin.firestore.FieldValue.increment(1);
} else { } else {
updateDoc[`${data.age}.${symptom.replace("/", "OR")}`] = updateDoc[
admin.firestore.FieldValue.increment(1); `${data.age}.${symptom.replace("/", "OR")}`
] = admin.firestore.FieldValue.increment(1);
} }
} }
}); });
@@ -359,19 +442,20 @@ export const saveResult = functions.https.onCall(
delete docData.name; delete docData.name;
delete docData.dose; delete docData.dose;
// INCREMENT TOTAL RESPONSE COUNT // INCREMENT TOTAL RESPONSE COUNT
const updateDoc : Record<string, admin.firestore.FieldValue> = { const updateDoc: Record<string, admin.firestore.FieldValue> = {
count: admin.firestore.FieldValue.increment(1), count: admin.firestore.FieldValue.increment(1),
}; };
// INCREMENT VALUES FOR EACH SYMPTOM // INCREMENT VALUES FOR EACH SYMPTOM
Object.keys(docData).forEach((symptom : string) => { Object.keys(docData).forEach((symptom: string) => {
if (docData[symptom] === true) { if (docData[symptom] === true) {
if (data.dose) { if (data.dose) {
updateDoc[ updateDoc[
`${data.dose}.${data.age}.${symptom.replace("/", "OR")}`] = `${data.dose}.${data.age}.${symptom.replace("/", "OR")}`
admin.firestore.FieldValue.increment(1); ] = admin.firestore.FieldValue.increment(1);
} else { } else {
updateDoc[`${data.age}.${symptom.replace("/", "OR")}`] = updateDoc[
admin.firestore.FieldValue.increment(1); `${data.age}.${symptom.replace("/", "OR")}`
] = admin.firestore.FieldValue.increment(1);
} }
} }
}); });
@@ -382,12 +466,13 @@ export const saveResult = functions.https.onCall(
.update(updateDoc); .update(updateDoc);
} }
} }
}); }
);
export const getPastResult = functions.https.onCall(async (data, context) => { export const getPastResult = functions.https.onCall(async (data, context) => {
if (!!data && !!context) { if (!!data && !!context) {
const {gid} = data; const {gid} = data;
const existingData : admin.firestore.DocumentData | undefined = await admin const existingData: admin.firestore.DocumentData | undefined = await admin
.firestore() .firestore()
.collection("results") .collection("results")
.doc(serializeGaId(gid)) .doc(serializeGaId(gid))
@@ -395,8 +480,7 @@ export const getPastResult = functions.https.onCall(async (data, context) => {
.then((doc) => doc.data()); .then((doc) => doc.data());
if (existingData) { if (existingData) {
if (existingData["NAUSEA / VOMITING"]) { if (existingData["NAUSEA / VOMITING"]) {
existingData["NAUSEA OR VOMITING"] = existingData["NAUSEA OR VOMITING"] = existingData["NAUSEA / VOMITING"];
existingData["NAUSEA / VOMITING"];
} }
return existingData; return existingData;
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "vaxandme", "name": "vaxandme",
"version": "0.1.0", "version": "1.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"firebase": "^8.4.1", "firebase": "^8.4.1",
+16 -4
View File
@@ -392,22 +392,23 @@ footer .by {
animation: 2s ease-out 0s infinite loading; animation: 2s ease-out 0s infinite loading;
} }
.result-header-contianer { .result-header-container {
display: flex; display: flex;
align-items: flex-start; align-items: center;
margin-top: 5rem; margin-top: 5rem;
flex-direction: column; flex-direction: column;
margin-bottom: 2rem; margin-bottom: 2rem;
} }
.result-header-contianer .symptom-container { .result-header-container .symptom-container {
margin-top: 0; margin: 0;
flex: 1; flex: 1;
width: 100%; width: 100%;
} }
.result-header-select { .result-header-select {
width: 100%; width: 100%;
margin-top: 2rem;
} }
.result-header-select .overline { .result-header-select .overline {
@@ -415,6 +416,17 @@ footer .by {
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
} }
.result-header-select .overline.errored {
margin-bottom: 0;
margin-top: 0.75rem;
color: #c43d34;
}
.result-header-select .overline.success {
margin-bottom: 0;
margin-top: 0.75rem;
}
.result-header-select select { .result-header-select select {
background-color: var(--color-tertiary); background-color: var(--color-tertiary);
border-radius: 1rem; border-radius: 1rem;
+3 -3
View File
@@ -109,16 +109,16 @@ footer {
} }
.result-header-contianer { .result-header-container {
margin-bottom: 0;
flex-direction: row; flex-direction: row;
} }
.result-header-contianer .symptom-container { .result-header-container .symptom-container {
width: fit-content; width: fit-content;
} }
.result-header-select { .result-header-select {
margin-top: 0rem;
width: fit-content; width: fit-content;
padding-left: 3rem; padding-left: 3rem;
border-left: 1px solid var(--color-secondary); border-left: 1px solid var(--color-secondary);
+210 -84
View File
@@ -14,8 +14,8 @@ const FIREBASE_CONFIG = {
}; };
firebase.initializeApp(FIREBASE_CONFIG); firebase.initializeApp(FIREBASE_CONFIG);
if (window.location.host.startsWith("localhost")) { if (window.location.host !== "www.vaxand.me") {
firebase.functions().useEmulator("localhost", 5001); firebase.functions().useEmulator(window.location.host.split(":")[0], 5001);
} }
const fb_functions = firebase.functions(); const fb_functions = firebase.functions();
const saveResult = fb_functions.httpsCallable("saveResultV1"); const saveResult = fb_functions.httpsCallable("saveResultV1");
@@ -280,6 +280,7 @@ class App extends React.Component {
stats: {}, stats: {},
loading: true, loading: true,
dataSource: DATA_CHOICES.FDA, dataSource: DATA_CHOICES.FDA,
loadingMessage: "Gettings stats...",
}; };
} }
componentDidMount() { componentDidMount() {
@@ -315,58 +316,78 @@ class App extends React.Component {
} }
} }
if (!window.localStorage.getItem("hasExistingAnswer")) { getStats()
this.setState({ loading: false }); .then((v) => {
} // GET & SET STATS
this.setState({
if (window.gtag) { stats: v.data,
window.gtag("get", "G-7VZCY223NG", "client_id", (client_id) => { loadingMessage: "Checking for existing answers...",
if (!!client_id) { dataSource: DATA_CHOICES.VNM_FDA,
this.setState({ gid: client_id }); });
getPastResult({ gid: client_id }) // HAS GID?
.then((v) => { if (window.gtag) {
const { data } = v; // GET GID
if (!!data) { window.gtag("get", "G-7VZCY223NG", "client_id", (client_id) => {
const { name, age, dose } = data; // GOT GID?
this.setState({ if (!!client_id) {
name, // SAVE GID
age, this.setState({ gid: client_id });
dose, // GET PAST ANSWER FOR GID
showInputPopup: true, getPastResult({ gid: client_id })
fetchedResult: data, .then((v) => {
answers: data, const { data } = v;
loading: false, if (!!data) {
const { name, age, dose } = data;
this.setState({
name,
age,
dose,
showInputPopup: true,
fetchedResult: data,
answers: data,
loading: false,
});
const inputPopupSubmit = document.querySelector(
".input-popup-submit"
);
if (inputPopupSubmit) {
inputPopupSubmit.classList.add("submitted");
inputPopupSubmit.innerHTML = "UPDATE ANSWER";
}
window.localStorage.setItem("hasExistingAnswer", true);
} else {
this.setState({ loading: false });
}
})
.catch((err) => {
console.error("ERROR Getting Past Answer", err);
this.setState({ loading: false });
}); });
const inputPopupSubmit = document.querySelector( } else {
".input-popup-submit"
);
if (inputPopupSubmit) {
inputPopupSubmit.classList.add("submitted");
inputPopupSubmit.innerHTML = "UPDATE ANSWER";
}
window.localStorage.setItem("hasExistingAnswer", true);
} else {
this.setState({ loading: false });
}
})
.catch((err) => {
console.error("ERROR Getting Past Answer", err);
this.setState({ loading: false }); this.setState({ loading: false });
}); }
});
} else {
this.setState({ loading: false });
} }
})
.catch((err) => {
console.error("ERROR GETTING STATS", err);
this.setState({ loading: false });
}); });
} else {
this.setState({ loading: false });
}
getStats().then((v) => this.setState({ stats: v.data }));
} }
shouldShowDoses = () => { shouldShowDoses = () => {
const { name } = this.state; const { name, loading } = this.state;
if (loading) {
return false;
}
return name && (name === VAX_NAMES.PB || name === VAX_NAMES.MD); return name && (name === VAX_NAMES.PB || name === VAX_NAMES.MD);
}; };
shouldShowAgeGroup = () => { shouldShowAgeGroup = () => {
const { name, dose } = this.state; const { name, dose, loading } = this.state;
if (loading) {
return false;
}
switch (name) { switch (name) {
case VAX_NAMES.JJ: case VAX_NAMES.JJ:
return true; return true;
@@ -378,7 +399,10 @@ class App extends React.Component {
} }
}; };
shouldShowResults = () => { shouldShowResults = () => {
const { name, dose, age } = this.state; const { name, dose, age, loading } = this.state;
if (loading) {
return false;
}
switch (name) { switch (name) {
case VAX_NAMES.JJ: case VAX_NAMES.JJ:
return !!age; return !!age;
@@ -390,7 +414,10 @@ class App extends React.Component {
} }
}; };
getResults = () => { getResults = () => {
const { name, dose, age } = this.state; const { name, dose, age, loading } = this.state;
if (loading) {
return null;
}
switch (name) { switch (name) {
case VAX_NAMES.JJ: case VAX_NAMES.JJ:
return VAX_RESULTS[name][age]; return VAX_RESULTS[name][age];
@@ -558,6 +585,96 @@ class App extends React.Component {
</div> </div>
); );
}; };
getStatForSymptom = (symptom) => {
const { dataSource, name, dose, age, stats } = this.state;
switch (dataSource) {
case DATA_CHOICES.FDA:
if (!!dose) {
return Math.ceil(
(VAX_RESULTS[name][dose][age][symptom] /
VAX_COUNTS[name][dose][age]) *
10
);
} else {
return Math.ceil(
(VAX_RESULTS[name][age][symptom] / VAX_COUNTS[name][age]) * 10
);
}
case DATA_CHOICES.VNM:
if (Object.keys(stats).length > 0) {
if (!!dose) {
return Math.ceil(
(stats[name][dose][age][symptom] / stats[name][dose][age].count) *
10
);
} else {
return Math.ceil(
(stats[name][age][symptom] / stats[name][age].count) * 10
);
}
} else {
return 0;
}
case DATA_CHOICES.VNM_FDA:
let symptomCount = 0;
let totalCount = 0;
if (Object.keys(stats).length > 0) {
if (!!dose) {
symptomCount =
VAX_RESULTS[name][dose][age][symptom] +
stats[name][dose][age][symptom];
totalCount =
VAX_COUNTS[name][dose][age] + stats[name][dose][age].count;
} else {
symptomCount =
VAX_RESULTS[name][age][symptom] + stats[name][age][symptom];
totalCount = VAX_COUNTS[name][age] + stats[name][age].count;
}
} else {
symptomCount = VAX_RESULTS[name][dose][age][symptom];
totalCount = VAX_COUNTS[name][dose][age];
}
return Math.ceil((symptomCount / totalCount) * 10);
default:
return 1;
}
};
getSampleSize = () => {
const { dataSource, name, dose, age, stats } = this.state;
switch (dataSource) {
case DATA_CHOICES.FDA:
if (!!dose) {
return VAX_COUNTS[name][dose][age];
} else {
return VAX_COUNTS[name][age];
}
case DATA_CHOICES.VNM:
if (Object.keys(stats).length > 0) {
if (!!dose) {
return stats[name][dose][age].count;
} else {
return stats[name][age].count;
}
} else {
return 0;
}
case DATA_CHOICES.VNM_FDA:
let totalCount = 0;
if (Object.keys(stats).length > 0) {
if (!!dose) {
totalCount =
VAX_COUNTS[name][dose][age] + stats[name][dose][age].count;
} else {
totalCount = VAX_COUNTS[name][age] + stats[name][age].count;
}
} else {
totalCount = VAX_COUNTS[name][dose][age];
}
return totalCount;
default:
return 0;
}
};
render() { render() {
const { const {
name, name,
@@ -567,6 +684,8 @@ class App extends React.Component {
loading, loading,
fetchedResult, fetchedResult,
dataSource, dataSource,
loadingMessage,
stats,
} = this.state; } = this.state;
let result = {}; let result = {};
let resultAtSite = {}; let resultAtSite = {};
@@ -598,7 +717,7 @@ class App extends React.Component {
{loading ? ( {loading ? (
<div className="loading-container"> <div className="loading-container">
<p className="question" data-font="sans"> <p className="question" data-font="sans">
Checking for existing answers... {loadingMessage}
</p> </p>
<div className="loading-bar-container"> <div className="loading-bar-container">
<div className="loading-bar"></div> <div className="loading-bar"></div>
@@ -690,7 +809,7 @@ class App extends React.Component {
) : null} ) : null}
{this.shouldShowResults() ? ( {this.shouldShowResults() ? (
<div className="result-header-contianer"> <div className="result-header-container">
<div className="symptom-container subheader"> <div className="symptom-container subheader">
<h3 data-font="sans-bold">Here's what to expect.</h3> <h3 data-font="sans-bold">Here's what to expect.</h3>
<h6 <h6
@@ -711,6 +830,7 @@ class App extends React.Component {
Use data from: Use data from:
</h6> </h6>
<select <select
disabled={Object.keys(stats).length < 1}
onChange={(e) => onChange={(e) =>
this.setState({ dataSource: e.target.value }) this.setState({ dataSource: e.target.value })
} }
@@ -720,6 +840,18 @@ class App extends React.Component {
<option key={dataKey}>{DATA_CHOICES[dataKey]}</option> <option key={dataKey}>{DATA_CHOICES[dataKey]}</option>
))} ))}
</select> </select>
<h6 className="overline success" data-font="sans-italic">
Based on{" "}
{this.getSampleSize()
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, ",")}{" "}
individual experiences
</h6>
{Object.keys(stats).length < 1 ? (
<h6 className="overline errored" data-font="sans-italic">
Failed to get stats from Vax&amp;Me
</h6>
) : null}
</div> </div>
</div> </div>
) : null} ) : null}
@@ -757,12 +889,7 @@ class App extends React.Component {
{symptom} {symptom}
</p> </p>
<p data-font="sans-bold" className="symptom-chance"> <p data-font="sans-bold" className="symptom-chance">
{Math.ceil( {this.getStatForSymptom(symptom)}/10
(!!dose
? resultAtSite[symptom] / VAX_COUNTS[name][dose][age]
: resultAtSite[symptom] / VAX_COUNTS[name][age]) * 10
)}
/10
</p> </p>
</div> </div>
))} ))}
@@ -773,12 +900,7 @@ class App extends React.Component {
{symptom} {symptom}
</p> </p>
<p data-font="sans-bold" className="symptom-chance"> <p data-font="sans-bold" className="symptom-chance">
{Math.ceil( {this.getStatForSymptom(symptom)}/10
(!!dose
? result[symptom] / VAX_COUNTS[name][dose][age]
: result[symptom] / VAX_COUNTS[name][age]) * 10
)}
/10
</p> </p>
</div> </div>
))} ))}
@@ -809,32 +931,36 @@ class App extends React.Component {
&#9660; &#9660;
</button> </button>
</div> </div>
<div className="input-popup-body"> {showInputPopup ? (
<p className="question" data-font="sans"> <div className="input-popup-body">
I am in age group <strong>{age}</strong> and got the
<strong> {name}</strong> vaccine. I experienced:
</p>
{this.shouldShowDoses()
? Object.keys(VAX_DOSES).map((dose) =>
this.getAnswerOptions(VAX_DOSES[dose])
)
: this.getAnswerOptions()}
<div className="question-container">
<p className="question" data-font="sans"> <p className="question" data-font="sans">
You can comeback and update your experience at any time! I am in age group <strong>{age}</strong> and got the
</p> <strong> {name}</strong> vaccine. I experienced:
<p className="question" data-font="sans-italic">
(Just use the same device)
</p> </p>
{this.shouldShowDoses()
? Object.keys(VAX_DOSES).map((dose) =>
this.getAnswerOptions(VAX_DOSES[dose])
)
: this.getAnswerOptions()}
<div className="question-container">
<p className="question" data-font="sans">
You can comeback and update your experience at any time!
</p>
<p className="question" data-font="sans-italic">
(Just use the same device)
</p>
</div>
</div> </div>
</div> ) : null}
<div {showInputPopup ? (
onClick={this.submitAnswer} <div
className="input-popup-submit" onClick={this.submitAnswer}
data-font="sans-bold" className="input-popup-submit"
> data-font="sans-bold"
SUBMIT >
</div> SUBMIT
</div>
) : null}
</div> </div>
) : null} ) : null}
</div> </div>