chore: adds metadata and hidden fields to csv, excel exports (#1806)

This commit is contained in:
Dhruwang Jariwala
2023-12-21 19:12:35 +05:30
committed by GitHub
parent 8857c971d6
commit f9861cf772
2 changed files with 45 additions and 50 deletions
@@ -5,11 +5,7 @@ import {
useResponseFilter,
} from "@/app/(app)/environments/[environmentId]/components/ResponseFilterContext";
import { fetchFile } from "@/app/lib/fetchFile";
import {
generateQuestionAndFilterOptions,
generateQuestionsAndAttributes,
getTodayDate,
} from "@/app/lib/surveys/surveys";
import { generateQuestionAndFilterOptions, getTodayDate } from "@/app/lib/surveys/surveys";
import { createId } from "@paralleldrive/cuid2";
import { differenceInDays, format, subDays } from "date-fns";
import { ChevronDown, ChevronUp, DownloadIcon } from "lucide-react";
@@ -91,7 +87,7 @@ const CustomFilter = ({ environmentTags, responses, survey, totalResponses }: Cu
const datePickerRef = useRef<HTMLDivElement>(null);
const getMatchQandA = (responses: any, survey: any) => {
const getMatchQandA = (responses: TResponse[], survey: TSurvey) => {
if (survey && responses) {
// Create a mapping of question IDs to their headlines
const questionIdToHeadline = {};
@@ -145,20 +141,58 @@ const CustomFilter = ({ environmentTags, responses, survey, totalResponses }: Cu
return "my_survey_responses";
}, [survey]);
function extracMetadataKeys(obj, parentKey = "") {
let keys: string[] = [];
for (let key in obj) {
if (typeof obj[key] === "object" && obj[key] !== null) {
keys = keys.concat(extracMetadataKeys(obj[key], parentKey + key + " - "));
} else {
keys.push(parentKey + key);
}
}
return keys;
}
const downloadResponses = useCallback(
async (filter: FilterDownload, filetype: "csv" | "xlsx") => {
const downloadResponse = filter === FilterDownload.ALL ? totalResponses : responses;
const { attributeMap, questionNames } = generateQuestionsAndAttributes(survey, downloadResponse);
const questionNames = survey.questions?.map((question) => question.headline);
const hiddenFieldIds = survey.hiddenFields.fieldIds;
const hiddenFieldResponse = {};
let metaDataFields = extracMetadataKeys(downloadResponse[0].meta);
const userAttributes = ["Init Attribute 1", "Init Attribute 2"];
const matchQandA = getMatchQandA(downloadResponse, survey);
const jsonData = matchQandA.map((response) => {
const fileResponse = {
const basicInfo = {
"Response ID": response.id,
Timestamp: response.createdAt,
Finished: response.finished,
"Survey ID": response.surveyId,
"Formbricks User ID": response.person?.id ?? "",
};
const metaDataKeys = extracMetadataKeys(response.meta);
let metaData = {};
metaDataKeys.forEach((key) => {
if (!metaDataFields.includes(key)) metaDataFields.push(key);
if (response.meta) {
if (key.includes("-")) {
const nestedKeyArray = key.split("-");
metaData[key] = response.meta[nestedKeyArray[0].trim()][nestedKeyArray[1].trim()] ?? "";
} else {
metaData[key] = response.meta[key] ?? "";
}
}
});
const personAttributes = response.personAttributes;
if (hiddenFieldIds && hiddenFieldIds.length > 0) {
hiddenFieldIds.forEach((hiddenFieldId) => {
hiddenFieldResponse[hiddenFieldId] = response.data[hiddenFieldId] ?? "";
});
}
const fileResponse = { ...basicInfo, ...metaData, ...personAttributes, ...hiddenFieldResponse };
// Map each question name to its corresponding answer
questionNames.forEach((questionName: string) => {
const matchingQuestion = response.responses.find((question) => question.question === questionName);
@@ -177,18 +211,6 @@ const CustomFilter = ({ environmentTags, responses, survey, totalResponses }: Cu
return fileResponse;
});
// Add attribute columns to the file
Object.keys(attributeMap).forEach((attributeName) => {
const attributeValues = attributeMap[attributeName];
Object.keys(attributeValues).forEach((personId) => {
const value = attributeValues[personId];
const matchingResponse = jsonData.find((response) => response["Formbricks User ID"] === personId);
if (matchingResponse) {
matchingResponse[attributeName] = value;
}
});
});
// Fields which will be used as column headers in the file
const fields = [
"Response ID",
@@ -196,8 +218,10 @@ const CustomFilter = ({ environmentTags, responses, survey, totalResponses }: Cu
"Finished",
"Survey ID",
"Formbricks User ID",
...Object.keys(attributeMap),
...metaDataFields,
...questionNames,
...(hiddenFieldIds ?? []),
...(survey.type === "web" ? userAttributes : []),
];
let response;
-29
View File
@@ -14,35 +14,6 @@ import { TSurveyQuestionType } from "@formbricks/types/surveys";
import { TSurvey } from "@formbricks/types/surveys";
import { TTag } from "@formbricks/types/tags";
export const generateQuestionsAndAttributes = (survey: TSurvey, responses: TResponse[]) => {
let questionNames: string[] = [];
if (survey?.questions) {
questionNames = survey.questions.map((question) => question.headline);
}
const attributeMap: Record<string, Record<string, string | number>> = {};
if (responses) {
responses.forEach((response) => {
const { person } = response;
if (person !== null) {
const { id, attributes } = person;
Object.keys(attributes).forEach((attributeName) => {
if (!attributeMap.hasOwnProperty(attributeName)) {
attributeMap[attributeName] = {};
}
attributeMap[attributeName][id] = attributes[attributeName];
});
}
});
}
return {
questionNames,
attributeMap,
};
};
const conditionOptions = {
openText: ["is"],
multipleChoiceSingle: ["Includes either"],