Skip to content

Commit

Permalink
Merge branch 'main' into data-importing
Browse files Browse the repository at this point in the history
  • Loading branch information
tameTNT authored Oct 26, 2024
2 parents 8d95f00 + 02e5eb6 commit 34590ff
Show file tree
Hide file tree
Showing 22 changed files with 367 additions and 149 deletions.
6 changes: 1 addition & 5 deletions client/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export async function getRequest<T>(path: string): Promise<FetchResponse<T>> {
credentials: 'include',
};
const response = await fetch(`${BACKEND_URL}${path}`, options);
if (!response.ok) throw new Error(response.statusText);

const data = await response.json();
return { status: response.status, error: data.error ? data.error : '', data };
Expand All @@ -31,7 +30,6 @@ export async function postRequest<T>(
body: body ? JSON.stringify(body) : null,
};
const response = await fetch(`${BACKEND_URL}${path}`, options);
if (!response.ok) throw new Error(response.statusText);

const data = await response.json();
return { status: response.status, error: data.error ? data.error : '', data };
Expand All @@ -54,7 +52,6 @@ export async function putRequest<T>(
body: body ? JSON.stringify(body) : null,
};
const response = await fetch(`${BACKEND_URL}${path}`, options);
if (!response.ok) throw new Error(response.statusText);

const data = await response.json();
return { status: response.status, error: data.error ? data.error : '', data };
Expand All @@ -67,15 +64,14 @@ export async function putRequest<T>(

export async function deleteRequest(
path: string
): Promise<FetchResponse<OkResponse>> {
): Promise<FetchResponse<YesNoResponse>> {
try {
const options: RequestInit = {
method: 'DELETE',
headers: createHeaders(true),
credentials: 'include',
};
const response = await fetch(`${BACKEND_URL}${path}`, options);
if (!response.ok) throw new Error(response.statusText);

const data = await response.json();
return { status: response.status, error: data.error ? data.error : '', data };
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/admin/AdminToggleSwitch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const AdminToggleSwitch = (props: {
setState: React.Dispatch<React.SetStateAction<boolean>>;
}) => {
return (
<div className="w-full flex flex-row items-center justify-center my-10">
<div className="w-full flex flex-row items-center justify-center">
<div className="bg-primaryLight flex flex-row rounded-full relative">
<div
className={`absolute top-0 left-0 w-44 h-16 transition-transform duration-300 ease-in-out ${
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/admin/tables/HidePopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const DeletePopup = ({ element, close }: DeletePopupProps) => {

const hideElement = async () => {
const resource = isProject(element) ? 'project' : 'judge';
const res = await postRequest<OkResponse>(`/${resource}/hide`, {id: element.id});
const res = await postRequest<YesNoResponse>(`/${resource}/hide`, {id: element.id});
if (res.status === 200) {
fetchStats();
isProject(element) ? fetchProjects() : fetchJudges();
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/admin/tables/JudgeRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const JudgeRow = ({ judge, idx, checked, handleCheckedChange }: JudgeRowProps) =
};

const hideJudge = async () => {
const res = await postRequest<OkResponse>(judge.active ? '/judge/hide' : '/judge/unhide', {id: judge.id});
const res = await postRequest<YesNoResponse>(judge.active ? '/judge/hide' : '/judge/unhide', {id: judge.id});
if (res.status === 200) {
alert(`Judge ${judge.active ? 'hidden' : 'un-hidden'} successfully!`);
fetchJudges();
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/admin/tables/ProjectRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const ProjectRow = ({ project, idx, checked, handleCheckedChange }: ProjectRowPr
};

const hideProject = async () => {
const res = await postRequest<OkResponse>(project.active ? '/project/hide' : '/project/unhide', {id: project.id});
const res = await postRequest<YesNoResponse>(project.active ? '/project/hide' : '/project/unhide', {id: project.id});
if (res.status === 200) {
alert(`Project ${project.active ? 'hidden' : 'un-hidden'} successfully!`);
fetchProjects();
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/judge/Ratings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ const Ratings = (props: RatingsProps) => {

// Score the current project
const scoreRes = props.update
? await putRequest<OkResponse>('/judge/score', {
? await putRequest<YesNoResponse>('/judge/score', {
categories: scores,
project: props.project?.project_id,
})
: await postRequest<OkResponse>('/judge/score', {
: await postRequest<YesNoResponse>('/judge/score', {
categories: scores,
initial: true,
});
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/judge/popups/FlagPopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const FlagPopup = (props: FlagPopupProps) => {
}

// Flag the current project
const flagRes = await postRequest<OkResponse>('/judge/skip', {
const flagRes = await postRequest<YesNoResponse>('/judge/skip', {
reason: selected,
});
if (flagRes.status !== 200) {
Expand Down
4 changes: 4 additions & 0 deletions client/src/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
"noProjects": {
"title": "There are no projects to judge",
"description": "You're early! There seems to be no projects currently in the system to judge. Please let an organizer know if this is unexpected."
},
"judgingEnded": {
"title": "Judging has been ended",
"description": "Judging has been ended by an admin. Please rank and submit what projects you have already seen."
}
},
"flags": {
Expand Down
81 changes: 78 additions & 3 deletions client/src/pages/admin/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import AdminToggleSwitch from '../../components/admin/AdminToggleSwitch';
import AdminToolbar from '../../components/admin/AdminToolbar';
import JuryHeader from '../../components/JuryHeader';
import Loading from '../../components/Loading';
import { postRequest } from '../../api';
import {getRequest, postRequest} from '../../api';
import { errorAlert } from '../../util';
import { useNavigate } from 'react-router-dom';
import Button from '../../components/Button';
Expand All @@ -16,11 +16,14 @@ const Admin = () => {
const navigate = useNavigate();
const [showProjects, setShowProjects] = useState(true);
const [loading, setLoading] = useState(true);
const [judgingIsOver, setJudgingIsOver] = useState(false);
const [numJudges, setNumJudges] = useState(0);
const [submittedJudges, setSubmittedJudges] = useState(0);

useEffect(() => {
// Check if user logged in
async function checkLoggedIn() {
const loggedInRes = await postRequest<OkResponse>('/admin/auth', null);
const loggedInRes = await postRequest<YesNoResponse>('/admin/auth', null);
if (loggedInRes.status === 401) {
console.error(`Admin is not logged in!`);
navigate('/');
Expand All @@ -33,10 +36,71 @@ const Admin = () => {

errorAlert(loggedInRes);
}

checkLoggedIn();

async function getNumJudges() {
const judgeListRes = await getRequest<Judge[]>('/judge/list')
if (judgeListRes.status !== 200) {
errorAlert(judgeListRes);
return;
}
setNumJudges(judgeListRes.data?.length as number);

}
getNumJudges();
checkSubmittedJudges();

async function checkJudgingEnded() {
const judgingEndedRes = await getRequest<YesNoResponse>('/check-judging-over')
if (judgingEndedRes.status !== 200) {
errorAlert(judgingEndedRes);
return;
}
setJudgingIsOver(Boolean(judgingEndedRes.data?.yes_no));
}
checkJudgingEnded();
}, []);

function endJudging() {
let confirmed = window.confirm("Are you sure you want to end judging? This cannot be undone.\n" +
"Judges will not be able to request new projects to rank and must submit rankings.");
if (confirmed) {
endJudgingReq().then(success => {
if (success) {
alert("Judging has now been ended. Judges will be notified and made to submit their rankings. " +
"Wait until all have submitted before recording final results.");
setJudgingIsOver(true);
checkSubmittedJudges();
} else {
alert("Failed to end judging.");
setJudgingIsOver(false);
}
})
}
}

async function endJudgingReq() {
console.log("Requesting server to end judging.")
const endJudgingRes = await postRequest<YesNoResponse>('/admin/end-judging', null)
return endJudgingRes.status === 200;
}

async function checkSubmittedJudges() {
console.log("Refreshing submitted judge count by counting current_projects array lengths")
const justListRes = await getRequest<Judge[]>('/judge/list')
if (justListRes.status !== 200) {
errorAlert(justListRes);
return;
}
if (justListRes.data){
let numSubmitted = 0
justListRes.data.forEach(j => {
if (j.past_rankings.flat().length == j.seen) numSubmitted++
})
setSubmittedJudges(numSubmitted)
}
}

if (loading) {
return <Loading disabled={!loading} />;
}
Expand All @@ -51,6 +115,17 @@ const Admin = () => {
className="absolute top-6 left-[16rem] w-40 md:w-52 text-lg py-2 px-1 hover:scale-100 focus:scale-100 rounded-md font-bold"
>Settings</Button>
<AdminStatsPanel />
<div className="w-full grid grid-cols-3 justify-center justify-items-center items-center my-5">
<div></div>
<Button
type="error"
onClick={endJudging}
disabled={judgingIsOver}
bold
className="justify-self-stretch md:w-full w-full"
>{judgingIsOver ? `Submitted judges: ${submittedJudges}/${numJudges}` : "End Judging"}</Button>
<div hidden={!judgingIsOver} onClick={checkSubmittedJudges} className="justify-self-start cursor-pointer" title="Refresh submitted judges">🔁</div>
</div>
<AdminToggleSwitch state={showProjects} setState={setShowProjects} />
<AdminToolbar showProjects={showProjects} />
<AdminTable showProjects={showProjects} />
Expand Down
52 changes: 26 additions & 26 deletions client/src/pages/admin/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const AdminSettings = () => {
const [judgingTimer, setJudgingTimer] = useState('');
const [minViews, setMinViews] = useState('');
const [categories, setCategories] = useState('');
const [rankingBatchSize, setRankingBatchSize] = useState('');
const [batchRankingSize, setBatchRankingSize] = useState('');
const [loading, setLoading] = useState(true);

async function getOptions() {
Expand Down Expand Up @@ -54,8 +54,8 @@ const AdminSettings = () => {
// Set min views
setMinViews(res.data.min_views.toString());

// Set ranking batch size
setRankingBatchSize(res.data.ranking_batch_size.toString())
// Set batch ranking size
setBatchRankingSize(res.data.batch_ranking_size.toString())
setLoading(false);
}

Expand All @@ -80,10 +80,10 @@ const AdminSettings = () => {
}

// Update the timer
const res = await postRequest<OkResponse>('/admin/timer', {
const res = await postRequest<YesNoResponse>('/admin/timer', {
judging_timer: timer,
});
if (res.status !== 200 || res.data?.ok !== 1) {
if (res.status !== 200 || res.data?.yes_no !== 1) {
errorAlert(res);
return;
}
Expand All @@ -101,10 +101,10 @@ const AdminSettings = () => {
}

// Update min views
const res = await postRequest<OkResponse>('/admin/min-views', {
const res = await postRequest<YesNoResponse>('/admin/min-views', {
min_views: v,
});
if (res.status !== 200 || res.data?.ok !== 1) {
if (res.status !== 200 || res.data?.yes_no !== 1) {
errorAlert(res);
return;
}
Expand All @@ -125,10 +125,10 @@ const AdminSettings = () => {
return
}
// Post the new categories
const res = await postRequest<OkResponse>('/admin/categories', {
const res = await postRequest<YesNoResponse>('/admin/categories', {
categories: filteredCats,
});
if (res.status !== 200 || res.data?.ok !== 1) {
if (res.status !== 200 || res.data?.yes_no !== 1) {
errorAlert(res);
return;
}
Expand All @@ -137,28 +137,28 @@ const AdminSettings = () => {
getOptions();
};

const updateRankingBatchSize = async () => {
// Convert rankingBatchSize to integer
const r = parseInt(rankingBatchSize);
const updateBatchRankingSize = async () => {
// Convert batchRankingSize to integer
const r = parseInt(batchRankingSize);
if (isNaN(r) || r < 2) {
alert('Minimum views should be a positive integer >= 2!');
alert('Minimum batch ranking size should be a positive integer >= 2!');
return;
}
const res = await postRequest<OkResponse>('/admin/ranking-batch-size', {
ranking_batch_size: r,
const res = await postRequest<YesNoResponse>('/admin/batch-ranking-size', {
batch_ranking_size: r,
});
if (res.status !== 200 || res.data?.ok !== 1) {
if (res.status !== 200 || res.data?.yes_no !== 1) {
errorAlert(res);
return;
}

alert('Ranking Batch Size updated!');
alert('Batch Ranking Size updated!');
getOptions();
}

const resetClock = async () => {
const res = await postRequest<OkResponse>('/admin/clock/reset', null);
if (res.status !== 200 || res.data?.ok !== 1) {
const res = await postRequest<YesNoResponse>('/admin/clock/reset', null);
if (res.status !== 200 || res.data?.yes_no !== 1) {
errorAlert(res);
return;
}
Expand All @@ -168,8 +168,8 @@ const AdminSettings = () => {
};

const dropDatabase = async () => {
const res = await postRequest<OkResponse>('/admin/reset', null);
if (res.status !== 200 || res.data?.ok !== 1) {
const res = await postRequest<YesNoResponse>('/admin/reset', null);
if (res.status !== 200 || res.data?.yes_no !== 1) {
errorAlert(res);
return;
}
Expand Down Expand Up @@ -284,7 +284,7 @@ const AdminSettings = () => {
Update Categories
</Button>

<SubSection>Set Ranking Batch Size</SubSection>
<SubSection>Set Batch Ranking Size (BRS)</SubSection>
<Description>
Set how many projects judges rank at a time (must be at least 2 obviously).
Judges can rank and reorder projects freely before submitting a batch of the specified size.
Expand All @@ -295,17 +295,17 @@ const AdminSettings = () => {
type="number"
min="2"
placeholder="8"
value={rankingBatchSize}
value={batchRankingSize}
onChange={(e) => {
setRankingBatchSize(e.target.value.toString());
setBatchRankingSize(e.target.value.toString());
}}
/>
<Button
type="primary"
onClick={updateRankingBatchSize}
onClick={updateBatchRankingSize}
className="mt-4 w-auto md:w-auto px-4 py-2"
>
Update Ranking Batch Size
Update Batch Ranking Size
</Button>

<Section>Judging Parameters</Section>
Expand Down
Loading

0 comments on commit 34590ff

Please sign in to comment.