Nantes Université

Skip to content
Extraits de code Groupes Projets
Valider dce8356d rédigé par Erwan BOUSSE's avatar Erwan BOUSSE
Parcourir les fichiers

feat: add eval markdown report support

parent e31d5497
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
Pipeline #72647 en échec
......@@ -4,4 +4,7 @@
"source.fixAll.eslint": true
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
},
}
\ No newline at end of file
......@@ -6,11 +6,12 @@
"packages": {
"": {
"name": "@evalassist/evalassist-lib",
"version": "0.3.2",
"version": "0.3.3",
"license": "ISC",
"dependencies": {
"axios": "^1.1.3",
"mkdirp": "^1.0.4"
"mkdirp": "^1.0.4",
"ts-markdown": "^0.3.0-beta.1"
},
"devDependencies": {
"@commitlint/cli": "^17.1.2",
......@@ -24,7 +25,7 @@
"eslint": "^8.25.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
"husky": "^8.0.1",
"husky": "^8.0.0",
"jest": "^29.2.1",
"lint-staged": "^13.0.3",
"prettier": "^2.7.1",
......@@ -5770,9 +5771,9 @@
"dev": true
},
"node_modules/json5": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"bin": {
"json5": "lib/cli.js"
......@@ -6007,9 +6008,9 @@
}
},
"node_modules/lint-staged/node_modules/yaml": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz",
"integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==",
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
"integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
"dev": true,
"engines": {
"node": ">= 14"
......@@ -7933,6 +7934,11 @@
"node": ">=12"
}
},
"node_modules/ts-markdown": {
"version": "0.3.0-beta.1",
"resolved": "https://registry.npmjs.org/ts-markdown/-/ts-markdown-0.3.0-beta.1.tgz",
"integrity": "sha512-AqKx75S/fS0jR0vdDuyqDvMXwevAwE4l7XlOslePrLp7Geig4i8QIbZCK+XGaHUde+MPYpclfGOoyBeQnQh02A=="
},
"node_modules/ts-node": {
"version": "10.9.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
......@@ -12701,9 +12707,9 @@
"dev": true
},
"json5": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true
},
"jsonc-parser": {
......@@ -12855,9 +12861,9 @@
"dev": true
},
"yaml": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz",
"integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==",
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
"integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
"dev": true
}
}
......@@ -14288,6 +14294,11 @@
}
}
},
"ts-markdown": {
"version": "0.3.0-beta.1",
"resolved": "https://registry.npmjs.org/ts-markdown/-/ts-markdown-0.3.0-beta.1.tgz",
"integrity": "sha512-AqKx75S/fS0jR0vdDuyqDvMXwevAwE4l7XlOslePrLp7Geig4i8QIbZCK+XGaHUde+MPYpclfGOoyBeQnQh02A=="
},
"ts-node": {
"version": "10.9.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
......
......@@ -30,7 +30,8 @@
},
"dependencies": {
"axios": "^1.1.3",
"mkdirp": "^1.0.4"
"mkdirp": "^1.0.4",
"ts-markdown": "^0.3.0-beta.1"
},
"devDependencies": {
"@commitlint/cli": "^17.1.2",
......
export interface Evaluator {
analyze(path: string): any
score(analysisResults: any): any
report(analysisResults: any, scores: any): Report
initialize?(configuration: any): void
}
......@@ -17,3 +18,39 @@ export interface CSVMemberLine {
memberID: string
data: any
}
export interface Report {
title: string
date: Date
content: ReportContent[]
}
export class ReportContent { }
export class ReportParagraph extends ReportContent {
constructor(
public content: string) {
super()
}
}
export class ReportList extends ReportContent {
constructor(
public items: string[]) {
super()
}
}
export class ReportUnorderedList extends ReportList {}
export class ReportOrderedList extends ReportList {}
export class ReportSection extends ReportContent {
constructor(
public title: string,
public content: ReportContent[]) {
super()
}
}
\ No newline at end of file
import { Evaluator, CSVProjectLine, CSVMemberLine } from '.'
import { Evaluator, CSVProjectLine, CSVMemberLine, Report, ReportContent, ReportParagraph, ReportSection, ReportList, ReportUnorderedList, ReportOrderedList } from '.'
import { CSVMaker } from '../../core/csv'
import { StorageProvider, Storage } from '../../core/storage'
import { Project, ProjectService } from '../project'
......@@ -12,6 +12,7 @@ import * as fs from 'fs'
import * as YAML from 'yaml'
import { exec } from 'child_process'
import { promisify } from 'util'
import { MarkdownEntry, tsMarkdown } from 'ts-markdown'
export class EvaluationService {
constructor(
......@@ -21,7 +22,7 @@ export class EvaluationService {
private csvWriter: CSVMaker,
private fileWriter: FileWriter,
private dateProvider: DateProvider,
) {}
) { }
getEvaluationDataReader(projectPath: string) {
const storage: Storage = this.storageProvider.forPath(projectPath)
......@@ -275,4 +276,114 @@ export class EvaluationService {
const deleted2 = writer.deleteScore(evaluationName)
return deleted1 || deleted2
}
}
public async reportWithStorage(
projectPath: string,
evaluator: Evaluator,
evaluationName: string,
overwrite = false,
): Promise<boolean> {
const writer = this.getEvaluationDataWriter(projectPath)
const reader = this.getEvaluationDataReader(projectPath)
if (reader.readReportData(evaluationName) && !overwrite) {
return false
}
const result = reader.readAnalysisResult(evaluationName)
if (result === null) {
throw new Problem(`There are no analysis results named '${evaluationName}' in this project.`)
}
const score = reader.readScore(evaluationName)
if (result === null) {
throw new Problem(`There are no scores named '${evaluationName}' in this project.`)
}
const reportData = await evaluator.report(result, score)
writer.writeReportData(evaluationName, reportData)
return true
}
private depthToMarkdownSection(depth: number, title: string): MarkdownEntry {
switch (depth) {
case (1): return { h1: title }
case (2): return { h2: title }
case (3): return { h3: title }
case (4): return { h4: title }
case (5): return { h5: title }
case (6): return { h6: title }
}
throw "Invalid depth"
}
private reportContentToMarkdown(reportContent: ReportContent, currentDepth: number): MarkdownEntry[] {
if (reportContent instanceof ReportParagraph) {
return [{ p: reportContent.content }]
} else if (reportContent instanceof ReportUnorderedList) {
return [{ ul: reportContent.items }]
} else if (reportContent instanceof ReportOrderedList) {
return [{ ol: reportContent.items }]
} else if (reportContent instanceof ReportSection) {
const entries: MarkdownEntry[] = []
entries.push(this.depthToMarkdownSection(currentDepth, reportContent.title))
for (let x of reportContent.content) {
entries.push(this.reportContentToMarkdown(x, currentDepth + 1))
}
return entries
}
throw "Unmanaged ReportContent kind. "
}
private extractProjectMarkdownData(report: Report, projectData: Project, members: Member[]): Array<MarkdownEntry> {
const entries: Array<MarkdownEntry> = []
entries.push({ h1: report.title })
entries.push({ p: report.date })
entries.push({ p: members.map((m) => m.name + "(" + m.username + ")").join(', '), })
for (let contentEntry of report.content) {
entries.push(this.reportContentToMarkdown(contentEntry, 2))
}
return entries
}
private generateMarkdownWithStorageSingle(
folderFullPath: string,
evaluationName: string,
membersToHide: string[]
): void {
const evaluationReader = this.getEvaluationDataReader(folderFullPath)
const projectReader = this.projectService.getProjectDataReader(folderFullPath)
const memberReader = this.memberService.getProjectDataReader(folderFullPath)
const score: any = evaluationReader.readScore(evaluationName)
if (score === null) {
throw new Problem(`There are no scoring results to use in the project: ${folderFullPath}.`)
}
const projectData: Project = projectReader.project()
const members: Member[] = memberReader
.members()
.filter((m1) => !membersToHide.map((m2) => m2.toLowerCase()).includes(m1.username.toLowerCase()))
const markdownData: MarkdownEntry[] = this.extractProjectMarkdownData(score, projectData, members)
const markdownContent: string = tsMarkdown(markdownData)
const writer = this.getEvaluationDataWriter(folderFullPath)
writer.writeReportMarkdown(evaluationName, markdownContent)
}
public generateMarkdownWithStorage(
projectsFolders: string[],
evaluationName: string,
membersToHide: string[] = [],
) {
for (const folderFullPath of projectsFolders) {
this.generateMarkdownWithStorageSingle(folderFullPath, evaluationName, membersToHide)
}
}
}
\ No newline at end of file
export const EvaluationAnalysisResults = 'evaluation/analysisResults/'
export const EvaluationScores = 'evaluation/scores/'
export const EvaluationDockerResults = 'evaluation/tmp/'
export const EvaluationReportData = 'evaluation/reportData/'
export const EvaluationReports = 'evaluation/reports/'
import { Storage } from '../../../core/storage'
import { EvaluationAnalysisResults, EvaluationDockerResults, EvaluationScores } from './evaluation.storage.keys'
import { Report } from '../evaluation.interface'
import { EvaluationAnalysisResults, EvaluationDockerResults, EvaluationReports, EvaluationScores } from './evaluation.storage.keys'
export class EvaluationStorageReader {
constructor(private storage: Storage) {}
readDockerAnalysisResult(): any {
return this.storage.get(`${EvaluationDockerResults}` + 'analysisResults')
}
readDockerScore(): any {
return this.storage.get(`${EvaluationDockerResults}` + 'scores')
}
readAnalysisResult(evaluationName: string): any {
return this.storage.get(`${EvaluationAnalysisResults}${evaluationName}`)
}
readScore(evaluationName: string): any {
return this.storage.get(`${EvaluationScores}${evaluationName}`) as any
}
readReportData(evaluationName: string) : Report {
return this.storage.get(`${EvaluationReports}${evaluationName}`) as Report
}
listEntries(): Array<string> {
return this.storage.list(EvaluationAnalysisResults)
......
import * as fs from 'fs'
import { Storage } from '../../../core/storage'
import { EvaluationAnalysisResults, EvaluationDockerResults, EvaluationScores } from './evaluation.storage.keys'
import { EvaluationAnalysisResults, EvaluationDockerResults, EvaluationReports as EvaluationReportData, EvaluationScores } from './evaluation.storage.keys'
import { Report } from '../evaluation.interface'
export class EvaluationStorageWriter {
constructor(private storage: Storage) {}
writeScore(evaluationName: string, evaluationResults: any) {
......@@ -13,6 +15,14 @@ export class EvaluationStorageWriter {
this.storage.set({ key: `${EvaluationAnalysisResults}${evaluationName}`, data: result })
}
writeReportData(evaluationName: string, reportData: Report) {
this.storage.set({ key: `${EvaluationReportData}${evaluationName}`, data: reportData })
}
writeReportMarkdown(evaluationName: string, content: string) {
this.storage.setRaw(`${EvaluationReportData}${evaluationName}.md`, content)
}
deleteScore(evaluationName: string): boolean {
return this.storage.clear(`${EvaluationScores}${evaluationName}`)
}
......
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter