mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2025-07-28 20:52:00 +08:00
[offers][chore] Save the generate offer profile analysis into db
This commit is contained in:
@ -0,0 +1,3 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "OffersAnalysis" ALTER COLUMN "overallPercentile" SET DATA TYPE DOUBLE PRECISION,
|
||||||
|
ALTER COLUMN "companyPercentile" SET DATA TYPE DOUBLE PRECISION;
|
@ -365,12 +365,12 @@ model OffersAnalysis {
|
|||||||
offerId String @unique
|
offerId String @unique
|
||||||
|
|
||||||
// OVERALL
|
// OVERALL
|
||||||
overallPercentile Int
|
overallPercentile Float
|
||||||
noOfSimilarOffers Int
|
noOfSimilarOffers Int
|
||||||
topOverallOffers OffersOffer[] @relation("TopOverallOffers")
|
topOverallOffers OffersOffer[] @relation("TopOverallOffers")
|
||||||
|
|
||||||
// Company
|
// Company
|
||||||
companyPercentile Int
|
companyPercentile Float
|
||||||
noOfSimilarCompanyOffers Int
|
noOfSimilarCompanyOffers Int
|
||||||
topCompanyOffers OffersOffer[] @relation("TopCompanyOffers")
|
topCompanyOffers OffersOffer[] @relation("TopCompanyOffers")
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ function Test() {
|
|||||||
],
|
],
|
||||||
experiences: [
|
experiences: [
|
||||||
{
|
{
|
||||||
companyId: 'cl95u79f000007im531ysjg79',
|
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||||
durationInMonths: 24,
|
durationInMonths: 24,
|
||||||
jobType: 'FULLTIME',
|
jobType: 'FULLTIME',
|
||||||
level: 'Junior',
|
level: 'Junior',
|
||||||
@ -151,7 +151,7 @@ function Test() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Comments: '',
|
// Comments: '',
|
||||||
companyId: 'cl95u79f000007im531ysjg79',
|
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||||
jobType: 'FULLTIME',
|
jobType: 'FULLTIME',
|
||||||
location: 'Singapore, Singapore',
|
location: 'Singapore, Singapore',
|
||||||
monthYearReceived: new Date('2022-09-30T07:58:54.000Z'),
|
monthYearReceived: new Date('2022-09-30T07:58:54.000Z'),
|
||||||
@ -180,7 +180,7 @@ function Test() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
comments: undefined,
|
comments: undefined,
|
||||||
companyId: 'cl95u79f000007im531ysjg79',
|
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||||
jobType: 'FULLTIME',
|
jobType: 'FULLTIME',
|
||||||
location: 'Singapore, Singapore',
|
location: 'Singapore, Singapore',
|
||||||
monthYearReceived: new Date('2022-09-30T07:58:54.000Z'),
|
monthYearReceived: new Date('2022-09-30T07:58:54.000Z'),
|
||||||
@ -261,7 +261,7 @@ function Test() {
|
|||||||
slug: 'meta',
|
slug: 'meta',
|
||||||
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||||
},
|
},
|
||||||
companyId: 'cl95u79f000007im531ysjg79',
|
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||||
durationInMonths: 24,
|
durationInMonths: 24,
|
||||||
id: 'cl96stky6002iw32gpt6t87s2',
|
id: 'cl96stky6002iw32gpt6t87s2',
|
||||||
jobType: 'FULLTIME',
|
jobType: 'FULLTIME',
|
||||||
@ -368,7 +368,7 @@ function Test() {
|
|||||||
slug: 'meta',
|
slug: 'meta',
|
||||||
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||||
},
|
},
|
||||||
companyId: 'cl95u79f000007im531ysjg79',
|
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||||
id: 'cl976t4de00047iygl0zbce11',
|
id: 'cl976t4de00047iygl0zbce11',
|
||||||
jobType: 'FULLTIME',
|
jobType: 'FULLTIME',
|
||||||
location: 'Singapore, Singapore',
|
location: 'Singapore, Singapore',
|
||||||
@ -421,7 +421,7 @@ function Test() {
|
|||||||
slug: 'meta',
|
slug: 'meta',
|
||||||
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||||
},
|
},
|
||||||
companyId: 'cl95u79f000007im531ysjg79',
|
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||||
id: 'cl96stky80031w32gau9mu1gs',
|
id: 'cl96stky80031w32gau9mu1gs',
|
||||||
jobType: 'FULLTIME',
|
jobType: 'FULLTIME',
|
||||||
location: 'Singapore, Singapore',
|
location: 'Singapore, Singapore',
|
||||||
@ -474,7 +474,7 @@ function Test() {
|
|||||||
slug: 'meta',
|
slug: 'meta',
|
||||||
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||||
},
|
},
|
||||||
companyId: 'cl95u79f000007im531ysjg79',
|
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||||
id: 'cl96stky9003bw32gc3l955vr',
|
id: 'cl96stky9003bw32gc3l955vr',
|
||||||
jobType: 'FULLTIME',
|
jobType: 'FULLTIME',
|
||||||
location: 'Singapore, Singapore',
|
location: 'Singapore, Singapore',
|
||||||
@ -527,7 +527,7 @@ function Test() {
|
|||||||
slug: 'meta',
|
slug: 'meta',
|
||||||
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||||
},
|
},
|
||||||
companyId: 'cl95u79f000007im531ysjg79',
|
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||||
id: 'cl976wf28000t7iyga4noyz7s',
|
id: 'cl976wf28000t7iyga4noyz7s',
|
||||||
jobType: 'FULLTIME',
|
jobType: 'FULLTIME',
|
||||||
location: 'Singapore, Singapore',
|
location: 'Singapore, Singapore',
|
||||||
@ -580,7 +580,7 @@ function Test() {
|
|||||||
slug: 'meta',
|
slug: 'meta',
|
||||||
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||||
},
|
},
|
||||||
companyId: 'cl95u79f000007im531ysjg79',
|
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||||
id: 'cl96tbb3o0051w32gjrpaiiit',
|
id: 'cl96tbb3o0051w32gjrpaiiit',
|
||||||
jobType: 'FULLTIME',
|
jobType: 'FULLTIME',
|
||||||
location: 'Singapore, Singapore',
|
location: 'Singapore, Singapore',
|
||||||
|
@ -5,7 +5,7 @@ import { trpc } from '~/utils/trpc';
|
|||||||
function profileAnalysis() {
|
function profileAnalysis() {
|
||||||
const analysis = trpc.useQuery([
|
const analysis = trpc.useQuery([
|
||||||
'offers.analysis.generate',
|
'offers.analysis.generate',
|
||||||
{ profileId: 'cl96stky5002ew32gx2kale2x' },
|
{ profileId: 'cl98yxuei002htx1s8lrmwzmy' },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return <div>{JSON.stringify(analysis.data)}</div>;
|
return <div>{JSON.stringify(analysis.data)}</div>;
|
||||||
|
@ -8,6 +8,7 @@ import type {
|
|||||||
OffersOffer,
|
OffersOffer,
|
||||||
OffersProfile,
|
OffersProfile,
|
||||||
} from '@prisma/client';
|
} from '@prisma/client';
|
||||||
|
import { JobType } from '@prisma/client';
|
||||||
import { TRPCError } from '@trpc/server';
|
import { TRPCError } from '@trpc/server';
|
||||||
|
|
||||||
import { createRouter } from '../context';
|
import { createRouter } from '../context';
|
||||||
@ -31,14 +32,32 @@ const binarySearchOfferPercentile = (
|
|||||||
let start = 0;
|
let start = 0;
|
||||||
let end = similarOffers.length - 1;
|
let end = similarOffers.length - 1;
|
||||||
|
|
||||||
|
const salary =
|
||||||
|
offer.jobType === JobType.FULLTIME
|
||||||
|
? offer.OffersFullTime?.totalCompensation.value
|
||||||
|
: offer.OffersIntern?.monthlySalary.value;
|
||||||
|
|
||||||
|
if (!salary) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: 'BAD_REQUEST',
|
||||||
|
message: 'Cannot analyse without salary',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
while (start <= end) {
|
while (start <= end) {
|
||||||
const mid = Math.floor((start + end) / 2);
|
const mid = Math.floor((start + end) / 2);
|
||||||
|
|
||||||
if (similarOffers[mid].id === offer.id) {
|
const similarOffer = similarOffers[mid];
|
||||||
|
const similarSalary =
|
||||||
|
similarOffer.jobType === JobType.FULLTIME
|
||||||
|
? similarOffer.OffersFullTime?.totalCompensation.value
|
||||||
|
: similarOffer.OffersIntern?.monthlySalary.value;
|
||||||
|
|
||||||
|
if (similarSalary === salary) {
|
||||||
return mid;
|
return mid;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offer.id < similarOffers[mid].id) {
|
if (salary < similarSalary) {
|
||||||
end = mid - 1;
|
end = mid - 1;
|
||||||
} else {
|
} else {
|
||||||
start = mid + 1;
|
start = mid + 1;
|
||||||
@ -199,30 +218,29 @@ export const offersAnalysisRouter = createRouter().query('generate', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let similarCompanyOffers = similarOffers.filter(
|
let similarCompanyOffers = similarOffers.filter(
|
||||||
(offer) => offer.companyId === overallHighestOffer.companyId,
|
(offer: { companyId: string }) =>
|
||||||
|
offer.companyId === overallHighestOffer.companyId,
|
||||||
);
|
);
|
||||||
|
|
||||||
// CALCULATE PERCENTILES
|
// CALCULATE PERCENTILES
|
||||||
const highestOfferAgainstOverallIndex = binarySearchOfferPercentile(
|
const overallIndex = binarySearchOfferPercentile(
|
||||||
overallHighestOffer,
|
overallHighestOffer,
|
||||||
similarOffers,
|
similarOffers,
|
||||||
);
|
);
|
||||||
const highestOfferAgainstOverallPercentile =
|
const overallPercentile = overallIndex / similarOffers.length;
|
||||||
highestOfferAgainstOverallIndex / similarOffers.length;
|
|
||||||
|
|
||||||
const highestOfferAgainstCompanyIndex = binarySearchOfferPercentile(
|
const companyIndex = binarySearchOfferPercentile(
|
||||||
overallHighestOffer,
|
overallHighestOffer,
|
||||||
similarCompanyOffers,
|
similarCompanyOffers,
|
||||||
);
|
);
|
||||||
const highestOfferAgainstCompanyPercentile =
|
const companyPercentile = companyIndex / similarCompanyOffers.length;
|
||||||
highestOfferAgainstCompanyIndex / similarCompanyOffers.length;
|
|
||||||
|
|
||||||
// FIND TOP >=90 PERCENTILE OFFERS
|
// FIND TOP >=90 PERCENTILE OFFERS
|
||||||
similarOffers = similarOffers.filter(
|
similarOffers = similarOffers.filter(
|
||||||
(offer) => offer.id !== overallHighestOffer.id,
|
(offer: { id: string }) => offer.id !== overallHighestOffer.id,
|
||||||
);
|
);
|
||||||
similarCompanyOffers = similarCompanyOffers.filter(
|
similarCompanyOffers = similarCompanyOffers.filter(
|
||||||
(offer) => offer.id !== overallHighestOffer.id,
|
(offer: { id: string }) => offer.id !== overallHighestOffer.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
const noOfSimilarOffers = similarOffers.length;
|
const noOfSimilarOffers = similarOffers.length;
|
||||||
@ -247,18 +265,113 @@ export const offersAnalysisRouter = createRouter().query('generate', {
|
|||||||
)
|
)
|
||||||
: similarCompanyOffers;
|
: similarCompanyOffers;
|
||||||
|
|
||||||
return {
|
const analysis = await ctx.prisma.offersAnalysis.create({
|
||||||
company: {
|
data: {
|
||||||
highestOfferAgainstCompanyPercentile,
|
companyPercentile,
|
||||||
noOfSimilarCompanyOffers,
|
noOfSimilarCompanyOffers,
|
||||||
topPercentileCompanyOffers,
|
|
||||||
},
|
|
||||||
overall: {
|
|
||||||
highestOfferAgainstOverallPercentile,
|
|
||||||
noOfSimilarOffers,
|
noOfSimilarOffers,
|
||||||
topPercentileOffers,
|
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 };
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
overallHighestOffer,
|
include: {
|
||||||
};
|
overallHighestOffer: {
|
||||||
|
include: {
|
||||||
|
OffersFullTime: {
|
||||||
|
include: {
|
||||||
|
totalCompensation: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OffersIntern: {
|
||||||
|
include: {
|
||||||
|
monthlySalary: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
company: true,
|
||||||
|
profile: {
|
||||||
|
include: {
|
||||||
|
background: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
topCompanyOffers: {
|
||||||
|
include: {
|
||||||
|
OffersFullTime: {
|
||||||
|
include: {
|
||||||
|
totalCompensation: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OffersIntern: {
|
||||||
|
include: {
|
||||||
|
monthlySalary: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
company: true,
|
||||||
|
profile: {
|
||||||
|
include: {
|
||||||
|
background: {
|
||||||
|
include: {
|
||||||
|
experiences: {
|
||||||
|
include: {
|
||||||
|
company: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
topOverallOffers: {
|
||||||
|
include: {
|
||||||
|
OffersFullTime: {
|
||||||
|
include: {
|
||||||
|
totalCompensation: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OffersIntern: {
|
||||||
|
include: {
|
||||||
|
monthlySalary: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
company: true,
|
||||||
|
profile: {
|
||||||
|
include: {
|
||||||
|
background: {
|
||||||
|
include: {
|
||||||
|
experiences: {
|
||||||
|
include: {
|
||||||
|
company: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return analysis;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user