You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
149 lines
3.5 KiB
149 lines
3.5 KiB
import type {
|
|
City,
|
|
Company,
|
|
Country,
|
|
QuestionsQuestion,
|
|
QuestionsQuestionTag,
|
|
QuestionsQuestionVote,
|
|
State,
|
|
} from '@prisma/client';
|
|
import { Vote } from '@prisma/client';
|
|
|
|
import type {
|
|
AggregatedQuestionEncounter,
|
|
CountryInfo,
|
|
Question,
|
|
} from '~/types/questions';
|
|
|
|
type QuestionTagEntry = { tag: QuestionsQuestionTag };
|
|
|
|
type AggregatableEncounters = Array<{
|
|
city: City | null;
|
|
company: Company | null;
|
|
country: Country | null;
|
|
role: string;
|
|
seenAt: Date;
|
|
state: State | null;
|
|
}>;
|
|
|
|
type QuestionWithAggregatableData = QuestionsQuestion & {
|
|
_count: {
|
|
answers: number;
|
|
comments: number;
|
|
};
|
|
encounters: AggregatableEncounters;
|
|
questionTagEntries: Array<QuestionTagEntry>;
|
|
user: {
|
|
name: string | null;
|
|
} | null;
|
|
votes: Array<QuestionsQuestionVote>;
|
|
};
|
|
|
|
export function createQuestionWithAggregateData(
|
|
data: QuestionWithAggregatableData,
|
|
): Question {
|
|
const votes: number = data.votes.reduce(
|
|
(previousValue: number, currentValue) => {
|
|
let result: number = previousValue;
|
|
|
|
switch (currentValue.vote) {
|
|
case Vote.UPVOTE:
|
|
result += 1;
|
|
break;
|
|
case Vote.DOWNVOTE:
|
|
result -= 1;
|
|
break;
|
|
}
|
|
return result;
|
|
},
|
|
0,
|
|
);
|
|
|
|
const question: Question = {
|
|
aggregatedQuestionEncounters: createAggregatedQuestionEncounter(
|
|
data.encounters,
|
|
),
|
|
content: data.content,
|
|
id: data.id,
|
|
numAnswers: data._count.answers,
|
|
numComments: data._count.comments,
|
|
numVotes: votes,
|
|
receivedCount: data.encounters.length,
|
|
seenAt: data.encounters[0].seenAt,
|
|
tags: data.questionTagEntries.map(({ tag }) => tag),
|
|
type: data.questionType,
|
|
updatedAt: data.updatedAt,
|
|
user: data.user?.name ?? '',
|
|
};
|
|
return question;
|
|
}
|
|
|
|
export function createAggregatedQuestionEncounter(
|
|
encounters: AggregatableEncounters,
|
|
): AggregatedQuestionEncounter {
|
|
const countryCounts: Record<string, CountryInfo> = {};
|
|
const companyCounts: Record<string, number> = {};
|
|
const roleCounts: Record<string, number> = {};
|
|
|
|
let latestSeenAt = encounters[0].seenAt;
|
|
|
|
for (const encounter of encounters) {
|
|
latestSeenAt =
|
|
latestSeenAt < encounter.seenAt ? encounter.seenAt : latestSeenAt;
|
|
|
|
if (encounter.company !== null) {
|
|
if (!(encounter.company.name in companyCounts)) {
|
|
companyCounts[encounter.company!.name] = 0;
|
|
}
|
|
companyCounts[encounter.company!.name] += 1;
|
|
}
|
|
|
|
if (encounter.country !== null) {
|
|
if (!(encounter.country.name in countryCounts)) {
|
|
countryCounts[encounter.country.name] = {
|
|
stateInfos: {},
|
|
total: 0,
|
|
};
|
|
}
|
|
const countryInfo = countryCounts[encounter.country.name];
|
|
|
|
countryInfo.total += 1;
|
|
|
|
const countryStateInfo = countryInfo.stateInfos;
|
|
|
|
if (encounter.state !== null) {
|
|
if (!(encounter.state.name in countryStateInfo)) {
|
|
countryStateInfo[encounter.state.name] = {
|
|
cityCounts: {},
|
|
total: 0,
|
|
};
|
|
}
|
|
const stateInfo = countryStateInfo[encounter.state.name];
|
|
|
|
stateInfo.total += 1;
|
|
|
|
const { cityCounts } = stateInfo;
|
|
|
|
if (encounter.city !== null) {
|
|
if (!(encounter.city.name in cityCounts)) {
|
|
cityCounts[encounter.city.name] = 0;
|
|
}
|
|
cityCounts[encounter.city.name] += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(encounter.role in roleCounts)) {
|
|
roleCounts[encounter.role] = 0;
|
|
}
|
|
roleCounts[encounter.role] += 1;
|
|
}
|
|
|
|
return {
|
|
companyCounts,
|
|
countryCounts,
|
|
latestSeenAt,
|
|
roleCounts,
|
|
};
|
|
}
|