Skip to content

Commit

Permalink
[add] models & controllers of Standard & Evaluation (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
TechQuery authored Aug 31, 2024
1 parent 8d27367 commit 5ba7797
Show file tree
Hide file tree
Showing 12 changed files with 296 additions and 83 deletions.
3 changes: 2 additions & 1 deletion ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@
3. [Organizer](source/controller/Organizer.ts)
4. [Announcement](source/controller/Announcement.ts)
5. [Git template](source/controller/GitTemplate.ts)
6. [Questionnaire](source/controller/Questionnaire.ts)
6. [Questionnaire & Standard](source/controller/Questionnaire.ts)
7. [Enrollment](source/controller/Enrollment.ts)
5. Team management
1. [Meta](source/controller/Team.ts)
2. [Member](source/controller/Member.ts)
3. [Work](source/controller/Work.ts)
4. [Evaluation](source/controller/Evaluation.ts)

## Best practice

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@kaiyuanshe/openhackathon-service",
"version": "0.20.0",
"version": "0.21.0",
"license": "LGPL-3.0",
"author": "[email protected]",
"description": "RESTful API service scaffold based on Node.js & TypeScript",
Expand Down
16 changes: 12 additions & 4 deletions source/controller/Enrollment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
Authorized,
Body,
CurrentUser,
ForbiddenError,
Get,
HttpCode,
JsonController,
Expand Down Expand Up @@ -76,16 +77,23 @@ export class EnrollmentController {
async createOne(
@CurrentUser() createdBy: User,
@Param('name') name: string,
@Body() { extensions }: Enrollment
@Body() { form }: Enrollment
) {
const hackathon = await hackathonStore.findOneBy({ name });
const hackathon = await hackathonStore.findOneBy({ name }),
now = Date.now();

if (!hackathon) throw new NotFoundError();

if (
now < +new Date(hackathon.enrollmentStartedAt) ||
now > +new Date(hackathon.enrollmentEndedAt)
)
throw new ForbiddenError('Not in enrollment period');

const saved = await store.save({
createdBy,
hackathon,
extensions,
form,
status: hackathon.autoApprove
? EnrollmentStatus.Approved
: EnrollmentStatus.PendingApproval
Expand All @@ -105,7 +113,7 @@ export class EnrollmentController {
{ status, keywords, pageSize, pageIndex }: EnrollmentFilter
) {
const where = searchConditionOf<Enrollment>(
['extensions'],
['form'],
keywords,
status && { status }
);
Expand Down
91 changes: 91 additions & 0 deletions source/controller/Evaluation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import {
Authorized,
Body,
CurrentUser,
ForbiddenError,
Get,
HttpCode,
JsonController,
NotFoundError,
Param,
Post,
QueryParams
} from 'routing-controllers';
import { ResponseSchema } from 'routing-controllers-openapi';

import {
BaseFilter,
dataSource,
Evaluation,
EvaluationListChunk,
Team,
User
} from '../model';
import { searchConditionOf } from '../utility';
import { ActivityLogController } from './ActivityLog';
import { HackathonController } from './Hackathon';

const store = dataSource.getRepository(Evaluation),
teamStore = dataSource.getRepository(Team);

@JsonController('/hackathon/:name/team/:tid/evaluation')
export class EvaluationController {
@Post()
@Authorized()
@HttpCode(201)
@ResponseSchema(Evaluation)
async createOne(
@CurrentUser() createdBy: User,
@Param('name') name: string,
@Param('tid') tid: number,
@Body() evaluation: Evaluation
) {
const team = await teamStore.findOne({
where: { id: tid },
relations: ['hackathon']
});
if (!team) throw new NotFoundError();

const { hackathon } = team,
now = Date.now();
if (
now < +new Date(hackathon.judgeStartedAt) ||
now > +new Date(hackathon.judgeEndedAt)
)
throw new ForbiddenError('Not in evaluation period');

await HackathonController.ensureJudge(createdBy.id, name);

const saved = await store.save({
...evaluation,
team,
hackathon: team.hackathon,
createdBy
});
await ActivityLogController.logCreate(
createdBy,
'Evaluation',
saved.id
);
return saved;
}

@Get()
@ResponseSchema(EvaluationListChunk)
async getList(
@Param('tid') tid: number,
@QueryParams() { keywords, pageSize, pageIndex }: BaseFilter
) {
const where = searchConditionOf<Evaluation>(
['scores', 'comment'],
keywords,
{ team: { id: tid } }
);
const [list, count] = await store.findAndCount({
where,
skip: pageSize * (pageIndex - 1),
take: pageSize
});
return { list, count };
}
}
5 changes: 5 additions & 0 deletions source/controller/Hackathon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ export class HackathonController {
throw new ForbiddenError();
}

static async ensureJudge(userId: number, hackathonName: string) {
if (!(await StaffController.isJudge(userId, hackathonName)))
throw new ForbiddenError();
}

static async ensureEnrolled(userId: number, hackathonName: string) {
if (!(await EnrollmentController.isEnrolled(userId, hackathonName)))
throw new ForbiddenError();
Expand Down
122 changes: 61 additions & 61 deletions source/controller/Questionnaire.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,104 +2,104 @@ import {
Authorized,
Body,
CurrentUser,
Delete,
Get,
HttpCode,
JsonController,
NotFoundError,
OnNull,
OnUndefined,
Param,
Patch,
Post
Put
} from 'routing-controllers';
import { ResponseSchema } from 'routing-controllers-openapi';

import { dataSource, Hackathon, Questionnaire, User } from '../model';
import { dataSource, Hackathon, Questionnaire, Standard, User } from '../model';
import { ActivityLogController } from './ActivityLog';
import { HackathonController } from './Hackathon';

const store = dataSource.getRepository(Questionnaire),
const questionnaireStore = dataSource.getRepository(Questionnaire),
standardStore = dataSource.getRepository(Standard),
hackathonStore = dataSource.getRepository(Hackathon);

@JsonController('/hackathon/:name/questionnaire')
export class QuestionnaireController {
@Get()
@JsonController('/hackathon/:name')
export class SurveyController {
@Get('/questionnaire')
@OnNull(404)
@ResponseSchema(Questionnaire)
getOne(@Param('name') name: string) {
return store.findOneBy({ hackathon: { name } });
getQuestionnaire(@Param('name') name: string) {
return questionnaireStore.findOneBy({ hackathon: { name } });
}

@Post()
@Put('/questionnaire')
@Authorized()
@HttpCode(201)
@ResponseSchema(Questionnaire)
async createOne(
@CurrentUser() createdBy: User,
async updateQuestionnaire(
@CurrentUser() user: User,
@Param('name') name: string,
@Body() form: Questionnaire
) {
const hackathon = await hackathonStore.findOneBy({ name });

if (!hackathon) throw new NotFoundError();

await HackathonController.ensureAdmin(createdBy.id, name);

const saved = await store.save({ ...form, hackathon, createdBy });

await ActivityLogController.logCreate(
createdBy,
'Questionnaire',
saved.id
);
await HackathonController.ensureAdmin(user.id, name);

const old = await this.getQuestionnaire(name);

const saved = await questionnaireStore.save({
...old,
...form,
hackathon,
...(old ? { updatedBy: user } : { createdBy: user })
});

if (old)
await ActivityLogController.logUpdate(
user,
'Questionnaire',
saved.id
);
else
await ActivityLogController.logCreate(
user,
'Questionnaire',
saved.id
);
return saved;
}

@Patch()
@Get('/standard')
@OnNull(404)
@ResponseSchema(Standard)
getStandard(@Param('name') name: string) {
return standardStore.findOneBy({ hackathon: { name } });
}

@Put('/standard')
@Authorized()
@ResponseSchema(Questionnaire)
async updateOne(
@CurrentUser() updatedBy: User,
@ResponseSchema(Standard)
async updateStandard(
@CurrentUser() user: User,
@Param('name') name: string,
@Body() { extensions }: Questionnaire
@Body() form: Standard
) {
const old = await this.getOne(name);

if (!old) throw new NotFoundError();

await HackathonController.ensureAdmin(updatedBy.id, name);

const saved = await store.save({ extensions, updatedBy });
const hackathon = await hackathonStore.findOneBy({ name });

await ActivityLogController.logUpdate(
updatedBy,
'Questionnaire',
old.id
);
return saved;
}
if (!hackathon) throw new NotFoundError();

@Delete()
@Authorized()
@OnUndefined(204)
async deleteOne(
@CurrentUser() deletedBy: User,
@Param('name') name: string
) {
const old = await this.getOne(name);
await HackathonController.ensureAdmin(user.id, name);

if (!old) throw new NotFoundError();
const old = await this.getStandard(name);

await HackathonController.ensureAdmin(deletedBy.id, name);
const saved = await standardStore.save({
...old,
...form,
hackathon,
...(old ? { updatedBy: user } : { createdBy: user })
});

await store.save({ ...old, deletedBy });
await store.softDelete(old.id);
if (old)
await ActivityLogController.logUpdate(user, 'Standard', saved.id);
else await ActivityLogController.logCreate(user, 'Standard', saved.id);

await ActivityLogController.logDelete(
deletedBy,
'Questionnaire',
old.id
);
return saved;
}
}
7 changes: 5 additions & 2 deletions source/controller/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import { StaffController } from './Staff';
import { OrganizerController } from './Organizer';
import { AnnouncementController } from './Announcement';
import { GitTemplateController } from './GitTemplate';
import { QuestionnaireController } from './Questionnaire';
import { SurveyController } from './Questionnaire';
import { EnrollmentController } from './Enrollment';
import { TeamController } from './Team';
import { TeamMemberController } from './TeamMember';
import { TeamWorkController } from './TeamWork';
import { EvaluationController } from './Evaluation';

export * from './Base';
export * from './User';
Expand All @@ -31,6 +32,7 @@ export * from './Enrollment';
export * from './Team';
export * from './TeamMember';
export * from './TeamWork';
export * from './Evaluation';

export const { swagger, mocker, router } = createAPI({
mock: !isProduct,
Expand All @@ -41,9 +43,10 @@ export const { swagger, mocker, router } = createAPI({
StaffController,
OrganizerController,
EnrollmentController,
QuestionnaireController,
SurveyController,
GitTemplateController,
AnnouncementController,
EvaluationController,
TeamWorkController,
TeamMemberController,
TeamController,
Expand Down
6 changes: 4 additions & 2 deletions source/model/ActivityLog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { GitTemplate } from './GitTemplate';
import { Hackathon } from './Hackathon';
import { Organizer } from './Organizer';
import { PlatformAdmin } from './PlatformAdmin';
import { Questionnaire } from './Questionnaire';
import { Evaluation, Questionnaire, Standard } from './Questionnaire';
import { Staff } from './Staff';
import { Team } from './Team';
import { TeamMember } from './TeamMember';
Expand All @@ -38,10 +38,12 @@ export const LogableTable = {
Announcement,
GitTemplate,
Questionnaire,
Standard,
Enrollment,
Team,
TeamMember,
TeamWork
TeamWork,
Evaluation
};
const LogableTableEnum = Object.fromEntries(
Object.entries(LogableTable).map(([key]) => [key, key])
Expand Down
Loading

0 comments on commit 5ba7797

Please sign in to comment.