app.controller.ts 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /**
  2. * * **Current State:** The `AppController` seeder includes a dynamic regex and fallback system to resolve and assign a `phaseCode` attribute to blocks during block seeding (lines 75-94).
  3. * * **Intended Mutation:** Remove the dynamic regex resolution block and `phaseCode` mapping from `mappedBlock` within the `2. BLOCK SEEDING` loop to achieve flat block seeding.
  4. * * **Risk Mitigation:** Cross-entity validation is entirely shifted to the `FFB Production` ledger level, making the master block seeder 100% independent.
  5. */
  6. import { Controller, Get, Logger, BadRequestException } from '@nestjs/common';
  7. import { AppService } from './app.service';
  8. import { PhaseService } from './site/services/phase.service';
  9. import { BlockService } from './site/services/block.service';
  10. import { FFBProductionService } from './FFB/services/ffb-production.service';
  11. import * as fs from 'fs';
  12. import * as path from 'path';
  13. @Controller()
  14. export class AppController {
  15. private readonly logger: Logger = new Logger('AppController');
  16. constructor(
  17. private readonly service: AppService,
  18. private readonly phaseService: PhaseService,
  19. private readonly blockService: BlockService,
  20. private readonly ffbService: FFBProductionService,
  21. ) { }
  22. @Get()
  23. getHello(): string {
  24. return this.service.getHello();
  25. }
  26. @Get('seed')
  27. async seedSystemData() {
  28. this.logger.log('🚀 Starting chronological seeding...');
  29. // Resolve JSON data file locations dynamically relative to process.cwd()
  30. const rootDir = process.cwd();
  31. const phaseDataPath = path.resolve(rootDir, '../mongo stuff/PhaseData.json');
  32. const blockDataPath = path.resolve(rootDir, '../mongo stuff/BlockData.json');
  33. const ffbDataPath = path.resolve(rootDir, '../mongo stuff/FFBProductionData.json');
  34. // 1. PHASE SEEDING
  35. if (!fs.existsSync(phaseDataPath)) {
  36. throw new BadRequestException(`PhaseData.json not found at ${phaseDataPath}`);
  37. }
  38. const rawPhases = JSON.parse(fs.readFileSync(phaseDataPath, 'utf8'));
  39. this.logger.log(`Found ${rawPhases.length} phases to process.`);
  40. for (const rawPhase of rawPhases) {
  41. try {
  42. const mappedPhase = {
  43. locId: Number(rawPhase.loc_id),
  44. phaseCode: rawPhase.name,
  45. description: rawPhase.description,
  46. locType: rawPhase.loc_type,
  47. };
  48. await this.phaseService.create(mappedPhase);
  49. } catch (err) {
  50. this.logger.error(`Error inserting Phase ${rawPhase.name}: ${err.message}`);
  51. }
  52. }
  53. console.log('[SEED] Phase collection initialization pass complete.');
  54. // 2. BLOCK SEEDING
  55. if (!fs.existsSync(blockDataPath)) {
  56. throw new BadRequestException(`BlockData.json not found at ${blockDataPath}`);
  57. }
  58. const rawBlocks = JSON.parse(fs.readFileSync(blockDataPath, 'utf8'));
  59. this.logger.log(`Found ${rawBlocks.length} blocks to process.`);
  60. for (const rawBlock of rawBlocks) {
  61. try {
  62. const mappedBlock = {
  63. locId: Number(rawBlock.loc_id),
  64. blockCode: rawBlock.blockCode,
  65. blockDesc: rawBlock.blockDesc,
  66. locType: rawBlock.loc_type,
  67. entryNo: Number(rawBlock.entry_no),
  68. entryYear: Number(rawBlock.entry_year),
  69. quarterPlanted: Number(rawBlock.quater_planted),
  70. monthPlanted: rawBlock.month_planted,
  71. totalTrees: Number(rawBlock.numOfTreesPlanted),
  72. totalMaturedTrees: Number(rawBlock.totalTreeMatured),
  73. totalImmaturedTrees: Number(rawBlock.totalTreeImmatured),
  74. totalDeadTrees: Number(rawBlock.totalTreeDead),
  75. plantedArea: Number(rawBlock.totalPlantedArea),
  76. initialPlantedArea: Number(rawBlock.initalPlantedArea),
  77. plantedLocUOM: rawBlock.plantedLocUOM,
  78. soilCondition: rawBlock.loc_soil_condition,
  79. };
  80. await this.blockService.create(mappedBlock);
  81. } catch (err) {
  82. this.logger.error(`Error inserting Block ${rawBlock.blockCode}: ${err.message}`);
  83. }
  84. }
  85. console.log('[SEED] Block collection initialization pass complete.');
  86. // 3. FFB TRANSACTION INGESTION & VECTOR ENRICHMENT STREAM
  87. if (!fs.existsSync(ffbDataPath)) {
  88. throw new BadRequestException(`FFBProductionData.json not found at ${ffbDataPath}`);
  89. }
  90. const rawFfbs = JSON.parse(fs.readFileSync(ffbDataPath, 'utf8'));
  91. // Trigger background seeder worker to prevent HTTP timeout
  92. this.runBackgroundFfbSeed(rawFfbs);
  93. this.logger.log(`[SEED] Ingestion worker spawned for all ${rawFfbs.length} records.`);
  94. return {
  95. status: 'success',
  96. message: `Chronological seeding initiated for ${rawFfbs.length} transaction records in the background. Monitor server logs for live updates.`,
  97. };
  98. }
  99. private async runBackgroundFfbSeed(records: any[]) {
  100. const CHUNK_SIZE = 5; // Adjust down if CPU is weak, up to 10 if using a dedicated GPU
  101. const COOL_DOWN_MS = 1000; // 1 second pause between chunks to let the CPU breathe
  102. this.logger.log(`[WORKER] Beginning processing matrix for ${records.length} items...`);
  103. for (let i = 0; i < records.length; i += CHUNK_SIZE) {
  104. const chunk = records.slice(i, i + CHUNK_SIZE);
  105. this.logger.log(`[WORKER] Processing batch chunk ${Math.floor(i / CHUNK_SIZE) + 1} of ${Math.ceil(records.length / CHUNK_SIZE)}...`);
  106. for (const rawFfb of chunk) {
  107. try {
  108. const mappedFfb = {
  109. productionDate: rawFfb.productionDate ? new Date(rawFfb.productionDate) : new Date('2024-01-01'),
  110. prjCode: rawFfb.prj_code,
  111. actCode: rawFfb.act_code,
  112. actName: rawFfb.act_name,
  113. entityCode: rawFfb.entitycode,
  114. orgnId: rawFfb.orgn_id ? Number(rawFfb.orgn_id) : 0,
  115. orgnCode: rawFfb.orgn_code,
  116. orgnFullName: rawFfb.orgn_full_name,
  117. orgnAddress: rawFfb.orgn_address,
  118. orgnCompRegNo: rawFfb.orgn_comp_reg_no,
  119. phaseCode: rawFfb.phaseCode || 'PH01',
  120. phaseName: rawFfb.phaseName || 'PHASE 01',
  121. phaseDesc: rawFfb.phaseDesc || 'PHASE 01',
  122. blockCode: rawFfb.blockCode,
  123. blockName: rawFfb.blockName || null,
  124. blockDesc: rawFfb.blockDesc || null,
  125. truckNo: rawFfb.truck_no,
  126. millNo: rawFfb.mill_no,
  127. actEntryNo: rawFfb.act_entry_no ? Number(rawFfb.act_entry_no) : 0,
  128. actRound: rawFfb.act_round ? Number(rawFfb.act_round) : 0,
  129. weightChitNo: rawFfb.weight_chit_no,
  130. ownNetWeight: rawFfb.own_net_weight ? Number(rawFfb.own_net_weight) : null,
  131. netWeight: rawFfb.net_weight ? Number(rawFfb.net_weight) : 0,
  132. actUom: rawFfb.act_uom,
  133. noOfBunches: rawFfb.no_of_bunches ? Number(rawFfb.no_of_bunches) : 0,
  134. qtyUom: rawFfb.qty_uom,
  135. docActQty: rawFfb.doc_act_qty ? Number(rawFfb.doc_act_qty) : 0,
  136. locArea: rawFfb.loc_area ? Number(rawFfb.loc_area) : 0,
  137. locUom: rawFfb.loc_uom,
  138. budgetedFfb: rawFfb.budgeted_ffb ? Number(rawFfb.budgeted_ffb) : null,
  139. remarks: rawFfb.remarks || '',
  140. issues: rawFfb.issues || null,
  141. };
  142. await this.ffbService.create(mappedFfb);
  143. } catch (err) {
  144. this.logger.error(`[WORKER ERROR] Step failed for record entry: ${err.message}`);
  145. }
  146. }
  147. // Enforce the event-loop cool down period
  148. if (i + CHUNK_SIZE < records.length) {
  149. await new Promise(resolve => setTimeout(resolve, COOL_DOWN_MS));
  150. }
  151. }
  152. this.logger.log(`[WORKER] ✅ SUCCESS! Full ledger dataset vectorization and seeder run complete.`);
  153. }
  154. }