diff --git a/package-lock.json b/package-lock.json index 3d9dab56..be1ec2cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "@ngx-translate/core": "15.0.0", "@ngx-translate/http-loader": "^8.0.0", "@popperjs/core": "2.11.8", + "@supercharge/promise-pool": "^3.1.0", "@svgdotjs/svg.js": "3.2.0", "@tensorflow/tfjs": "^4.13.0", "@tensorflow/tfjs-backend-wasm": "^4.13.0", @@ -5088,6 +5089,14 @@ "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", "dev": true }, + "node_modules/@supercharge/promise-pool": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@supercharge/promise-pool/-/promise-pool-3.1.0.tgz", + "integrity": "sha512-gB3NukbIcYzRtPoE6dx9svQYPodxvnfQlaaQd8N/z87E6WaMfRE7o5HwB+LZ+KeM0nsNAq1n4TmBtfz1VCUR+Q==", + "engines": { + "node": ">=8" + } + }, "node_modules/@svgdotjs/svg.js": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@svgdotjs/svg.js/-/svg.js-3.2.0.tgz", diff --git a/package.json b/package.json index 4a5d5755..6bc58601 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "@ngx-translate/core": "15.0.0", "@ngx-translate/http-loader": "^8.0.0", "@popperjs/core": "2.11.8", + "@supercharge/promise-pool": "^3.1.0", "@svgdotjs/svg.js": "3.2.0", "@tensorflow/tfjs": "^4.13.0", "@tensorflow/tfjs-backend-wasm": "^4.13.0", diff --git a/src/main/webapp/app/scanexam/alignscan/alignscan.component.html b/src/main/webapp/app/scanexam/alignscan/alignscan.component.html index 51fc2fa0..2ca2029d 100644 --- a/src/main/webapp/app/scanexam/alignscan/alignscan.component.html +++ b/src/main/webapp/app/scanexam/alignscan/alignscan.component.html @@ -52,6 +52,20 @@

Layout options

+
+ + + +
+

Téléchargement pdfs

template.pdf diff --git a/src/main/webapp/app/scanexam/alignscan/alignscan.component.ts b/src/main/webapp/app/scanexam/alignscan/alignscan.component.ts index 4c76aa37..9104c6e1 100644 --- a/src/main/webapp/app/scanexam/alignscan/alignscan.component.ts +++ b/src/main/webapp/app/scanexam/alignscan/alignscan.component.ts @@ -30,6 +30,7 @@ import { QuestionService } from '../../entities/question/service/question.servic import { ZoneService } from '../../entities/zone/service/zone.service'; import { TemplateService } from 'app/entities/template/service/template.service'; import { ScanService } from 'app/entities/scan/service/scan.service'; +import PromisePool from '@supercharge/promise-pool'; export interface IPage { image?: ArrayBuffer; @@ -80,8 +81,6 @@ export class AlignScanComponent implements OnInit, CacheUploadNotification { blocked = false; observable: Observable | undefined; observer: Subscriber | undefined; - observablePage: Observable | undefined; - observerPage: Subscriber | undefined; allowPartialAlign = false; message = ''; @@ -90,6 +89,7 @@ export class AlignScanComponent implements OnInit, CacheUploadNotification { scale = 2; nbPageAlignInCache = 0; activeIndex = 1; + stepPage = 50; responsiveOptions2: any[] = [ { breakpoint: '1500px', @@ -114,9 +114,9 @@ export class AlignScanComponent implements OnInit, CacheUploadNotification { showMapping = false; layoutsidebarVisible = false; - nbreCore = navigator.hardwareConcurrency - 2; + nbreCore = (navigator.hardwareConcurrency - 2) / 2 > 0 ? (navigator.hardwareConcurrency - 2) / 2 : 1; nbreCoreMax = navigator.hardwareConcurrency - 2; - + resolves: Map = new Map(); constructor( public examService: ExamService, protected activatedRoute: ActivatedRoute, @@ -175,6 +175,7 @@ export class AlignScanComponent implements OnInit, CacheUploadNotification { this.allowPartialAlign = p > 0; // && p1 > 0 && p === p1; } + processpage: number[] = []; initPool(): void { this.observable = new Observable(observer => { this.observer = observer; @@ -184,9 +185,13 @@ export class AlignScanComponent implements OnInit, CacheUploadNotification { selectTransferables: input => [input.imageA], workerCount: this.nbreCore, fallbackWorkerCount: 3, - }).subscribe( - (e: IImageAlignement) => { + }).subscribe({ + next: (e: IImageAlignement) => { const im = new ImageData(new Uint8ClampedArray(e.imageAligned!), e.imageAlignedWidth!, e.imageAlignedHeight); + + // this.submessage = '' + this.avancement + this.avancementunit; + this.processpage.push(e.pageNumber!); + e.imageAligned = undefined; if (e.imagesDebugTracesWidth !== undefined) { const dim = new ImageData(new Uint8ClampedArray(e.imagesDebugTraces!), e.imagesDebugTracesWidth!, e.imagesDebugTracesHeight); @@ -198,28 +203,27 @@ export class AlignScanComponent implements OnInit, CacheUploadNotification { title: 'Exam', }); } + /* this.resolves.get(e.pageNumber!)() + this.resolves.delete(e.pageNumber!) + this.avancement = this.currentPageAlignOver; + this.currentPageAlignOver = this.currentPageAlignOver + 1; + im.data.set([]);*/ this.saveEligneImage(e.pageNumber!, im).then(() => { im.data.set([]); - if (this.currentPageAlign < this.numberPagesInScan + 1) { - this.observerPage?.next(this.currentPageAlign); - this.currentPageAlign = this.currentPageAlign + 1; - } - if (this.currentPageAlignOver < this.numberPagesInScan) { - this.avancement = this.currentPageAlignOver; - this.currentPageAlignOver = this.currentPageAlignOver + 1; - } else { - this.avancement = this.currentPageAlignOver; - this.currentPageAlignOver = this.currentPageAlignOver + 1; - this.observerPage?.complete(); - this.observer?.complete(); - } + this.resolves.get(e.pageNumber!)(); + this.resolves.delete(e.pageNumber!); + this.avancement = this.currentPageAlignOver; + this.currentPageAlignOver = this.currentPageAlignOver + 1; + + // this.progress = ; }); }, - err => { + // eslint-disable-next-line object-shorthand + error: err => { console.log(err); }, - ); + }); } async removeElement(examId: number): Promise { @@ -265,9 +269,8 @@ export class AlignScanComponent implements OnInit, CacheUploadNotification { } await this.load(); await this.loadTemplatePage(); - this.initPool(); - this.exportAsImage(); + await this.exportAsImage(); } async loadTemplatePage(): Promise { return new Promise(resolve => { @@ -312,6 +315,7 @@ export class AlignScanComponent implements OnInit, CacheUploadNotification { this.partialAlign = false; this.showMapping = false; this.startPage = 1; + this.processpage = []; this.currentPageAlign = 1; this.endPage = value; this.numberPagesInScan = value; @@ -338,38 +342,96 @@ export class AlignScanComponent implements OnInit, CacheUploadNotification { this.router.navigateByUrl('/exam/' + this.examId); } - public exportAsImage(): void { - this.observablePage = new Observable(observer => { - this.observerPage = observer; - }); + public async exportAsImage(): Promise { + const stepPage = this.stepPage; + let nbrePageToProcess = this.numberPagesInScan; + if (this.partialAlign) { + nbrePageToProcess = this.numberPagesInScan - this.startPage + 1; + } - this.observablePage.subscribe( - e => { - this.alignPage(e); - }, - err => console.log(err), - () => { - this.saveData(); - }, - ); + console.error(nbrePageToProcess); - if (this.partialAlign) { - this.currentPageAlign = this.startPage; - } else { - this.startPage = 1; - this.currentPageAlign = 1; + let count = Math.floor(nbrePageToProcess / stepPage); + for (let k = 0; k < count; k++) { + const pagesnumber: number[] = []; + if (this.alignement !== 'off') { + this.initPool(); + } + if (this.partialAlign) { + this.currentPageAlignOver = this.startPage; + this.avancement = this.startPage; + for (let i = 1 + k * stepPage; i <= (k + 1) * stepPage; i++) { + if (i >= this.startPage) { + pagesnumber.push(i); + } + } + } else { + this.currentPageAlignOver = 1 + k * stepPage; + this.avancement = 1 + k * stepPage; + for (let i = 1 + k * stepPage; i <= (k + 1) * stepPage; i++) { + pagesnumber.push(i); + } + } + await PromisePool.for(pagesnumber) + .withConcurrency(this.nbreCore) + /* .onTaskFinished((page) => { + console.error('end send page to process ', page); + })*/ + .process( + async page => + new Promise(resolve => { + this.alignPage(page); + this.resolves.set(page, resolve); + }), + ); + if (this.alignement !== 'off') { + this.observer?.complete(); + } + } + if (this.alignement !== 'off') { + this.initPool(); } + const count2 = nbrePageToProcess % stepPage; + console.error(count2); + let count1 = Math.floor(nbrePageToProcess / stepPage); + console.error(count1); + if (count2 > 0) { + const pagesnumber: number[] = []; - while ( - this.currentPageAlign < this.startPage + Math.floor(this.nbreCore * 1.5) /* (navigator.hardwareConcurrency - 1) /* * 2 */ && - this.currentPageAlign < this.numberPagesInScan + 1 - ) { - this.observerPage!.next(this.currentPageAlign); - this.currentPageAlign = this.currentPageAlign + 1; + if (this.partialAlign) { + this.currentPageAlignOver = this.startPage; + this.avancement = this.startPage; + for (let i = 1 + count1 * stepPage; i <= count1 * stepPage + count2; i++) { + if (i >= this.startPage) { + pagesnumber.push(i); + } + } + } else { + this.currentPageAlignOver = 1 + count1 * stepPage; + this.avancement = 1 + count1 * stepPage; + for (let i = 1 + count1 * stepPage; i <= count1 * stepPage + count2; i++) { + pagesnumber.push(i); + } + } + console.error(pagesnumber); + await PromisePool.for(pagesnumber) + .withConcurrency(this.nbreCore) + .process( + async page => + new Promise(resolve => { + this.alignPage(page); + this.resolves.set(page, resolve); + }), + ); + if (this.alignement !== 'off') { + this.observer?.complete(); + } } + + this.saveData(); } - public async alignPage(page: number): Promise { + public async alignPage(page: number): Promise { if (this.alignement === 'off') { const dataURL = await this.db.getFirstNonAlignImage(+this.examId, page); if (dataURL !== undefined) { @@ -378,31 +440,23 @@ export class AlignScanComponent implements OnInit, CacheUploadNotification { pageNumber: page, value: dataURL!.value, }); - if (this.currentPageAlign < this.numberPagesInScan + 1) { - this.observerPage?.next(this.currentPageAlign); - this.currentPageAlign = this.currentPageAlign + 1; - } - if (this.currentPageAlignOver < this.numberPagesInScan) { - this.avancement = this.currentPageAlignOver; - this.currentPageAlignOver = this.currentPageAlignOver + 1; - } else { - this.avancement = this.currentPageAlignOver; - this.currentPageAlignOver = this.currentPageAlignOver + 1; - this.observerPage?.complete(); - this.observer?.complete(); - } - return page; + this.avancement = this.currentPageAlignOver; + this.currentPageAlignOver = this.currentPageAlignOver + 1; + this.resolves.get(page)(); + this.resolves.delete(page); + + return; } else { this.messageService.add({ severity: 'error', summary: this.translateService.instant('scanexam.npagealign'), detail: this.translateService.instant('scanexam.npagealigndetails') + page, }); - return page; + return; } } else { - const p = this.aligneImages(page); - return p! + 1; + this.aligneImages(page); + return; } } @@ -429,7 +483,7 @@ export class AlignScanComponent implements OnInit, CacheUploadNotification { i.src = file; } - aligneImages(pagen: number): number { + aligneImages(pagen: number): void { let paget = pagen % this.nbreFeuilleParCopie; if (paget === 0) { paget = this.nbreFeuilleParCopie; @@ -449,7 +503,7 @@ export class AlignScanComponent implements OnInit, CacheUploadNotification { indexDb: this.preferenceService.getPreference().cacheDb === 'indexdb', }); } - return pagen; + return; } private imageDataToDataURL(img: ImageData): string { diff --git a/src/main/webapp/app/scanexam/associer-copies-etudiants/allbindings/allbindings.component.ts b/src/main/webapp/app/scanexam/associer-copies-etudiants/allbindings/allbindings.component.ts index 035677dd..e33ae832 100644 --- a/src/main/webapp/app/scanexam/associer-copies-etudiants/allbindings/allbindings.component.ts +++ b/src/main/webapp/app/scanexam/associer-copies-etudiants/allbindings/allbindings.component.ts @@ -6,7 +6,6 @@ import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; import { ExamSheetService } from '../../../entities/exam-sheet/service/exam-sheet.service'; import { firstValueFrom } from 'rxjs'; import { StudentService } from 'app/entities/student/service/student.service'; -import { v4 as uuid } from 'uuid'; @Component({ selector: 'jhi-allbindings', @@ -83,52 +82,37 @@ export class AllbindingsComponent implements OnInit { } async bindStudentInternal(student: IStudent, currentStudent: number, element: any): Promise { - let examSheet4CurrentStudentId: (number | undefined)[] = []; - if (student.examSheets) { - examSheet4CurrentStudentId = student.examSheets.filter((ex: any) => ex?.scanId === this.exam.scanfileId).map(ex1 => ex1.id); + console.error(this.students); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + const examSheet4CurrentStudent1: IExamSheet[] = [...new Set(this.students.flatMap(s => s.recognizedStudent.examSheets!))].filter( + sh => + sh.pagemin === currentStudent * this.nbreFeuilleParCopie && + sh.pagemax === (currentStudent + 1) * this.nbreFeuilleParCopie - 1 && + sh.scanId === this.exam.scanfileId, + ); + // ExamSheetExist + if (examSheet4CurrentStudent1.length === 1) { + const sheet = examSheet4CurrentStudent1[0]; + sheet.students = [student]; + await firstValueFrom(this.sheetService.updateStudents(sheet.id!, [student.id!])); + element.bound = true; } - // Récupère la sheet courante. - const examSheet4CurrentPage: IExamSheet[] = ( - this.students - .filter( - s => - s.examSheets?.some( - (ex: any) => ex?.scanId === this.exam.scanfileId && ex.pagemin === currentStudent * this.nbreFeuilleParCopie, - ), + // ExamSheetNotExist + else { + const sheets: IExamSheet[] | null = ( + await firstValueFrom( + this.sheetService.query({ + scanId: this.exam.scanfileId, + pagemin: currentStudent * this.nbreFeuilleParCopie, + pagemax: (currentStudent + 1) * this.nbreFeuilleParCopie - 1, + }), ) - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - .map(s => s.examSheets) as any - ).flat(); - // Passe cette sheet à -1 -1. sémantique plus associé - - for (const ex of examSheet4CurrentPage.filter(ex2 => !examSheet4CurrentStudentId.includes(ex2.id))) { - ex.pagemin = -1; - ex.pagemax = -1; - await firstValueFrom(this.sheetService.update(ex)); - } - - // Pour l'étudiant sélectionné. récupère la sheet. Si elle exite, on met à jour les bonnes pages sinon on crée la page. - - const examS4Student = student.examSheets?.filter((ex: IExamSheet) => ex.scanId === this.exam.scanfileId); - if (examS4Student !== undefined && examS4Student.length > 0) { - for (const ex of examS4Student) { - ex.pagemin = currentStudent * this.nbreFeuilleParCopie; - ex.pagemax = (currentStudent + 1) * this.nbreFeuilleParCopie - 1; - await firstValueFrom(this.sheetService.update(ex)); + ).body; + if (sheets !== null && sheets.length === 1) { + await firstValueFrom(this.sheetService.updateStudents(sheets[0].id!, [student.id!])); + element.bound = true; } - } else { - const sheet: IExamSheet = { - name: uuid(), - pagemin: currentStudent * this.nbreFeuilleParCopie, - pagemax: (currentStudent + 1) * this.nbreFeuilleParCopie - 1, - scanId: this.exam.scanfileId, - students: [student], - }; - const e = await firstValueFrom(this.sheetService.create(sheet)); - student.examSheets?.push(e.body!); - await firstValueFrom(this.studentService.update(student)); } - element.bound = true; } async bindAllStudent(element: any): Promise { @@ -141,6 +125,7 @@ export class AllbindingsComponent implements OnInit { await this.bindStudentInternal(s.recognizedStudent, s.currentStudent, s); } } catch (e) { + console.error(e); console.error('could not bind ', s.recognizedStudent); } } diff --git a/src/main/webapp/app/scanexam/chargerscan/chargerscan.component.html b/src/main/webapp/app/scanexam/chargerscan/chargerscan.component.html index 26938b61..13d5ad96 100644 --- a/src/main/webapp/app/scanexam/chargerscan/chargerscan.component.html +++ b/src/main/webapp/app/scanexam/chargerscan/chargerscan.component.html @@ -68,7 +68,7 @@

-
+
@@ -107,7 +107,7 @@

diff --git a/src/main/webapp/app/scanexam/chargerscan/chargerscan.component.ts b/src/main/webapp/app/scanexam/chargerscan/chargerscan.component.ts index 0c777bc7..30309c78 100644 --- a/src/main/webapp/app/scanexam/chargerscan/chargerscan.component.ts +++ b/src/main/webapp/app/scanexam/chargerscan/chargerscan.component.ts @@ -16,7 +16,7 @@ import { ExamService } from 'app/entities/exam/service/exam.service'; import { IScan, Scan } from 'app/entities/scan/scan.model'; // import { AlertError } from 'app/shared/alert/alert-error.model'; import { MessageService, ConfirmationService } from 'primeng/api'; -import { finalize, firstValueFrom, Observable, scan, Subscriber } from 'rxjs'; +import { finalize, firstValueFrom, Observable, scan } from 'rxjs'; import { ScanService } from '../../entities/scan/service/scan.service'; import { IExam } from '../../entities/exam/exam.model'; import { CacheServiceImpl } from '../db/CacheServiceImpl'; @@ -27,6 +27,7 @@ import { QuestionService } from 'app/entities/question/service/question.service' import { ZoneService } from 'app/entities/zone/service/zone.service'; import { PreferenceService } from '../preference-page/preference.service'; import { ViewandreorderpagesComponent } from '../viewandreorderpages/viewandreorderpages.component'; +import { PromisePool } from '@supercharge/promise-pool'; interface Upload { progress: number; @@ -75,13 +76,14 @@ export class ChargerscanComponent implements OnInit { editForm: UntypedFormGroup; progress = 0; pageInTemplate = 0; - pageInScan = 0; + pageAlreadyScan = 0; message = ''; reloadScan = false; merge = true; saveTemplate = true; - observablePage: Observable | undefined; - observerPage: Subscriber | undefined; + loaded = false; + // observablePage: Observable | undefined; + // observerPage: Subscriber | undefined; @ViewChild('viewpage') viewcomponent!: ViewandreorderpagesComponent; @@ -110,7 +112,6 @@ export class ChargerscanComponent implements OnInit { // private editedImage: HTMLCanvasElement | undefined; templatePages: Map = new Map(); phase1 = false; - loaded = false; alignement = 'marker'; alignementOptions = [ { label: 'Off', value: 'off' }, @@ -187,7 +188,7 @@ export class ChargerscanComponent implements OnInit { } async init(): Promise { const p = await this.db.countNonAlignImage(+this.examid!); - this.pageInScan = p; + this.pageAlreadyScan = p; const templatePage = await this.db.countPageTemplate(+this.examid!); this.pageInTemplate = templatePage; } @@ -288,7 +289,11 @@ export class ChargerscanComponent implements OnInit { } protected onSaveSuccess(): void { - this.initCacheProcessing(false); + if (this.merge) { + this.initCacheProcessing(false); + } else { + this.initCacheProcessing(true); + } this.reloadScan = false; // this.gotoUE(); } @@ -369,22 +374,25 @@ export class ChargerscanComponent implements OnInit { } const e1 = await firstValueFrom(this.templateService.getPdf(this.exam.templateId)); this.blob1 = e1; + this.loaded = true; } } - + i = 1; public async pdfloaded(): Promise { - if (!this.phase1) { - this.nbreFeuilleParCopie = this.pdfService.numberOfPages(); - } - this.loaded = true; - if (this.phase1) { - if (this.pdfService.numberOfPages() !== 0) { - // Change if partial update - this.numberPagesInScan = this.pdfService.numberOfPages(); - this.avancementunit = ' / ' + this.numberPagesInScan; + if (this.loaded && this.pdfService.numberOfPages() !== 0) { + if (!this.phase1) { + this.nbreFeuilleParCopie = this.pdfService.numberOfPages(); + await this.process(); + } else { + if (this.pdfService.numberOfPages() !== 0 && this.i < 20) { + // Change if partial update + this.numberPagesInScan = this.pdfService.numberOfPages(); + this.avancementunit = ' / ' + this.numberPagesInScan; + await this.process(); + this.i = this.i + 1; + } } } - await this.process(); } blob: any; @@ -405,59 +413,68 @@ export class ChargerscanComponent implements OnInit { if (this.blob !== undefined) { this.blob1 = this.blob; } else { - const e1 = await firstValueFrom(this.scanService.getPdf(this.exam.scanfileId)); - this.blob1 = e1; + this.blob1 = await firstValueFrom(this.scanService.getPdf(this.exam.scanfileId)); } } } } } else { - this.observablePage = new Observable(observer => { - this.observerPage = observer; - }); - - this.observablePage.subscribe( - e => { - this.processPage(e, false); - // this.alignPage(e); - }, - err => console.log(err), - () => { - this.phase1 = false; - this.blob = undefined; - this.blob1 = undefined; - this.blocked = false; - this.firstPageToLoad = 0; - if (this.viewcomponent !== undefined) { - this.viewcomponent.update(); - } else { - this.db.countNonAlignImage(+this.examid!).then(p => { - this.db.countPageTemplate(+this.examid!).then(p1 => { - this.pageInScan = p; - this.pageInTemplate = p1; - this.showVignette = true; - }); - }); - } - - this.progress = 0; - this.submessage = ''; - this.message = ''; - // this.saveData(); - }, - ); - this.startPage = 1; this.currentPageAlign = 1; - while ( - this.currentPageAlign < this.startPage + ((navigator.hardwareConcurrency - 1) * 3) / 2 && - this.currentPageAlign < this.numberPagesInScan + 1 - ) { - this.observerPage!.next(this.currentPageAlign); - this.currentPageAlign = this.currentPageAlign + 1; + const pagesnumber: number[] = []; + for (let i = 1; i <= this.numberPagesInScan; i++) { + pagesnumber.push(i); } + await PromisePool.for(pagesnumber) + .withConcurrency(30) + /* .onTaskStarted((page, pool) => { + console.log(`Progress: ${pool.processedPercentage()}%`); + console.log(`Active tasks: ${pool.processedItems().length}`); + console.log(`Active tasks: ${pool.activeTasksCount()}`); + console.log(`Finished tasks: ${pool.processedItems().length}`); + console.log(`Finished tasks: ${pool.processedCount()}`); + })*/ + .onTaskFinished((page, pool) => { + this.progress = pool.processedPercentage(); + this.avancement = this.currentPageAlignOver; + this.currentPageAlignOver = this.currentPageAlignOver + 1; + this.submessage = '' + this.avancement + this.avancementunit; + }) + .process(async page => { + console.error('send process', page); + await this.processPage(page, false); + }); + // await Promise.all(this.imagesP); + console.error('end of processing'); + /* if (this.images1.length>20){ + await this.db.addNonAligneImages(this.images1); + this.images1 = []; + }*/ + + this.loaded = false; + // this.blob = undefined; + // this.blob1 = undefined; + // this.phase1 = false; + this.blocked = false; + this.firstPageToLoad = 0; + if (this.viewcomponent !== undefined) { + this.viewcomponent.update(); + } else { + this.db.countNonAlignImage(+this.examid!).then(p => { + this.db.countPageTemplate(+this.examid!).then(p1 => { + this.pageAlreadyScan = p; + this.pageInTemplate = p1; + this.showVignette = true; + }); + }); + } + + this.progress = 0; + this.submessage = ''; + this.message = ''; } + this.blocked = false; } async saveTemplateImage(pageN: number, imageD: any): Promise { @@ -475,8 +492,10 @@ export class ChargerscanComponent implements OnInit { } } + images1: any[] = []; + async saveNonAligneImage(pageN: number, imageD: any): Promise { - await this.db.addNonAligneImage({ + const im1 = { examId: +this.examid!, pageNumber: pageN + this.firstPageToLoad, value: JSON.stringify( @@ -485,51 +504,45 @@ export class ChargerscanComponent implements OnInit { }, this.replacer, ), - }); - if (this.currentPageAlign < this.numberPagesInScan + 1) { - this.observerPage?.next(this.currentPageAlign); - this.currentPageAlign = this.currentPageAlign + 1; - } - if (this.currentPageAlignOver < this.numberPagesInScan) { - this.avancement = this.currentPageAlignOver; - this.currentPageAlignOver = this.currentPageAlignOver + 1; - this.progress = Math.floor((this.avancement * 100) / this.numberPagesInScan); - this.submessage = '' + this.avancement + this.avancementunit; - } else { - this.avancement = this.currentPageAlignOver; - this.currentPageAlignOver = this.currentPageAlignOver + 1; - this.observerPage?.complete(); - console.timeEnd('processPage'); - } + }; + await this.db.addNonAligneImage(im1); + /* this.images1.push(im1) + if (this.images1.length>20){ + await this.db.addNonAligneImages(this.images1); + this.images1 = []; + }*/ } gotoUE(): any { this.router.navigateByUrl('/exam/' + this.examid!); } - public async processPage(page: number, template: boolean): Promise { + // imagesP: Promise[] = []; + public async processPage(page: number, template: boolean): Promise { + console.error('process page'); const scale = { scale: this.scale }; - if (page === 1 && !template) console.time('processPage'); - if (page === 1 && !template) console.timeLog('processPage', 'before getDataURL ', page); + if (page < 10 && !template) console.time('processPage' + page); + if (page < 10 && !template) console.timeLog('processPage' + page, 'before getDataURL ', page); const dataURL = await this.pdfService.getPageAsImage(page, scale); - if (page === 1 && !template) console.timeLog('processPage', 'getDataURL ', page); - const p = await this.saveImageScan(dataURL, page, template); - if (page === 1 && !template) console.timeLog('processPage', 'saveImageScan ', page); - - return p + 1; + // this.pdfService.getP + if (page < 10 && !template) console.timeLog('processPage' + page, 'getDataURL ', page); + await this.saveImageScan(dataURL, page, template); + if (page < 10 && !template) console.timeLog('processPage' + page, 'saveImageScan ', page); } - async saveImageScan(file: any, pagen: number, template: boolean): Promise { - // await this.saveNonAligneImage(pagen, file); + saveImageScan(file: any, pagen: number, template: boolean): Promise { return new Promise(resolve => { if (pagen === 1 && !template) console.timeLog('processPage', 'start', pagen); const i = new Image(); i.onload = async () => { if (pagen === 1 && !template) console.timeLog('processPage', 'image Loaded ', pagen); - const editedImage = document.createElement('canvas'); - editedImage.width = i.width; - editedImage.height = i.height; + const editedImage = new OffscreenCanvas(i.width, i.height); + + // document.createElement('canvas'); + // document.createOff + // editedImage.width = i.width; + // editedImage.height = i.height; const ctx = editedImage.getContext('2d'); ctx!.drawImage(i, 0, 0); if (pagen === 1 && !template) console.timeLog('processPage', 'draw first canvas ', pagen); @@ -560,15 +573,22 @@ export class ChargerscanComponent implements OnInit { exportImageType = this.preferenceService.getPreference().imageTypeExport; } - const webPImageURL = editedImage.toDataURL(exportImageType, compression); + const webPImageBlob = await editedImage.convertToBlob({ + type: exportImageType, + quality: compression, + }); + const webPImageURL = await blobToDataURL(webPImageBlob); if (template) { await this.saveTemplateImage(pagen, webPImageURL); + console.error('save template ' + pagen); + resolve(); } else { if (pagen === 1 && !template) console.timeLog('processPage', 'before save ', pagen); await this.saveNonAligneImage(pagen, webPImageURL); + resolve(); + if (pagen === 1 && !template) console.timeLog('processPage', 'after save ', pagen); } - resolve(pagen); }; i.src = file; }); @@ -585,3 +605,17 @@ export class ChargerscanComponent implements OnInit { } } } + +function blobToDataURL(blob: Blob): Promise { + return new Promise((resolve, reject) => { + const a = new FileReader(); + a.onload = e => { + if (e.target !== null) { + resolve(e.target.result); + } else { + reject(); + } + }; + a.readAsDataURL(blob); + }); +} diff --git a/src/main/webapp/app/scanexam/corrigequestion/corrigequestion.component.ts b/src/main/webapp/app/scanexam/corrigequestion/corrigequestion.component.ts index 6917fc88..6231149b 100644 --- a/src/main/webapp/app/scanexam/corrigequestion/corrigequestion.component.ts +++ b/src/main/webapp/app/scanexam/corrigequestion/corrigequestion.component.ts @@ -269,7 +269,7 @@ export class CorrigequestionComponent implements OnInit, AfterViewInit { // await this.refreshStudentList(forceRefreshStudent); } if (this.studentid !== studentid_prev) { - this.getSelectedStudent(this.currentStudent); + await this.getSelectedStudent(this.currentStudent); } // must be done here as the change of the nbreQuestions triggers the event of change question with page = 0 @@ -397,7 +397,7 @@ export class CorrigequestionComponent implements OnInit, AfterViewInit { this.questions = undefined; setTimeout(() => { this.questions = questions; - }, 0); + }, 20); }, 1); } diff --git a/src/main/webapp/app/scanexam/db/CacheServiceImpl.ts b/src/main/webapp/app/scanexam/db/CacheServiceImpl.ts index 16908d93..be8c830a 100644 --- a/src/main/webapp/app/scanexam/db/CacheServiceImpl.ts +++ b/src/main/webapp/app/scanexam/db/CacheServiceImpl.ts @@ -63,6 +63,9 @@ export class CacheServiceImpl implements CacheService { addNonAligneImage(elt: AlignImage): Promise { return this.service.addNonAligneImage(elt); } + addNonAligneImages(elts: AlignImage[]): Promise { + return this.service.addNonAligneImages(elts); + } export(examId: number, options?: ExportOptions | undefined): Promise { return this.service.export(examId, options); } diff --git a/src/main/webapp/app/scanexam/db/db.ts b/src/main/webapp/app/scanexam/db/db.ts index 2d81142b..4a5a8791 100644 --- a/src/main/webapp/app/scanexam/db/db.ts +++ b/src/main/webapp/app/scanexam/db/db.ts @@ -199,6 +199,9 @@ export class ExamIndexDB extends Dexie { async addNonAligneImage(elt: AlignImage) { await this.nonAlignImages.put(elt); } + async addNonAligneImages(elts: AlignImage[]) { + await this.nonAlignImages.bulkAdd(elts); + } export(options?: ExportOptions): Promise { return super.export(options); @@ -464,6 +467,19 @@ export class AppDB implements CacheService { return db1.addNonAligneImage(elt); } + async addNonAligneImages(elts: AlignImage[]): Promise { + if (elts.length > 0) { + let db1 = this.dbs.get(elts[0].examId); + if (db1 === undefined) { + db1 = new ExamIndexDB(elts[0].examId); + this.dbs.set(elts[0].examId, db1); + } + return db1.addNonAligneImages(elts); + } else { + return; + } + } + export(examId: number, options?: ExportOptions): Promise { let db1 = this.dbs.get(examId); if (db1 === undefined) { @@ -652,6 +668,7 @@ export interface CacheService { removePageNonAlignForExamForPages(examId: number, pageStart: number, pageEnd: number): Promise; addAligneImage(elt: AlignImage): Promise; addNonAligneImage(elt: AlignImage): Promise; + addNonAligneImages(elts: AlignImage[]): Promise; export(examId: number, options?: ExportOptions): Promise; import(examId: number, blob: Blob, options?: ImportOptions): Promise; countPageTemplate(examId: number): Promise; diff --git a/src/main/webapp/app/scanexam/db/dbsqlite.ts b/src/main/webapp/app/scanexam/db/dbsqlite.ts index de9bbffe..3f0345d1 100644 --- a/src/main/webapp/app/scanexam/db/dbsqlite.ts +++ b/src/main/webapp/app/scanexam/db/dbsqlite.ts @@ -209,6 +209,26 @@ export class SqliteCacheService implements CacheService { return this._dispatch('addNonAligneImage', el1, [el1.value]).toPromise(); } + async addNonAligneImages(elts: ImageDB[]) { + const enc = new TextEncoder(); // always utf-8 + if (elts.length > 0) { + const els: any[] = []; + const els1: ArrayBufferLike[] = []; + elts.forEach(elt => { + const el1 = { + examId: elt.examId, + pageNumber: elt.pageNumber, + value: enc.encode(elt.value).buffer, + }; + els.push(el1); + els1.push(el1.value); + }); + + return this._dispatch('addNonAligneImages', els, els1).toPromise(); + } + return; + } + export(examId: number, options?: ExportOptions | undefined): Promise { return this._dispatch('export', { examId: examId, diff --git a/src/main/webapp/i18n/en/scanexammodule.json b/src/main/webapp/i18n/en/scanexammodule.json index fbf7bcca..13ebd972 100644 --- a/src/main/webapp/i18n/en/scanexammodule.json +++ b/src/main/webapp/i18n/en/scanexammodule.json @@ -443,6 +443,8 @@ "hybridcommentStep": "Number of slice(s)", "hybridcommentStepTooltip": "Used to indicate whether the marker can divide the value of this comment into a number of slices. For example, if the mark for this comment is 80% of the overall mark and the number of brackets is 2, the marker may not apply this comment, apply this comment with one slice (40% of the overall mark) or apply this comment with two slices (80% of the overall mark).", "hybridcommentGrade": "Grade", - "downloadpdf": "Download pdfs" + "downloadpdf": "Download pdfs", + "stepPage": "Number of images processed in batch", + "stepPageTooltip": "Used to configure the number of images processed in batch before memory is released" } } diff --git a/src/main/webapp/i18n/fr/scanexammodule.json b/src/main/webapp/i18n/fr/scanexammodule.json index 6765266a..8f5df6ee 100644 --- a/src/main/webapp/i18n/fr/scanexammodule.json +++ b/src/main/webapp/i18n/fr/scanexammodule.json @@ -447,6 +447,8 @@ "hybridcommentStep": "Nombre de tranche(s)", "hybridcommentStepTooltip": "Permet d'exprimer si le correcteur pourra diviser la valeur de ce commentaire en un nombre de tranche(s). Ainsi, si la note de ce commentaire vaut 80% de la note globale et que le nombre de tranche est 2, le correcteur pourra ne pas appliquer ce commentaire, appliquer ce commentaire avec une tranche (40% de la note globale), appliquer ce commentaire avec deux tranches 80% de la note globale", "hybridcommentGrade": "Note", - "downloadpdf": "Téléchargement pdfs" + "downloadpdf": "Téléchargement pdfs", + "stepPage": "Nombre d'images traitées en batch", + "stepPageTooltip": "Permet de configurer le nombre d'images traitées en batch avant libération de la mémoire" } }