Skip to content

Commit

Permalink
Merge pull request #107 from ChubachiPT2024/feature/classify-api
Browse files Browse the repository at this point in the history
#96 分類用 API の実装
  • Loading branch information
a2311kk authored Jul 9, 2024
2 parents 1394781 + 64b10a8 commit 7d3d2dd
Show file tree
Hide file tree
Showing 13 changed files with 207 additions and 7 deletions.
5 changes: 5 additions & 0 deletions src/@types/interface.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { AssessmentClassifyCommand } from 'src/application/assessments/assessmentClassifyCommand'
import { CourseGetCommand } from 'src/application/courses/courseGetCommand'
import { ReportListGetResult } from 'src/application/reportLists/reportListGetResult'
import { ReportListImportCommand } from 'src/application/reportLists/reportListImportCommand'
Expand All @@ -21,6 +22,10 @@ export interface IElectronAPI {
getCourseAsync: (
courseGetCommand: CourseGetCommand
) => Promise<CourseGetResult>

classifyAssessmentAsync: (
assessmentClassifyCommand: AssessmentClassifyCommand
) => Proimse<void>
}

declare global {
Expand Down
32 changes: 32 additions & 0 deletions src/application/assessments/assessmentApplicationService.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Assessment } from 'src/domain/models/assessments/assessment'
import { InMemoryAssessmentRepository } from 'src/infrastructure/inMemory/assessments/inMemoryAssessmentRepository'
import { describe, expect, test } from 'vitest'
import { AssessmentApplicationService } from './assessmentApplicationService'
import { AssessmentClassifyCommand } from './assessmentClassifyCommand'

describe('classify', () => {
test('The grade and rank are set after classification.', async () => {
// Arrange
const repository = new InMemoryAssessmentRepository()
const assessment = new Assessment(0, 0)
await repository.saveAsync(assessment)
const service = new AssessmentApplicationService(repository)
const command = new AssessmentClassifyCommand(
assessment.reportId,
assessment.studentNumId,
1,
2
)

// Act
await service.classifyAsync(command)

// Assert
const classified = await repository.findAsync(
assessment.reportId,
assessment.studentNumId
)
expect(classified.grade).toBe(1)
expect(classified.rank).toBe(2)
})
})
34 changes: 34 additions & 0 deletions src/application/assessments/assessmentApplicationService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { AssessmentRepository } from 'src/domain/models/assessments/assessmentRepository'
import { AssessmentClassifyCommand } from './assessmentClassifyCommand'

/**
* 個別評価アプリケーションサービス
*/
export class AssessmentApplicationService {
/**
* コンストラクタ
*
* @param assessmentRepository 個別評価リポジトリ
*/
public constructor(
private readonly assessmentRepository: AssessmentRepository
) {}

/**
* 個別評価を分類する
*
* @param command 個別評価分類コマンド
*/
public async classifyAsync(
command: AssessmentClassifyCommand
): Promise<void> {
const assessment = await this.assessmentRepository.findAsync(
command.reportId,
command.studentNumId
)

const classified = assessment.classify(command.grade, command.rank)

await this.assessmentRepository.saveAsync(classified)
}
}
19 changes: 19 additions & 0 deletions src/application/assessments/assessmentClassifyCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* 個別評価分類コマンド
*/
export class AssessmentClassifyCommand {
/**
* コンストラクタ
*
* @param reportId レポート ID
* @param studentNumId 学籍番号
* @param grade 評点
* @param rank 評点内の位置
*/
public constructor(
public readonly reportId: number,
public readonly studentNumId: number,
public readonly grade: number,
public readonly rank: number
) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ describe('import', () => {

await service.importAsync(command)

const assessments = await assessmentRepository.findAsync(35677)
const assessments = await assessmentRepository.findByReportIdAsync(35677)
const assessment = assessments.find((x) => x.studentNumId === 23745148)

expect(assessment.reportId).toBe(35677)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export class ReportListApplicationService {
const submissionMap = new Map(submissions.map((x) => [x.studentNumId, x]))

// 対象のレポートについて、学籍番号を Key, 個別評価を Value とする Map を作成
const assessments = await this.assessmentRepository.findAsync(
const assessments = await this.assessmentRepository.findByReportIdAsync(
command.reportId
)
const assessmentMap = new Map(assessments.map((x) => [x.studentNumId, x]))
Expand Down
16 changes: 16 additions & 0 deletions src/domain/models/assessments/assessment.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { describe, expect, test } from 'vitest'
import { Assessment } from './assessment'

describe('classify', () => {
test('The grade and rank are set after classification', async () => {
// Arrange
const assessment = new Assessment(0, 0)

// Act
const classified = assessment.classify(1, 2)

// Assert
expect(classified.grade).toBe(1)
expect(classified.rank).toBe(2)
})
})
19 changes: 19 additions & 0 deletions src/domain/models/assessments/assessment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,23 @@ export class Assessment {
throw new TypeError('The score must be in [0, 100].')
}
}

/**
* 分類する
*
* @param grade 評点
* @param rank 評点内の位置
* @returns 分類後の個別評価
*/
public classify(grade: number, rank: number) {
return new Assessment(
this.reportId,
this.studentNumId,
this.feedback,
this.memo,
grade,
rank,
this.score
)
}
}
12 changes: 10 additions & 2 deletions src/domain/models/assessments/assessmentRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,18 @@ export interface AssessmentRepository {
saveAsync(assessment: Assessment): Promise<void>

/**
* 個別評価を検索する
* 個別評価をレポート ID で検索する
*
* @param reportId レポート ID
* @returns 個別評価
*/
findAsync(reportId: number): Promise<Assessment[]>
findByReportIdAsync(reportId: number): Promise<Assessment[]>

/**
* 個別評価を検索する
*
* @param reportId レポート ID
* @param studentNumId 学籍番号
*/
findAsync(reportId: number, studentNumId: number): Promise<Assessment>
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('save', () => {
const expected = new Assessment(1, 2)
await repository.saveAsync(expected)

const actual = await repository.findAsync(expected.reportId)
const actual = await repository.findByReportIdAsync(expected.reportId)

expect(actual.length).toBe(1)
expect(actual[0].reportId).toBe(expected.reportId)
Expand All @@ -20,3 +20,37 @@ describe('save', () => {
expect(actual[0].score).toBe(expected.score)
})
})

describe('find', () => {
test('The saved assessment is found.', async () => {
// Arrange
const repository = new InMemoryAssessmentRepository()
const expected = new Assessment(1, 2)
await repository.saveAsync(expected)

// Act
const actual = await repository.findAsync(
expected.reportId,
expected.studentNumId
)

// Assert
expect(actual.reportId).toBe(expected.reportId)
expect(actual.studentNumId).toBe(expected.studentNumId)
expect(actual.feedback).toBe(expected.feedback)
expect(actual.memo).toBe(expected.memo)
expect(actual.grade).toBe(expected.grade)
expect(actual.rank).toBe(expected.rank)
expect(actual.score).toBe(expected.score)
})

test('The unsaved assessment is not found.', async () => {
// Arrange
const repository = new InMemoryAssessmentRepository()
const expected = new Assessment(1, 2)
await repository.saveAsync(expected)

// Act, Assert
expect(() => repository.findAsync(3, 4)).rejects.toThrowError()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,28 @@ export class InMemoryAssessmentRepository implements AssessmentRepository {
}

/**
* 個別評価を検索する
* 個別評価をレポート ID で検索する
*
* @param reportId レポート ID
* @returns 個別評価
*/
async findAsync(reportId: number): Promise<Assessment[]> {
async findByReportIdAsync(reportId: number): Promise<Assessment[]> {
const map = this.assessments.get(reportId) ?? new Map<number, Assessment>()
return [...map.values()]
}

/**
* 個別評価を検索する
*
* @param reportId レポート ID
* @param studentNumId 学籍番号
* @returns 個別評価
*/
async findAsync(reportId: number, studentNumId: number): Promise<Assessment> {
const assessment = this.assessments.get(reportId)?.get(studentNumId)
if (!assessment) {
throw new Error('The assessment is not found.')
}
return assessment
}
}
13 changes: 13 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { CourseApplicationService } from './application/courses/courseApplicatio
import { InMemorySubmissionRepository } from './infrastructure/inMemory/submissions/inMemorySubmissionRepository'
import { InMemoryAssessmentRepository } from './infrastructure/inMemory/assessments/inMemoryAssessmentRepository'
import { ReportListGetCommand } from './application/reportLists/reportListGetCommand'
import { AssessmentClassifyCommand } from './application/assessments/assessmentClassifyCommand'
import { AssessmentApplicationService } from './application/assessments/assessmentApplicationService'

// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) {
Expand Down Expand Up @@ -58,6 +60,9 @@ const reportListApplicationService = new ReportListApplicationService(
)
const reportApplicationService = new ReportApplicationService(reportRepository)
const courseApplicationService = new CourseApplicationService(courseRepository)
const assessmentApplicationService = new AssessmentApplicationService(
assessmentRepository
)

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
Expand Down Expand Up @@ -87,6 +92,14 @@ app.whenReady().then(() => {
await courseApplicationService.getAsync(courseGetCommand)
)

ipcMain.handle(
'classifyAssessmentAsync',
async (_, assessmentClassifyCommand: AssessmentClassifyCommand) =>
await assessmentApplicationService.classifyAsync(
assessmentClassifyCommand
)
)

createWindow()
})

Expand Down
5 changes: 5 additions & 0 deletions src/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ReportListImportCommand } from './application/reportLists/reportListImp
import { ReportGetCommand } from './application/reports/reportGetCommand'
import { CourseGetCommand } from './application/courses/courseGetCommand'
import { ReportListGetCommand } from './application/reportLists/reportListGetCommand'
import { AssessmentClassifyCommand } from './application/assessments/assessmentClassifyCommand'

contextBridge.exposeInMainWorld('electronAPI', {
importReportListAsync: (reportListImportCommand: ReportListImportCommand) =>
Expand All @@ -20,4 +21,8 @@ contextBridge.exposeInMainWorld('electronAPI', {

getCourseAsync: (courseGetCommand: CourseGetCommand) =>
ipcRenderer.invoke('getCourseAsync', courseGetCommand),

classifyAssessmentAsync: (
assessmentClassifyCommand: AssessmentClassifyCommand
) => ipcRenderer.invoke('classifyAssessmentAsync', assessmentClassifyCommand),
})

0 comments on commit 7d3d2dd

Please sign in to comment.