Browse Source

simple ffb harvest component

Dr-Swopt 2 months ago
parent
commit
ab9b64f1f0

+ 32 - 0
src/FFB/ffb-harvest.controller.ts

@@ -0,0 +1,32 @@
+import { Controller, Get, Post, Delete, Body, Param, Query } from '@nestjs/common';
+import { FFBHarvestService } from './ffb-harvest.service';
+import { FFBHarvest } from 'src/FFB/ffb-harvest.schema';
+
+@Controller('ffb-harvest')
+export class FFBHarvestController {
+  constructor(private readonly harvestService: FFBHarvestService) {}
+
+  @Post()
+  async create(@Body() body: FFBHarvest) {
+    console.log('POST /ffb-harvest');
+    return this.harvestService.create(body);
+  }
+
+  @Get()
+  async findAll(@Query() query: any) {
+    console.log('GET /ffb-harvest', query);
+    return this.harvestService.findAll(query);
+  }
+
+  @Get(':id')
+  async findById(@Param('id') id: string) {
+    console.log(`GET /ffb-harvest/${id}`);
+    return this.harvestService.findById(id);
+  }
+
+  @Delete(':id')
+  async delete(@Param('id') id: string) {
+    console.log(`DELETE /ffb-harvest/${id}`);
+    return this.harvestService.delete(id);
+  }
+}

+ 13 - 0
src/FFB/ffb-harvest.module.ts

@@ -0,0 +1,13 @@
+import { Module } from '@nestjs/common';
+import { FFBHarvestController } from './ffb-harvest.controller';
+import { FFBHarvestService } from './ffb-harvest.service';
+import { MongoModule } from 'src/mongo/mongo.module';
+
+@Module({
+  imports: [
+    MongoModule
+  ],
+  controllers: [FFBHarvestController],
+  providers: [FFBHarvestService],
+})
+export class FFBHarvestModule {}

+ 13 - 0
src/FFB/ffb-harvest.schema.ts

@@ -0,0 +1,13 @@
+export interface FFBHarvest {
+  _id?: string;
+  harvestDate: Date;
+  site: string;
+  phase: string;
+  block: string;
+  harvester: string;
+  daysOfWork: number;
+  weight: number;
+  weightUom: string;
+  quantity: number;
+  quantityUom: string;
+}

+ 41 - 0
src/FFB/ffb-harvest.service.ts

@@ -0,0 +1,41 @@
+import { Injectable } from '@nestjs/common';
+import { MongoCoreService } from '../mongo/mongo-core.service';
+import { FFBHarvestRepository } from '../mongo/mongo-ffb-harvest.repository';
+import { FFBHarvest } from './ffb-harvest.schema';
+
+@Injectable()
+export class FFBHarvestService {
+  private repo: FFBHarvestRepository;
+
+  constructor(private readonly mongoCore: MongoCoreService) {}
+
+  /** Lazily get or create the repository */
+  private async getRepository(): Promise<FFBHarvestRepository> {
+    if (!this.repo) {
+      const db = await this.mongoCore.getDb();
+      this.repo = new FFBHarvestRepository(db);
+      await this.repo.init();
+    }
+    return this.repo;
+  }
+
+  async create(harvest: FFBHarvest) {
+    const repo = await this.getRepository();
+    return repo.create(harvest);
+  }
+
+  async findAll(filter: Record<string, any> = {}) {
+    const repo = await this.getRepository();
+    return repo.findAll(filter);
+  }
+
+  async findById(id: string) {
+    const repo = await this.getRepository();
+    return repo.findById(id);
+  }
+
+  async delete(id: string) {
+    const repo = await this.getRepository();
+    return repo.delete(id);
+  }
+}

+ 68 - 59
src/activity/activity.controller.ts

@@ -1,78 +1,87 @@
-// src/harvest/harvest.controller.ts
+// src/activity/activity.controller.ts
 import { Controller, Get, Post, Body, Param, Put, Delete, Query } from '@nestjs/common';
 import { ActivityService } from './activity.service';
 import { CreateActivityDto, UpdateActivityDto } from './activity.dto';
 
 @Controller('activity')
 export class ActivityController {
-    constructor(private readonly activityService: ActivityService) { }
+  constructor(private readonly activityService: ActivityService) {}
 
-    @Post()
-    async create(@Body() dto: CreateActivityDto) {
-        return this.activityService.create(dto);
-    }
+  @Post()
+  async create(@Body() dto: CreateActivityDto) {
+    console.log('POST /activity');
+    return this.activityService.create(dto);
+  }
 
-    @Get()
-    async findAll(@Query() query: Record<string, any>) {
-        return this.activityService.findAll(query);
-    }
+  @Get()
+  async findAll(@Query() query: Record<string, any>) {
+    console.log('GET /activity');
+    return this.activityService.findAll(query);
+  }
 
-    @Get(':id')
-    async findOne(@Param('id') id: string) {
-        return this.activityService.findById(id);
-    }
+  @Get(':id')
+  async findOne(@Param('id') id: string) {
+    console.log(`GET /activity/${id}`);
+    return this.activityService.findById(id);
+  }
 
-    @Put(':id')
-    async update(@Param('id') id: string, @Body() dto: UpdateActivityDto) {
-        return this.activityService.update(id, dto);
-    }
+  @Put(':id')
+  async update(@Param('id') id: string, @Body() dto: UpdateActivityDto) {
+    console.log(`PUT /activity/${id}`);
+    return this.activityService.update(id, dto);
+  }
 
-    @Delete(':id')
-    async remove(@Param('id') id: string) {
-        return this.activityService.delete(id);
-    }
+  @Delete(':id')
+  async remove(@Param('id') id: string) {
+    console.log(`DELETE /activity/${id}`);
+    return this.activityService.delete(id);
+  }
 
-    @Get('stats/resources')
-    async totalResources() {
-        return this.activityService.getTotalResourceHours();
-    }
+  @Get('stats/resources')
+  async totalResources() {
+    console.log('GET /activity/stats/resources');
+    return this.activityService.getTotalResourceHours();
+  }
 
-    @Get('stats/outputs')
-    async totalOutputs() {
-        return this.activityService.getTotalOutputWeight();
-    }
+  @Get('stats/outputs')
+  async totalOutputs() {
+    console.log('GET /activity/stats/outputs');
+    return this.activityService.getTotalOutputWeight();
+  }
 
-    @Get('date-range')
-    async byDateRange(@Query('start') start: string, @Query('end') end: string) {
-        return this.activityService.findByDateRange(new Date(start), new Date(end));
-    }
+  @Get('date-range')
+  async byDateRange(@Query('start') start: string, @Query('end') end: string) {
+    console.log('GET /activity/date-range');
+    return this.activityService.findByDateRange(new Date(start), new Date(end));
+  }
 
-    @Get('stats/workers')
-    async totalWorkers(
-        @Query('start') start?: string,
-        @Query('end') end?: string,
-        @Query('location') location?: string,
-    ) {
-        const filter: any = {};
-        if (start && end) {
-            filter.start = new Date(start);
-            filter.end = new Date(end);
-        }
-        if (location) filter.location = location;
-        return this.activityService.getTotalWorkers(filter);
+  @Get('stats/workers')
+  async totalWorkers(
+    @Query('start') start?: string,
+    @Query('end') end?: string,
+    @Query('location') location?: string,
+  ) {
+    console.log('GET /activity/stats/workers');
+    const filter: any = {};
+    if (start && end) {
+      filter.start = new Date(start);
+      filter.end = new Date(end);
     }
+    if (location) filter.location = location;
+    return this.activityService.getTotalWorkers(filter);
+  }
 
-    @Get('stats/outputs/detail')
-    async totalOutputsDetail(
-        @Query('start') start?: string,
-        @Query('end') end?: string,
-    ) {
-        const filter: any = {};
-        if (start && end) {
-            filter.start = new Date(start);
-            filter.end = new Date(end);
-        }
-        return this.activityService.getTotalOutputs(filter);
+  @Get('stats/outputs/detail')
+  async totalOutputsDetail(
+    @Query('start') start?: string,
+    @Query('end') end?: string,
+  ) {
+    console.log('GET /activity/stats/outputs/detail');
+    const filter: any = {};
+    if (start && end) {
+      filter.start = new Date(start);
+      filter.end = new Date(end);
     }
-
+    return this.activityService.getTotalOutputs(filter);
+  }
 }

+ 2 - 2
src/activity/activity.module.ts

@@ -2,10 +2,10 @@
 import { Module } from '@nestjs/common';
 import { ActivityController } from './activity.controller';
 import { ActivityService } from './activity.service';
-import { ServiceModule } from 'src/services/service.module';
+import { MongoModule } from 'src/mongo/mongo.module';
 
 @Module({
-  imports: [ServiceModule],
+  imports: [MongoModule],
   controllers: [ActivityController],
   providers: [ActivityService],
   exports: [ActivityService],

+ 38 - 68
src/activity/activity.service.ts

@@ -1,124 +1,94 @@
-// src/harvest/harvest.service.ts
 import { Injectable } from '@nestjs/common';
-import { HarvestActivity } from './activity.schema';
+import { ActivityRepository } from '../mongo/mongo-activity.repository';
+import { MongoCoreService } from '../mongo/mongo-core.service';
 import { CreateActivityDto, UpdateActivityDto } from './activity.dto';
 import { ObjectId } from 'mongodb';
-import { MongoService } from 'src/services/mongo.service';
+import { HarvestActivity } from './activity.schema';
 
 @Injectable()
 export class ActivityService {
-    private readonly collectionName = 'activities';
+    private repo: ActivityRepository;
+
+    constructor(private readonly mongoCore: MongoCoreService) { }
 
-    constructor(private readonly mongo: MongoService) { }
+    private async getRepository(): Promise<ActivityRepository> {
+        if (!this.repo) {
+            const db = await this.mongoCore.getDb(); // ✅ await here
+            this.repo = new ActivityRepository(db);
+        }
+        return this.repo;
+    }
 
-    /* ------------------------------
-     * Utility: Convert DTO to Schema
-     * ------------------------------ */
-    private dtoToHarvestActivity(dto: CreateActivityDto): HarvestActivity {
-        return {
+    async create(dto: CreateActivityDto) {
+        const repo = await this.getRepository();
+        const activity: HarvestActivity = {
             name: dto.name,
             type: dto.type,
-            resources: dto.resources.map((r) => ({
+            resources: dto.resources.map(r => ({
                 ...r,
                 id: r.id ? new ObjectId(r.id) : new ObjectId(),
             })),
             duration: dto.duration,
-            outputs: dto.outputs.map((o) => ({
+            outputs: dto.outputs.map(o => ({
                 ...o,
                 id: o.id ? new ObjectId(o.id) : new ObjectId(),
             })),
-            targets: dto.targets.map((t) => ({
+            targets: dto.targets.map(t => ({
                 ...t,
                 id: t.id ? new ObjectId(t.id) : new ObjectId(),
             })),
             dateStart: new Date(dto.dateStart),
             dateEnd: new Date(dto.dateEnd),
         };
+        return repo.create(activity);
     }
 
-    /* ------------------------------
-     * CRUD OPERATIONS
-     * ------------------------------ */
-    async create(dto: CreateActivityDto) {
-        const activity = this.dtoToHarvestActivity(dto);
-        return this.mongo.createActivity(activity);
-    }
 
     async findAll(filter: Record<string, any> = {}) {
-        return this.mongo.getAllActivities(filter);
+        const repo = await this.getRepository();
+        return repo.findAll(filter);
     }
 
     async findById(id: string) {
-        return this.mongo.getActivityById(id);
+        const repo = await this.getRepository();
+        return repo.findById(id);
     }
 
     async update(id: string, dto: UpdateActivityDto) {
-        // For partial updates, we only convert provided IDs
+        const repo = await this.getRepository();
         const update: any = { ...dto };
-        if (dto.duration) {
-            update.duration = {
-                value: {
-                    quantity: dto.duration.value?.quantity ?? 0,
-                    uom: dto.duration.value?.uom ?? 'hours',
-                },
-            };
-        }
-        if (dto.resources) {
-            update.resources = dto.resources.map((r) => ({
-                ...r,
-                id: r.id ? new ObjectId(r.id) : new ObjectId(),
-            }));
-        }
-        if (dto.outputs) {
-            update.outputs = dto.outputs.map((o) => ({
-                ...o,
-                id: o.id ? new ObjectId(o.id) : new ObjectId(),
-            }));
-        }
-        if (dto.targets) {
-            update.targets = dto.targets.map((t) => ({
-                ...t,
-                id: t.id ? new ObjectId(t.id) : new ObjectId(),
-            }));
-        }
         if (dto.dateStart) update.dateStart = new Date(dto.dateStart);
         if (dto.dateEnd) update.dateEnd = new Date(dto.dateEnd);
-
-        return this.mongo.updateActivity(id, update);
+        return repo.update(id, update);
     }
 
     async delete(id: string) {
-        return this.mongo.deleteActivity(id);
+        const repo = await this.getRepository();
+        return repo.delete(id);
     }
 
-    /* ------------------------------
-     * Aggregations / Queries
-     * ------------------------------ */
-
     async getTotalResourceHours() {
-        return this.mongo.getTotalResourceHours();
+        const repo = await this.getRepository();
+        return repo.getTotalResourceHours();
     }
 
     async getTotalOutputWeight() {
-        return this.mongo.getTotalOutputWeight();
+        const repo = await this.getRepository();
+        return repo.getTotalOutputWeight();
     }
 
     async findByDateRange(start: Date, end: Date) {
-        return this.mongo.getActivitiesByDateRange(start, end);
+        const repo = await this.getRepository();
+        return repo.getActivitiesByDateRange(start, end);
     }
 
-    /* ------------------------------
-   * Aggregations / Custom Queries
-   * ------------------------------ */
-
     async getTotalWorkers(filter: { start?: Date; end?: Date; location?: string }) {
-        return this.mongo.getTotalWorkers(filter);
+        const repo = await this.getRepository();
+        return repo.getTotalWorkers(filter);
     }
 
     async getTotalOutputs(filter: { start?: Date; end?: Date }) {
-        return this.mongo.getTotalOutputs(filter);
+        const repo = await this.getRepository();
+        return repo.getTotalOutputs(filter);
     }
-
-
-
 }

+ 4 - 0
src/app.module.ts

@@ -9,12 +9,16 @@ import { MongooseModule } from '@nestjs/mongoose';
 import { mongooseConfig } from './config/mongoose.config';
 import { ServiceModule } from './services/service.module';
 import { ActivityModule } from './activity/activity.module';
+import { MongoModule } from './mongo/mongo.module';
+import { FFBHarvestModule } from './FFB/ffb-harvest.module';
 
 @Module({
   imports: [
     MongooseModule.forRootAsync({
       useFactory: () => mongooseConfig,
     }),
+    FFBHarvestModule,
+    MongoModule,
     AuthModule,
     AttendanceModule,
     PaymentModule,

+ 3 - 3
src/config/config.ts

@@ -1,7 +1,7 @@
 
 export const serverConfig = {
-  exposedUrl: 'https://localhost:3000',
+  exposedUrl: 'https://192.168.100.100:4000',
   rpName: 'My App',
-  rpId: 'localhost',
-  origin: 'https://localhost:3000',
+  rpId: '192.168.100.100',
+  origin: 'https://192.168.100.100:4000',
 };

+ 3 - 5
src/main.ts

@@ -10,10 +10,8 @@ import { ValidationPipe } from '@nestjs/common';
 async function bootstrap() {
   const certsDir = join(__dirname, '..', 'certs');
   const httpsOptions = {
-    key: fs.readFileSync(join(certsDir, 'localhost+4-key.pem')),
-    cert: fs.readFileSync(join(certsDir, 'localhost+4.pem')),
-    // key: fs.readFileSync(join(certsDir, 'local-key.pem')),
-    // cert: fs.readFileSync(join(certsDir, 'local-cert.pem')),
+    key: fs.readFileSync(join(certsDir, '192.168.100.100+2-key.pem')),
+    cert: fs.readFileSync(join(certsDir, '192.168.100.100+2.pem')),
   };
 
   // const app = await NestFactory.create<NestExpressApplication>(AppModule);
@@ -52,7 +50,7 @@ async function bootstrap() {
     res.sendFile(indexPath);
   });
 
-  await app.listen(3000, '0.0.0.0');
+  await app.listen(4000, '0.0.0.0');
   console.log(`🚀 HTTPS server running at ${serverConfig.exposedUrl}`);
 
 }

+ 97 - 0
src/mongo/mongo-activity.repository.ts

@@ -0,0 +1,97 @@
+import { Db } from 'mongodb';
+import { MongoRepository } from './mongo-repository';
+
+export class ActivityRepository extends MongoRepository<any> {
+  constructor(db: Db) {
+    super(db, 'activities');
+  }
+
+  async getTotalResourceHours() {
+    return this.collection
+      .aggregate([
+        { $unwind: '$resources' },
+        {
+          $group: {
+            _id: '$resources.type',
+            totalHours: { $sum: { $toDecimal: '$resources.value.quantity' } },
+          },
+        },
+      ])
+      .toArray();
+  }
+
+  async getTotalOutputWeight() {
+    return this.collection
+      .aggregate([
+        { $unwind: '$outputs' },
+        {
+          $group: {
+            _id: '$outputs.type',
+            totalWeightKg: { $sum: { $toDecimal: '$outputs.weightValue.weight' } },
+          },
+        },
+      ])
+      .toArray();
+  }
+
+  async getActivitiesByDateRange(start: Date, end: Date) {
+    return this.collection
+      .find({
+        dateStart: { $gte: start },
+        dateEnd: { $lte: end },
+      })
+      .toArray();
+  }
+
+  async getTotalWorkers(filter: { start?: Date; end?: Date; location?: string } = {}) {
+    const matchStage: any = {};
+
+    if (filter.start && filter.end) {
+      matchStage.dateStart = { $gte: filter.start };
+      matchStage.dateEnd = { $lte: filter.end };
+    }
+
+    if (filter.location) {
+      matchStage['targets.name'] = filter.location;
+    }
+
+    const pipeline = [
+      { $match: Object.keys(matchStage).length ? matchStage : {} },
+      { $unwind: '$resources' },
+      { $match: { 'resources.type': 'worker' } },
+      {
+        $group: {
+          _id: '$resources.name',
+          totalHours: { $sum: { $toDecimal: '$resources.value.quantity' } },
+        },
+      },
+      { $project: { _id: 0, worker: '$_id', totalHours: 1 } },
+    ];
+
+    return this.collection.aggregate(pipeline).toArray();
+  }
+
+  async getTotalOutputs(filter: { start?: Date; end?: Date } = {}) {
+    const matchStage: any = {};
+
+    if (filter.start && filter.end) {
+      matchStage.dateStart = { $gte: filter.start };
+      matchStage.dateEnd = { $lte: filter.end };
+    }
+
+    const pipeline = [
+      { $match: Object.keys(matchStage).length ? matchStage : {} },
+      { $unwind: '$outputs' },
+      {
+        $group: {
+          _id: '$outputs.type',
+          totalBunch: { $sum: { $toDecimal: '$outputs.value.quantity' } },
+          totalWeightKg: { $sum: { $toDecimal: '$outputs.weightValue.weight' } },
+        },
+      },
+      { $project: { _id: 0, outputType: '$_id', totalBunch: 1, totalWeightKg: 1 } },
+    ];
+
+    return this.collection.aggregate(pipeline).toArray();
+  }
+}

+ 49 - 0
src/mongo/mongo-core.service.ts

@@ -0,0 +1,49 @@
+import { Injectable, OnModuleInit, Logger } from '@nestjs/common';
+import { MongoClient, Db } from 'mongodb';
+
+@Injectable()
+export class MongoCoreService implements OnModuleInit {
+  private client: MongoClient;
+  private db: Db;
+  private readonly logger = new Logger(MongoCoreService.name);
+  private connectionPromise: Promise<Db>;
+
+  async onModuleInit() {
+    this.connectionPromise = this.connect();
+    await this.connectionPromise; // ensures connection completes
+  }
+
+  private async connect(): Promise<Db> {
+    const uri = process.env.MONGO_URI;
+    const dbName = process.env.MONGO_DB_NAME || 'swopt-ai-test-a';
+
+    if (!uri) throw new Error('❌ MONGO_URI not defined in environment');
+
+    this.logger.log(`Connecting to MongoDB...`);
+    this.client = new MongoClient(uri);
+
+    try {
+      await this.client.connect();
+      this.db = this.client.db(dbName);
+      this.logger.log(`✅ Connected to MongoDB (${dbName})`);
+      return this.db;
+    } catch (err) {
+      this.logger.error(`❌ Failed to connect to MongoDB`, err);
+      throw err;
+    }
+  }
+
+  /** Always returns a connected DB (waits if still connecting) */
+  async getDb(): Promise<Db> {
+    if (this.db) return this.db;
+    if (this.connectionPromise) return this.connectionPromise;
+    throw new Error('MongoDB is not connected.');
+  }
+
+  async onModuleDestroy() {
+    if (this.client) {
+      await this.client.close();
+      this.logger.log('MongoDB connection closed.');
+    }
+  }
+}

+ 51 - 0
src/mongo/mongo-ffb-harvest.repository.ts

@@ -0,0 +1,51 @@
+import { Db, ObjectId, WithId } from 'mongodb';
+import { FFBHarvest } from 'src/FFB/ffb-harvest.schema';
+export class FFBHarvestRepository {
+  private readonly collectionName = 'FFB Harvest';
+
+  constructor(private readonly db: Db) {}
+
+  private get collection() {
+    return this.db.collection<FFBHarvest>(this.collectionName);
+  }
+
+  async init() {
+    const collections = await this.db.listCollections({ name: this.collectionName }).toArray();
+    if (collections.length === 0) {
+      await this.db.createCollection(this.collectionName, {
+        timeseries: {
+          timeField: 'harvestDate',
+          metaField: 'site',
+          granularity: 'days',
+        },
+      });
+      console.log(`✅ Created time series collection: ${this.collectionName}`);
+    }
+  }
+
+  async create(harvest: FFBHarvest): Promise<FFBHarvest> {
+    const result = await this.collection.insertOne({
+      ...harvest,
+      harvestDate: new Date(harvest.harvestDate),
+    });
+    return { ...harvest, _id: result.insertedId.toString() };
+  }
+
+  async findAll(filter: Record<string, any> = {}): Promise<FFBHarvest[]> {
+    const results = await this.collection.find(filter).toArray();
+    // Convert ObjectId to string for frontend-friendliness
+    return results.map((r: WithId<FFBHarvest>) => ({
+      ...r,
+      _id: r._id?.toString(),
+    }));
+  }
+
+  async findById(id: string): Promise<FFBHarvest | null> {
+    const result = await this.collection.findOne({ _id: new ObjectId(id) as any });
+    return result ? { ...result, _id: result._id.toString() } : null;
+  }
+
+  async delete(id: string) {
+    return this.collection.deleteOne({ _id: new ObjectId(id) as any });
+  }
+}

+ 52 - 0
src/mongo/mongo-repository.ts

@@ -0,0 +1,52 @@
+import {
+    Collection,
+    Db,
+    Filter,
+    ObjectId,
+    OptionalUnlessRequiredId,
+    UpdateFilter,
+    WithId,
+} from 'mongodb';
+
+export class MongoRepository<T extends { _id?: ObjectId }> {
+    protected readonly collection: Collection<T>;
+
+    constructor(dbOrCollection: Db | Collection<T>, collectionName?: string) {
+        if (dbOrCollection instanceof Collection) {
+            this.collection = dbOrCollection;
+        } else if (collectionName) {
+            this.collection = dbOrCollection.collection<T>(collectionName);
+        } else {
+            throw new Error('Invalid MongoRepository constructor usage');
+        }
+    }
+
+    async create(data: Partial<T>): Promise<{ insertedId: ObjectId }> {
+        const result = await this.collection.insertOne(data as OptionalUnlessRequiredId<T>);
+        if (!result.acknowledged || !result.insertedId) {
+            throw new Error(`Failed to insert document into ${this.collection.collectionName}`);
+        }
+        return { insertedId: result.insertedId };
+    }
+
+    async findAll(filter: Filter<T> = {} as Filter<T>): Promise<WithId<T>[]> {
+        return this.collection.find(filter).toArray();
+    }
+
+    async findById(id: string): Promise<WithId<T> | null> {
+        return this.collection.findOne({ _id: new ObjectId(id) } as Filter<T>);
+    }
+
+    async update(id: string, update: Partial<T>): Promise<{ modifiedCount: number }> {
+        const result = await this.collection.updateOne(
+            { _id: new ObjectId(id) } as Filter<T>,
+            { $set: update } as UpdateFilter<T>,
+        );
+        return { modifiedCount: result.modifiedCount };
+    }
+
+    async delete(id: string): Promise<{ deletedCount: number }> {
+        const result = await this.collection.deleteOne({ _id: new ObjectId(id) } as Filter<T>);
+        return { deletedCount: result.deletedCount };
+    }
+}

+ 8 - 0
src/mongo/mongo.module.ts

@@ -0,0 +1,8 @@
+import { Module } from '@nestjs/common';
+import { MongoCoreService } from './mongo-core.service';
+
+@Module({
+  providers: [MongoCoreService],
+  exports: [MongoCoreService],
+})
+export class MongoModule {}

+ 0 - 0
src/common/schemas/plantation.node.schema.ts → src/plantation/plantation.node.schema.ts


+ 0 - 0
src/common/schemas/task.schema.ts → src/plantation/task.schema.ts


+ 0 - 0
src/common/schemas/worker.schema.ts → src/plantation/worker.schema.ts


+ 0 - 172
src/services/mongo.service.ts

@@ -1,172 +0,0 @@
-import { Injectable, OnModuleInit, OnModuleDestroy, Logger } from '@nestjs/common';
-import { MongoClient, Db, Collection, ObjectId } from 'mongodb';
-
-@Injectable()
-export class MongoService implements OnModuleInit, OnModuleDestroy {
-    private client: MongoClient;
-    private db: Db;
-    private readonly logger = new Logger(MongoService.name);
-
-    async onModuleInit() {
-        const uri = process.env.MONGO_URI;
-        if (!uri) throw new Error('MONGO_URI not set in environment variables');
-
-        this.client = new MongoClient(uri);
-        await this.client.connect();
-
-        this.db = this.client.db('swopt-ai-test-a');
-        this.logger.log('Connected to MongoDB');
-    }
-
-    async onModuleDestroy() {
-        await this.client.close();
-        this.logger.log('MongoDB connection closed');
-    }
-
-    // Get collection reference
-    private collection(name: string): Collection {
-        return this.db.collection(name);
-    }
-
-    /* ------------------------------
-     * CRUD OPERATIONS (activities)
-     * ------------------------------ */
-
-    async createActivity(data: any) {
-        const result = await this.collection('activities').insertOne(data);
-        return { insertedId: result.insertedId };
-    }
-
-    async getAllActivities(filter: Record<string, any> = {}) {
-        return this.collection('activities').find(filter).toArray();
-    }
-
-    async getActivityById(id: string) {
-        return this.collection('activities').findOne({ _id: new ObjectId(id) });
-    }
-
-    async updateActivity(id: string, update: Record<string, any>) {
-        const result = await this.collection('activities').updateOne(
-            { _id: new ObjectId(id) },
-            { $set: update },
-        );
-        return { modifiedCount: result.modifiedCount };
-    }
-
-    async deleteActivity(id: string) {
-        const result = await this.collection('activities').deleteOne({ _id: new ObjectId(id) });
-        return { deletedCount: result.deletedCount };
-    }
-
-    /* ------------------------------
-     * AGGREGATION EXAMPLES
-     * ------------------------------ */
-
-    // Example 1: Get total hours worked per resource type
-    async getTotalResourceHours() {
-        return this.collection('activities')
-            .aggregate([
-                { $unwind: '$resources' },
-                {
-                    $group: {
-                        _id: '$resources.type',
-                        totalHours: { $sum: { $toDecimal: '$resources.value.quantity' } },
-                    },
-                },
-            ])
-            .toArray();
-    }
-
-    // Example 2: Get total outputs by weight (e.g., total FFB harvested)
-    async getTotalOutputWeight() {
-        return this.collection('activities')
-            .aggregate([
-                { $unwind: '$outputs' },
-                {
-                    $group: {
-                        _id: '$outputs.type',
-                        totalWeightKg: { $sum: { $toDecimal: '$outputs.weightValue.weight' } },
-                    },
-                },
-            ])
-            .toArray();
-    }
-
-    // Example 3: Activities within date range
-    async getActivitiesByDateRange(start: Date, end: Date) {
-        return this.collection('activities')
-            .find({
-                dateStart: { $gte: start },
-                dateEnd: { $lte: end },
-            })
-            .toArray();
-    }
-
-
-    // 1️⃣ Total workers (optionally filter by date range or location)
-    async getTotalWorkers(filter: { start?: Date; end?: Date; location?: string } = {}) {
-        const matchStage: any = {};
-
-        if (filter.start && filter.end) {
-            matchStage.dateStart = { $gte: filter.start };
-            matchStage.dateEnd = { $lte: filter.end };
-        }
-
-        if (filter.location) {
-            matchStage['targets.name'] = filter.location;
-        }
-
-        const pipeline = [
-            { $match: Object.keys(matchStage).length ? matchStage : {} },
-            { $unwind: '$resources' },
-            { $match: { 'resources.type': 'worker' } },
-            {
-                $group: {
-                    _id: '$resources.name',
-                    totalHours: { $sum: { $toDecimal: '$resources.value.quantity' } },
-                },
-            },
-            {
-                $project: {
-                    _id: 0,
-                    worker: '$_id',
-                    totalHours: 1,
-                },
-            },
-        ];
-
-        return this.collection('activities').aggregate(pipeline).toArray();
-    }
-
-    // 2️⃣ Total outputs (bunch + weight, with optional date range)
-    async getTotalOutputs(filter: { start?: Date; end?: Date } = {}) {
-        const matchStage: any = {};
-
-        if (filter.start && filter.end) {
-            matchStage.dateStart = { $gte: filter.start };
-            matchStage.dateEnd = { $lte: filter.end };
-        }
-
-        const pipeline = [
-            { $match: Object.keys(matchStage).length ? matchStage : {} },
-            { $unwind: '$outputs' },
-            {
-                $group: {
-                    _id: '$outputs.type',
-                    totalBunch: { $sum: { $toDecimal: '$outputs.value.quantity' } },
-                    totalWeightKg: { $sum: { $toDecimal: '$outputs.weightValue.weight' } },
-                },
-            },
-            {
-                $project: {
-                    _id: 0,
-                    outputType: '$_id',
-                    totalBunch: 1,
-                    totalWeightKg: 1,
-                },
-            },
-        ];
-
-        return this.collection('activities').aggregate(pipeline).toArray();
-    }
-}

+ 2 - 3
src/services/service.module.ts

@@ -1,9 +1,8 @@
 import { Module } from '@nestjs/common';
 import { TreeService } from './tree.service';
-import { MongoService } from './mongo.service';
 
 @Module({
-  providers: [TreeService, MongoService],
-  exports: [TreeService, MongoService],
+  providers: [TreeService],
+  exports: [TreeService],
 })
 export class ServiceModule {}