mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-03-22 02:14:41 -05:00
Make ending plus voting a script
This commit is contained in:
parent
8f13aa2fe6
commit
b68cfc2aa4
|
|
@ -13,6 +13,7 @@
|
|||
"migrate:reset": "prisma migrate reset",
|
||||
"gen": "npx prisma generate",
|
||||
"prebuild": "ts-node prisma/scripts/preBuild.ts",
|
||||
"results": "ts-node prisma/scripts/endPlusVoting.ts",
|
||||
"mongo": "ts-node prisma/scripts/dataFromMongo.ts",
|
||||
"top500": "ts-node prisma/scripts/top500jsons.ts",
|
||||
"league": "cross-env NODE_OPTIONS=--max-old-space-size=8192 ts-node prisma/scripts/leagueJsons.ts",
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import { useState } from "react";
|
|||
import { ADMIN_DISCORD_ID } from "utils/constants";
|
||||
import { getToastOptions } from "utils/objects";
|
||||
import { sendData } from "utils/postData";
|
||||
import { trpc } from "utils/trpc";
|
||||
|
||||
const AdminPage = () => {
|
||||
const toast = useToast();
|
||||
|
|
@ -15,16 +14,6 @@ const AdminPage = () => {
|
|||
switchAccountId: "",
|
||||
discordId: "",
|
||||
});
|
||||
const endVoting = trpc.useMutation("plus.endVoting", {
|
||||
onSuccess: () => console.log("success"),
|
||||
onError: (e) => console.error(e.message),
|
||||
});
|
||||
|
||||
const handleEndVoting = () => {
|
||||
if (!window.confirm("End voting?")) return;
|
||||
|
||||
endVoting.mutate(null);
|
||||
};
|
||||
|
||||
if (!user || user.discordId !== ADMIN_DISCORD_ID) return null;
|
||||
|
||||
|
|
@ -60,13 +49,6 @@ const AdminPage = () => {
|
|||
<Button isLoading={sending} onClick={updateUser}>
|
||||
Submit
|
||||
</Button>
|
||||
<Heading my={4}>End voting</Heading>
|
||||
<Button
|
||||
isLoading={endVoting.status === "loading"}
|
||||
onClick={handleEndVoting}
|
||||
>
|
||||
End
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
|
||||
|
|
|
|||
208
prisma/scripts/endPlusVoting.ts
Normal file
208
prisma/scripts/endPlusVoting.ts
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
import { Prisma } from "@prisma/client";
|
||||
import { getPercentageFromCounts } from "../../utils/plus";
|
||||
import { VOUCH_CRITERIA } from "../../utils/constants";
|
||||
import prisma from "../client";
|
||||
|
||||
// Include Prisma's .env file as well, so we can fetch the DATABASE_URL
|
||||
require("dotenv").config({ path: "prisma/.env" });
|
||||
|
||||
const main = async () => {
|
||||
const [ballots, statuses, suggestions] = await Promise.all([
|
||||
prisma.plusBallot.findMany({
|
||||
where: { isStale: false },
|
||||
include: { voterUser: { include: { plusStatus: true } } },
|
||||
}),
|
||||
prisma.plusStatus.findMany({}),
|
||||
prisma.plusSuggestion.findMany({}),
|
||||
]);
|
||||
|
||||
const month = new Date().getMonth() + 1;
|
||||
const year = new Date().getFullYear();
|
||||
|
||||
const summariesByTier = [
|
||||
new Map<number, Prisma.PlusVotingSummaryCreateManyInput>(),
|
||||
new Map<number, Prisma.PlusVotingSummaryCreateManyInput>(),
|
||||
new Map<number, Prisma.PlusVotingSummaryCreateManyInput>(),
|
||||
new Map<number, Prisma.PlusVotingSummaryCreateManyInput>(),
|
||||
];
|
||||
|
||||
for (const ballot of ballots) {
|
||||
if (!summariesByTier[ballot.tier].has(ballot.votedId)) {
|
||||
summariesByTier[ballot.tier].set(ballot.votedId, {
|
||||
month,
|
||||
tier: ballot.tier,
|
||||
userId: ballot.votedId,
|
||||
wasSuggested: suggestions.some(
|
||||
(suggestion) =>
|
||||
suggestion.tier === ballot.tier &&
|
||||
suggestion.suggestedId === ballot.votedId
|
||||
),
|
||||
wasVouched: statuses.some(
|
||||
(status) =>
|
||||
status.vouchTier === ballot.tier && status.userId === ballot.votedId
|
||||
),
|
||||
year,
|
||||
countsEU: [0, 0, 0, 0],
|
||||
countsNA: [0, 0, 0, 0],
|
||||
});
|
||||
}
|
||||
|
||||
const summary = summariesByTier[ballot.tier].get(ballot.votedId);
|
||||
if (!summary) throw Error("unexpected no summary");
|
||||
|
||||
const isNA = ballot.voterUser.plusStatus!.region === "NA";
|
||||
|
||||
const scoreToIndex = { "-2": 0, "-1": 1, "1": 2, "2": 3 } as const;
|
||||
const arrToChange = (isNA ? summary.countsNA : summary.countsEU) as [
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number
|
||||
];
|
||||
const key = ("" + ballot.score) as keyof typeof scoreToIndex;
|
||||
arrToChange[scoreToIndex[key]]++;
|
||||
}
|
||||
|
||||
const members: [number[], number[], number[], number[]] = [[], [], [], []];
|
||||
const canVouch: [null, number[], number[], number[]] = [null, [], [], []];
|
||||
const vouchRevoked: number[] = [];
|
||||
|
||||
const alreadyMember = new Set<number>();
|
||||
|
||||
for (const [i, summaries] of [
|
||||
Array.from(summariesByTier[1].values()),
|
||||
Array.from(summariesByTier[2].values()),
|
||||
Array.from(summariesByTier[3].values()),
|
||||
].entries()) {
|
||||
const tier = i + 1;
|
||||
|
||||
for (const summary of summaries) {
|
||||
const plusStatus = statuses.find(
|
||||
(status) => status.userId === summary.userId
|
||||
);
|
||||
if (!plusStatus) {
|
||||
throw Error("unexpected no plusStatus in loop");
|
||||
}
|
||||
|
||||
const passedVoting =
|
||||
getPercentageFromCounts(
|
||||
summary.countsNA as number[],
|
||||
summary.countsEU as number[],
|
||||
plusStatus.region
|
||||
) >= 50;
|
||||
|
||||
if (passedVoting) {
|
||||
if (!alreadyMember.has(summary.userId)) {
|
||||
members[tier].push(summary.userId);
|
||||
alreadyMember.add(summary.userId);
|
||||
}
|
||||
} else {
|
||||
// get put to a lower tier only if not suggestion or vouch
|
||||
if (
|
||||
!summary.wasSuggested &&
|
||||
!summary.wasVouched &&
|
||||
!alreadyMember.has(summary.userId)
|
||||
) {
|
||||
const tierToDemoteTo = tier === 3 ? 0 : tier + 1;
|
||||
members[tierToDemoteTo].push(summary.userId);
|
||||
alreadyMember.add(summary.userId);
|
||||
}
|
||||
|
||||
// if they were vouched time for whoever vouched them to have their perms revoked for 6 months
|
||||
if (plusStatus.vouchTier === tier) {
|
||||
if (!plusStatus.voucherId) {
|
||||
throw Error("unexpexted no voucher id");
|
||||
}
|
||||
vouchRevoked.push(plusStatus.voucherId);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const gotVouchPermits =
|
||||
getPercentageFromCounts(
|
||||
summary.countsNA as number[],
|
||||
summary.countsEU as number[],
|
||||
plusStatus.region
|
||||
) >= VOUCH_CRITERIA[tier];
|
||||
|
||||
if (gotVouchPermits) {
|
||||
canVouch[tier]!.push(summary.userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log("deleting stale plus ballots and setting can vouch for null");
|
||||
|
||||
await Promise.all([
|
||||
prisma.plusBallot.deleteMany({ where: { isStale: true } }),
|
||||
prisma.plusStatus.updateMany({
|
||||
data: { canVouchFor: null },
|
||||
}),
|
||||
]);
|
||||
|
||||
const now = new Date();
|
||||
|
||||
console.log("doing a lot of stuff");
|
||||
|
||||
await prisma.$transaction([
|
||||
prisma.plusBallot.updateMany({ data: { isStale: true } }),
|
||||
prisma.plusSuggestion.deleteMany({}),
|
||||
prisma.plusVotingSummary.createMany({
|
||||
data: [
|
||||
...Array.from(summariesByTier[1].values()),
|
||||
...Array.from(summariesByTier[2].values()),
|
||||
...Array.from(summariesByTier[3].values()),
|
||||
],
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
data: { vouchTier: null, voucherId: null },
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
where: { canVouchAgainAfter: { lt: new Date() } },
|
||||
data: { canVouchAgainAfter: null },
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
where: { userId: { in: members[0] } },
|
||||
data: { membershipTier: null },
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
where: { userId: { in: members[1] } },
|
||||
data: { membershipTier: 1 },
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
where: { userId: { in: members[2] } },
|
||||
data: { membershipTier: 2 },
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
where: { userId: { in: members[3] } },
|
||||
data: { membershipTier: 3 },
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
where: { userId: { in: canVouch[1] } },
|
||||
data: { canVouchFor: 1 },
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
where: { userId: { in: canVouch[2] } },
|
||||
data: { canVouchFor: 2 },
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
where: { userId: { in: canVouch[3] } },
|
||||
data: { canVouchFor: 3 },
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
where: { userId: { in: vouchRevoked } },
|
||||
data: {
|
||||
canVouchAgainAfter: new Date(now.getFullYear(), now.getMonth() + 5, 1),
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
console.log("done with a lot of stuff");
|
||||
};
|
||||
|
||||
main()
|
||||
.catch((e) => console.error(e))
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
191
services/plus.ts
191
services/plus.ts
|
|
@ -2,7 +2,6 @@ import { PlusRegion, Prisma } from "@prisma/client";
|
|||
import { httpError } from "@trpc/server";
|
||||
import prisma from "prisma/client";
|
||||
import { shuffleArray } from "utils/arrays";
|
||||
import { ADMIN_ID, VOUCH_CRITERIA } from "utils/constants";
|
||||
import { getPercentageFromCounts, getVotingRange } from "utils/plus";
|
||||
import { userBasicSelection } from "utils/prisma";
|
||||
import { suggestionFullSchema } from "utils/validators/suggestion";
|
||||
|
|
@ -603,195 +602,6 @@ const editVote = async ({
|
|||
});
|
||||
};
|
||||
|
||||
const endVoting = async (userId: number) => {
|
||||
if (userId !== ADMIN_ID) throw httpError.badRequest("not admin");
|
||||
|
||||
const [ballots, statuses, suggestions] = await Promise.all([
|
||||
prisma.plusBallot.findMany({
|
||||
where: { isStale: false },
|
||||
include: { voterUser: { include: { plusStatus: true } } },
|
||||
}),
|
||||
prisma.plusStatus.findMany({}),
|
||||
prisma.plusSuggestion.findMany({}),
|
||||
]);
|
||||
|
||||
const month = new Date().getMonth() + 1;
|
||||
const year = new Date().getFullYear();
|
||||
|
||||
const summariesByTier = [
|
||||
new Map<number, Prisma.PlusVotingSummaryCreateManyInput>(),
|
||||
new Map<number, Prisma.PlusVotingSummaryCreateManyInput>(),
|
||||
new Map<number, Prisma.PlusVotingSummaryCreateManyInput>(),
|
||||
new Map<number, Prisma.PlusVotingSummaryCreateManyInput>(),
|
||||
];
|
||||
|
||||
for (const ballot of ballots) {
|
||||
if (!summariesByTier[ballot.tier].has(ballot.votedId)) {
|
||||
summariesByTier[ballot.tier].set(ballot.votedId, {
|
||||
month,
|
||||
tier: ballot.tier,
|
||||
userId: ballot.votedId,
|
||||
wasSuggested: suggestions.some(
|
||||
(suggestion) =>
|
||||
suggestion.tier === ballot.tier &&
|
||||
suggestion.suggestedId === ballot.votedId
|
||||
),
|
||||
wasVouched: statuses.some(
|
||||
(status) =>
|
||||
status.vouchTier === ballot.tier && status.userId === ballot.votedId
|
||||
),
|
||||
year,
|
||||
countsEU: [0, 0, 0, 0],
|
||||
countsNA: [0, 0, 0, 0],
|
||||
});
|
||||
}
|
||||
|
||||
const summary = summariesByTier[ballot.tier].get(ballot.votedId);
|
||||
if (!summary) throw httpError.badRequest("unexpected no summary");
|
||||
|
||||
const isNA = ballot.voterUser.plusStatus!.region === "NA";
|
||||
|
||||
const scoreToIndex = { "-2": 0, "-1": 1, "1": 2, "2": 3 } as const;
|
||||
const arrToChange = (isNA ? summary.countsNA : summary.countsEU) as [
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number
|
||||
];
|
||||
const key = ("" + ballot.score) as keyof typeof scoreToIndex;
|
||||
arrToChange[scoreToIndex[key]]++;
|
||||
}
|
||||
|
||||
const members: [number[], number[], number[], number[]] = [[], [], [], []];
|
||||
const canVouch: [null, number[], number[], number[]] = [null, [], [], []];
|
||||
const vouchRevoked: number[] = [];
|
||||
|
||||
const alreadyMember = new Set<number>();
|
||||
|
||||
for (const [i, summaries] of [
|
||||
Array.from(summariesByTier[1].values()),
|
||||
Array.from(summariesByTier[2].values()),
|
||||
Array.from(summariesByTier[3].values()),
|
||||
].entries()) {
|
||||
const tier = i + 1;
|
||||
|
||||
for (const summary of summaries) {
|
||||
const plusStatus = statuses.find(
|
||||
(status) => status.userId === summary.userId
|
||||
);
|
||||
if (!plusStatus)
|
||||
throw httpError.badRequest("unexpected no plusStatus in loop");
|
||||
|
||||
const passedVoting =
|
||||
getPercentageFromCounts(
|
||||
summary.countsNA as number[],
|
||||
summary.countsEU as number[],
|
||||
plusStatus.region
|
||||
) >= 50;
|
||||
|
||||
if (passedVoting) {
|
||||
if (!alreadyMember.has(summary.userId)) {
|
||||
members[tier].push(summary.userId);
|
||||
alreadyMember.add(summary.userId);
|
||||
}
|
||||
} else {
|
||||
// get put to a lower tier only if not suggestion or vouch
|
||||
if (
|
||||
!summary.wasSuggested &&
|
||||
!summary.wasVouched &&
|
||||
!alreadyMember.has(summary.userId)
|
||||
) {
|
||||
const tierToDemoteTo = tier === 3 ? 0 : tier + 1;
|
||||
members[tierToDemoteTo].push(summary.userId);
|
||||
alreadyMember.add(summary.userId);
|
||||
}
|
||||
|
||||
// if they were vouched time for whoever vouched them to have their perms revoked for 6 months
|
||||
if (plusStatus.vouchTier === tier) {
|
||||
if (!plusStatus.voucherId)
|
||||
throw httpError.badRequest("unexpexted no voucher id");
|
||||
vouchRevoked.push(plusStatus.voucherId);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const gotVouchPermits =
|
||||
getPercentageFromCounts(
|
||||
summary.countsNA as number[],
|
||||
summary.countsEU as number[],
|
||||
plusStatus.region
|
||||
) >= VOUCH_CRITERIA[tier];
|
||||
|
||||
if (gotVouchPermits) {
|
||||
canVouch[tier]!.push(summary.userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
prisma.plusBallot.deleteMany({ where: { isStale: true } }),
|
||||
prisma.plusStatus.updateMany({
|
||||
data: { canVouchFor: null },
|
||||
}),
|
||||
]);
|
||||
|
||||
const now = new Date();
|
||||
|
||||
return prisma.$transaction([
|
||||
prisma.plusBallot.updateMany({ data: { isStale: true } }),
|
||||
prisma.plusSuggestion.deleteMany({}),
|
||||
prisma.plusVotingSummary.createMany({
|
||||
data: [
|
||||
...Array.from(summariesByTier[1].values()),
|
||||
...Array.from(summariesByTier[2].values()),
|
||||
...Array.from(summariesByTier[3].values()),
|
||||
],
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
data: { vouchTier: null, voucherId: null },
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
where: { canVouchAgainAfter: { lt: new Date() } },
|
||||
data: { canVouchAgainAfter: null },
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
where: { userId: { in: members[0] } },
|
||||
data: { membershipTier: null },
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
where: { userId: { in: members[1] } },
|
||||
data: { membershipTier: 1 },
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
where: { userId: { in: members[2] } },
|
||||
data: { membershipTier: 2 },
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
where: { userId: { in: members[3] } },
|
||||
data: { membershipTier: 3 },
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
where: { userId: { in: canVouch[1] } },
|
||||
data: { canVouchFor: 1 },
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
where: { userId: { in: canVouch[2] } },
|
||||
data: { canVouchFor: 2 },
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
where: { userId: { in: canVouch[3] } },
|
||||
data: { canVouchFor: 3 },
|
||||
}),
|
||||
prisma.plusStatus.updateMany({
|
||||
where: { userId: { in: vouchRevoked } },
|
||||
data: {
|
||||
canVouchAgainAfter: new Date(now.getFullYear(), now.getMonth() + 5, 1),
|
||||
},
|
||||
}),
|
||||
]);
|
||||
};
|
||||
|
||||
const plusService = {
|
||||
getPlusStatuses,
|
||||
getSuggestions,
|
||||
|
|
@ -805,7 +615,6 @@ const plusService = {
|
|||
addVotes,
|
||||
editVote,
|
||||
votedUserScores,
|
||||
endVoting,
|
||||
};
|
||||
|
||||
export default plusService;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user