[offers][chore] Allow analysis to analyse all companies in the backend

pull/471/head
Bryann Yeap Kok Keong 3 years ago committed by Ai Ling
parent 1ea1afc8a2
commit d0b5867dd7

@ -362,9 +362,8 @@ model OffersOffer {
offersFullTime OffersFullTime? @relation(fields: [offersFullTimeId], references: [id], onDelete: Cascade)
offersFullTimeId String? @unique
OffersAnalysis OffersAnalysis? @relation("HighestOverallOffer")
OffersAnalysisTopOverallOffers OffersAnalysis[] @relation("TopOverallOffers")
OffersAnalysisTopCompanyOffers OffersAnalysis[] @relation("TopCompanyOffers")
offersAnalysis OffersAnalysis? @relation("HighestOverallOffer")
offersAnalysisUnit OffersAnalysisUnit[]
}
model OffersIntern {
@ -405,14 +404,21 @@ model OffersAnalysis {
offerId String @unique
// OVERALL
overallPercentile Float
overallAnalysis OffersAnalysisUnit @relation("OverallAnalysis", fields: [overallAnalysisUnitId], references: [id])
overallAnalysisUnitId String
companyAnalysis OffersAnalysisUnit[] @relation("CompanyAnalysis")
}
model OffersAnalysisUnit {
id String @id @default(cuid())
percentile Float
noOfSimilarOffers Int
topOverallOffers OffersOffer[] @relation("TopOverallOffers")
topSimilarOffers OffersOffer[]
// Company
companyPercentile Float
noOfSimilarCompanyOffers Int
topCompanyOffers OffersOffer[] @relation("TopCompanyOffers")
offersAnalysisOverall OffersAnalysis[] @relation("OverallAnalysis")
offersAnalysisCompany OffersAnalysis[] @relation("CompanyAnalysis")
}
// End of Offers project models.

@ -1,6 +1,7 @@
import type {
Company,
OffersAnalysis,
OffersAnalysisUnit,
OffersBackground,
OffersCurrency,
OffersEducation,
@ -18,9 +19,9 @@ import { TRPCError } from '@trpc/server';
import type {
AddToProfileResponse,
Analysis,
AnalysisHighestOffer,
AnalysisOffer,
AnalysisUnit,
Background,
CreateOfferProfileResponse,
DashboardOffer,
@ -111,32 +112,32 @@ const analysisOfferDtoMapper = (
return analysisOfferDto;
};
const analysisDtoMapper = (
noOfOffers: number,
percentile: number,
topPercentileOffers: Array<
OffersOffer & {
company: Company;
offersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
offersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null;
profile: OffersProfile & {
background:
| (OffersBackground & {
experiences: Array<
OffersExperience & { company: Company | null }
>;
})
const analysisUnitDtoMapper = (
analysisUnit: OffersAnalysisUnit & {
topSimilarOffers: Array<
OffersOffer & {
company: Company;
offersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
};
}
>,
offersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null;
profile: OffersProfile & {
background:
| (OffersBackground & {
experiences: Array<
OffersExperience & { company: Company | null }
>;
})
| null;
};
}
>;
},
) => {
const analysisDto: Analysis = {
noOfOffers,
percentile,
topPercentileOffers: topPercentileOffers.map((offer) =>
const analysisDto: AnalysisUnit = {
noOfOffers: analysisUnit.noOfSimilarOffers,
percentile: analysisUnit.percentile,
topPercentileOffers: analysisUnit.topSimilarOffers.map((offer) =>
analysisOfferDtoMapper(offer),
),
};
@ -166,6 +167,52 @@ const analysisHighestOfferDtoMapper = (
export const profileAnalysisDtoMapper = (
analysis:
| (OffersAnalysis & {
companyAnalysis: Array<
OffersAnalysisUnit & {
topSimilarOffers: Array<
OffersOffer & {
company: Company;
offersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
offersIntern:
| (OffersIntern & { monthlySalary: OffersCurrency })
| null;
profile: OffersProfile & {
background:
| (OffersBackground & {
experiences: Array<
OffersExperience & { company: Company | null }
>;
})
| null;
};
}
>;
}
>;
overallAnalysis: OffersAnalysisUnit & {
topSimilarOffers: Array<
OffersOffer & {
company: Company;
offersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
offersIntern:
| (OffersIntern & { monthlySalary: OffersCurrency })
| null;
profile: OffersProfile & {
background:
| (OffersBackground & {
experiences: Array<
OffersExperience & { company: Company | null }
>;
})
| null;
};
}
>;
};
overallHighestOffer: OffersOffer & {
company: Company;
offersFullTime:
@ -176,46 +223,6 @@ export const profileAnalysisDtoMapper = (
| null;
profile: OffersProfile & { background: OffersBackground | null };
};
topCompanyOffers: Array<
OffersOffer & {
company: Company;
offersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
offersIntern:
| (OffersIntern & { monthlySalary: OffersCurrency })
| null;
profile: OffersProfile & {
background:
| (OffersBackground & {
experiences: Array<
OffersExperience & { company: Company | null }
>;
})
| null;
};
}
>;
topOverallOffers: Array<
OffersOffer & {
company: Company;
offersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
offersIntern:
| (OffersIntern & { monthlySalary: OffersCurrency })
| null;
profile: OffersProfile & {
background:
| (OffersBackground & {
experiences: Array<
OffersExperience & { company: Company | null }
>;
})
| null;
};
}
>;
})
| null,
) => {
@ -224,19 +231,11 @@ export const profileAnalysisDtoMapper = (
}
const profileAnalysisDto: ProfileAnalysis = {
companyAnalysis: [
analysisDtoMapper(
analysis.noOfSimilarCompanyOffers,
analysis.companyPercentile,
analysis.topCompanyOffers,
),
],
id: analysis.id,
overallAnalysis: analysisDtoMapper(
analysis.noOfSimilarOffers,
analysis.overallPercentile,
analysis.topOverallOffers,
companyAnalysis: analysis.companyAnalysis.map((analysisUnit) =>
analysisUnitDtoMapper(analysisUnit),
),
id: analysis.id,
overallAnalysis: analysisUnitDtoMapper(analysis.overallAnalysis),
overallHighestOffer: analysisHighestOfferDtoMapper(
analysis.overallHighestOffer,
),
@ -442,6 +441,52 @@ export const profileDtoMapper = (
profile: OffersProfile & {
analysis:
| (OffersAnalysis & {
companyAnalysis: Array<
OffersAnalysisUnit & {
topSimilarOffers: Array<
OffersOffer & {
company: Company;
offersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
offersIntern:
| (OffersIntern & { monthlySalary: OffersCurrency })
| null;
profile: OffersProfile & {
background:
| (OffersBackground & {
experiences: Array<
OffersExperience & { company: Company | null }
>;
})
| null;
};
}
>;
}
>;
overallAnalysis: OffersAnalysisUnit & {
topSimilarOffers: Array<
OffersOffer & {
company: Company;
offersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
offersIntern:
| (OffersIntern & { monthlySalary: OffersCurrency })
| null;
profile: OffersProfile & {
background:
| (OffersBackground & {
experiences: Array<
OffersExperience & { company: Company | null }
>;
})
| null;
};
}
>;
};
overallHighestOffer: OffersOffer & {
company: Company;
offersFullTime:
@ -452,46 +497,6 @@ export const profileDtoMapper = (
| null;
profile: OffersProfile & { background: OffersBackground | null };
};
topCompanyOffers: Array<
OffersOffer & {
company: Company;
offersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
offersIntern:
| (OffersIntern & { monthlySalary: OffersCurrency })
| null;
profile: OffersProfile & {
background:
| (OffersBackground & {
experiences: Array<
OffersExperience & { company: Company | null }
>;
})
| null;
};
}
>;
topOverallOffers: Array<
OffersOffer & {
company: Company;
offersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
offersIntern:
| (OffersIntern & { monthlySalary: OffersCurrency })
| null;
profile: OffersProfile & {
background:
| (OffersBackground & {
experiences: Array<
OffersExperience & { company: Company | null }
>;
})
| null;
};
}
>;
})
| null;
background:

@ -14,6 +14,15 @@ import { profileAnalysisDtoMapper } from '~/mappers/offers-mappers';
import { createRouter } from '../context';
type Offer = OffersOffer & {
company: Company;
offersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
offersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null;
profile: OffersProfile & { background: OffersBackground | null };
};
const searchOfferPercentile = (
offer: OffersOffer & {
company: Company;
@ -58,46 +67,62 @@ export const offersAnalysisRouter = createRouter()
async resolve({ ctx, input }) {
const analysis = await ctx.prisma.offersAnalysis.findFirst({
include: {
overallHighestOffer: {
companyAnalysis: {
include: {
company: true,
offersFullTime: {
include: {
totalCompensation: true,
},
},
offersIntern: {
topSimilarOffers: {
include: {
monthlySalary: true,
},
},
profile: {
include: {
background: true,
company: true,
offersFullTime: {
include: {
totalCompensation: true,
},
},
offersIntern: {
include: {
monthlySalary: true,
},
},
profile: {
include: {
background: {
include: {
experiences: {
include: {
company: true,
},
},
},
},
},
},
},
},
},
},
topCompanyOffers: {
overallAnalysis: {
include: {
company: true,
offersFullTime: {
include: {
totalCompensation: true,
},
},
offersIntern: {
topSimilarOffers: {
include: {
monthlySalary: true,
},
},
profile: {
include: {
background: {
company: true,
offersFullTime: {
include: {
totalCompensation: true,
},
},
offersIntern: {
include: {
experiences: {
monthlySalary: true,
},
},
profile: {
include: {
background: {
include: {
company: true,
experiences: {
include: {
company: true,
},
},
},
},
},
@ -106,7 +131,7 @@ export const offersAnalysisRouter = createRouter()
},
},
},
topOverallOffers: {
overallHighestOffer: {
include: {
company: true,
offersFullTime: {
@ -121,15 +146,7 @@ export const offersAnalysisRouter = createRouter()
},
profile: {
include: {
background: {
include: {
experiences: {
include: {
company: true,
},
},
},
},
background: true,
},
},
},
@ -310,11 +327,56 @@ export const offersAnalysisRouter = createRouter()
},
});
let similarCompanyOffers = similarOffers.filter(
(offer) => offer.companyId === overallHighestOffer.companyId,
// COMPANY ANALYSIS
const companyMap = new Map<string, Offer>();
offers.forEach((offer) => {
if (companyMap.get(offer.companyId) == null) {
companyMap.set(offer.companyId, offer);
}
});
const companyAnalysis = Array.from(companyMap.values()).map(
(companyOffer) => {
// TODO: Refactor calculating analysis into a function
let similarCompanyOffers = similarOffers.filter(
(offer) => offer.companyId === overallHighestOffer.companyId,
);
const companyIndex = searchOfferPercentile(
overallHighestOffer,
similarCompanyOffers,
);
const companyPercentile =
similarCompanyOffers.length <= 1
? 100
: 100 - (100 * companyIndex) / (similarCompanyOffers.length - 1);
// Get top offers (excluding user's offer)
similarCompanyOffers = similarCompanyOffers.filter(
(offer) => offer.id !== companyOffer.id,
);
const noOfSimilarCompanyOffers = similarCompanyOffers.length;
const similarCompanyOffers90PercentileIndex = Math.ceil(
noOfSimilarCompanyOffers * 0.1,
);
const topPercentileCompanyOffers =
noOfSimilarCompanyOffers > 2
? similarCompanyOffers.slice(
similarCompanyOffers90PercentileIndex,
similarCompanyOffers90PercentileIndex + 2,
)
: similarCompanyOffers;
return {
noOfSimilarOffers: noOfSimilarCompanyOffers,
percentile: companyPercentile,
topSimilarOffers: topPercentileCompanyOffers,
};
},
);
// CALCULATE PERCENTILES
// OVERALL ANALYSIS
const overallIndex = searchOfferPercentile(
overallHighestOffer,
similarOffers,
@ -324,23 +386,9 @@ export const offersAnalysisRouter = createRouter()
? 100
: 100 - (100 * overallIndex) / (similarOffers.length - 1);
const companyIndex = searchOfferPercentile(
overallHighestOffer,
similarCompanyOffers,
);
const companyPercentile =
similarCompanyOffers.length <= 1
? 100
: 100 - (100 * companyIndex) / (similarCompanyOffers.length - 1);
// FIND TOP >=90 PERCENTILE OFFERS, DOESN'T GIVE 100th PERCENTILE
// e.g. If there only 4 offers, it gives the 2nd and 3rd offer
similarOffers = similarOffers.filter(
(offer) => offer.id !== overallHighestOffer.id,
);
similarCompanyOffers = similarCompanyOffers.filter(
(offer) => offer.id !== overallHighestOffer.id,
);
const noOfSimilarOffers = similarOffers.length;
const similarOffers90PercentileIndex = Math.ceil(noOfSimilarOffers * 0.1);
@ -352,86 +400,100 @@ export const offersAnalysisRouter = createRouter()
)
: similarOffers;
const noOfSimilarCompanyOffers = similarCompanyOffers.length;
const similarCompanyOffers90PercentileIndex = Math.ceil(
noOfSimilarCompanyOffers * 0.1,
);
const topPercentileCompanyOffers =
noOfSimilarCompanyOffers > 2
? similarCompanyOffers.slice(
similarCompanyOffers90PercentileIndex,
similarCompanyOffers90PercentileIndex + 2,
)
: similarCompanyOffers;
const analysis = await ctx.prisma.offersAnalysis.create({
data: {
companyPercentile,
noOfSimilarCompanyOffers,
noOfSimilarOffers,
companyAnalysis: {
create: companyAnalysis.map((analysisUnit) => {
return {
noOfSimilarOffers: analysisUnit.noOfSimilarOffers,
percentile: analysisUnit.percentile,
topSimilarOffers: {
connect: analysisUnit.topSimilarOffers.map((offer) => {
return { id: offer.id };
}),
},
};
}),
},
overallAnalysis: {
create: {
noOfSimilarOffers,
percentile: overallPercentile,
topSimilarOffers: {
connect: topPercentileOffers.map((offer) => {
return { id: offer.id };
}),
},
},
},
overallHighestOffer: {
connect: {
id: overallHighestOffer.id,
},
},
overallPercentile,
profile: {
connect: {
id: input.profileId,
},
},
topCompanyOffers: {
connect: topPercentileCompanyOffers.map((offer) => {
return { id: offer.id };
}),
},
topOverallOffers: {
connect: topPercentileOffers.map((offer) => {
return { id: offer.id };
}),
},
},
include: {
overallHighestOffer: {
companyAnalysis: {
include: {
company: true,
offersFullTime: {
topSimilarOffers: {
include: {
totalCompensation: true,
},
},
offersIntern: {
include: {
monthlySalary: true,
},
},
profile: {
include: {
background: true,
company: true,
offersFullTime: {
include: {
totalCompensation: true,
},
},
offersIntern: {
include: {
monthlySalary: true,
},
},
profile: {
include: {
background: {
include: {
experiences: {
include: {
company: true,
},
},
},
},
},
},
},
},
},
},
topCompanyOffers: {
overallAnalysis: {
include: {
company: true,
offersFullTime: {
topSimilarOffers: {
include: {
totalCompensation: true,
},
},
offersIntern: {
include: {
monthlySalary: true,
},
},
profile: {
include: {
background: {
company: true,
offersFullTime: {
include: {
totalCompensation: true,
},
},
offersIntern: {
include: {
monthlySalary: true,
},
},
profile: {
include: {
experiences: {
background: {
include: {
company: true,
experiences: {
include: {
company: true,
},
},
},
},
},
@ -440,7 +502,7 @@ export const offersAnalysisRouter = createRouter()
},
},
},
topOverallOffers: {
overallHighestOffer: {
include: {
company: true,
offersFullTime: {
@ -455,15 +517,7 @@ export const offersAnalysisRouter = createRouter()
},
profile: {
include: {
background: {
include: {
experiences: {
include: {
company: true,
},
},
},
},
background: true,
},
},
},

@ -128,46 +128,62 @@ export const offersProfileRouter = createRouter()
include: {
analysis: {
include: {
overallHighestOffer: {
companyAnalysis: {
include: {
company: true,
offersFullTime: {
include: {
totalCompensation: true,
},
},
offersIntern: {
topSimilarOffers: {
include: {
monthlySalary: true,
},
},
profile: {
include: {
background: true,
company: true,
offersFullTime: {
include: {
totalCompensation: true,
},
},
offersIntern: {
include: {
monthlySalary: true,
},
},
profile: {
include: {
background: {
include: {
experiences: {
include: {
company: true,
},
},
},
},
},
},
},
},
},
},
topCompanyOffers: {
overallAnalysis: {
include: {
company: true,
offersFullTime: {
include: {
totalCompensation: true,
},
},
offersIntern: {
topSimilarOffers: {
include: {
monthlySalary: true,
},
},
profile: {
include: {
background: {
company: true,
offersFullTime: {
include: {
experiences: {
totalCompensation: true,
},
},
offersIntern: {
include: {
monthlySalary: true,
},
},
profile: {
include: {
background: {
include: {
company: true,
experiences: {
include: {
company: true,
},
},
},
},
},
@ -176,7 +192,7 @@ export const offersProfileRouter = createRouter()
},
},
},
topOverallOffers: {
overallHighestOffer: {
include: {
company: true,
offersFullTime: {
@ -191,15 +207,7 @@ export const offersProfileRouter = createRouter()
},
profile: {
include: {
background: {
include: {
experiences: {
include: {
company: true,
},
},
},
},
background: true,
},
},
},
@ -409,7 +417,7 @@ export const offersProfileRouter = createRouter()
message: 'Missing fields in background experiences.',
});
}),
)
),
},
specificYoes: {
create: input.background.specificYoes.map((x) => {

@ -143,14 +143,14 @@ export type OffersDiscussion = {
};
export type ProfileAnalysis = {
companyAnalysis: Array<Analysis>;
companyAnalysis: Array<AnalysisUnit>;
id: string;
overallAnalysis: Analysis;
overallAnalysis: AnalysisUnit;
overallHighestOffer: AnalysisHighestOffer;
profileId: string;
};
export type Analysis = {
export type AnalysisUnit = {
noOfOffers: number;
percentile: number;
topPercentileOffers: Array<AnalysisOffer>;

Loading…
Cancel
Save