mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-07-10 23:27:31 +00:00
feat: add occupancy module with controller, service, and related DTOs for heat map data retrieval
This commit is contained in:
@ -504,6 +504,17 @@ export class ControllerRoute {
|
|||||||
'This endpoint retrieves the historical data of power clamp devices based on the provided community or space UUID.';
|
'This endpoint retrieves the historical data of power clamp devices based on the provided community or space UUID.';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static Occupancy = class {
|
||||||
|
public static readonly ROUTE = 'occupancy';
|
||||||
|
|
||||||
|
static ACTIONS = class {
|
||||||
|
public static readonly GET_OCCUPANCY_HEAT_MAP_SUMMARY =
|
||||||
|
'Get occupancy heat map data';
|
||||||
|
public static readonly GET_OCCUPANCY_HEAT_MAP_DESCRIPTION =
|
||||||
|
'This endpoint retrieves the occupancy heat map data based on the provided parameters.';
|
||||||
|
};
|
||||||
|
};
|
||||||
static DEVICE = class {
|
static DEVICE = class {
|
||||||
public static readonly ROUTE = 'devices';
|
public static readonly ROUTE = 'devices';
|
||||||
|
|
||||||
|
4
libs/common/src/constants/presence.sensor.enum.ts
Normal file
4
libs/common/src/constants/presence.sensor.enum.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export enum PresenceSensorEnum {
|
||||||
|
PRESENCE_STATE = 'presence_state',
|
||||||
|
SENSITIVITY = 'sensitivity',
|
||||||
|
}
|
@ -10,6 +10,7 @@ import {
|
|||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
} from '@app/common/modules/power-clamp/repositories';
|
} from '@app/common/modules/power-clamp/repositories';
|
||||||
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
||||||
|
import { OccupancyService } from '@app/common/helper/services/occupancy.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
providers: [
|
providers: [
|
||||||
@ -21,6 +22,7 @@ import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service
|
|||||||
PowerClampDailyRepository,
|
PowerClampDailyRepository,
|
||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
SqlLoaderService,
|
SqlLoaderService,
|
||||||
|
OccupancyService,
|
||||||
],
|
],
|
||||||
controllers: [DeviceStatusFirebaseController],
|
controllers: [DeviceStatusFirebaseController],
|
||||||
exports: [DeviceStatusFirebaseService, DeviceStatusLogRepository],
|
exports: [DeviceStatusFirebaseService, DeviceStatusLogRepository],
|
||||||
|
@ -21,6 +21,8 @@ import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log
|
|||||||
import { ProductType } from '@app/common/constants/product-type.enum';
|
import { ProductType } from '@app/common/constants/product-type.enum';
|
||||||
import { PowerClampService } from '@app/common/helper/services/power.clamp.service';
|
import { PowerClampService } from '@app/common/helper/services/power.clamp.service';
|
||||||
import { PowerClampEnergyEnum } from '@app/common/constants/power.clamp.enargy.enum';
|
import { PowerClampEnergyEnum } from '@app/common/constants/power.clamp.enargy.enum';
|
||||||
|
import { PresenceSensorEnum } from '@app/common/constants/presence.sensor.enum';
|
||||||
|
import { OccupancyService } from '@app/common/helper/services/occupancy.service';
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DeviceStatusFirebaseService {
|
export class DeviceStatusFirebaseService {
|
||||||
private tuya: TuyaContext;
|
private tuya: TuyaContext;
|
||||||
@ -29,6 +31,7 @@ export class DeviceStatusFirebaseService {
|
|||||||
private readonly configService: ConfigService,
|
private readonly configService: ConfigService,
|
||||||
private readonly deviceRepository: DeviceRepository,
|
private readonly deviceRepository: DeviceRepository,
|
||||||
private readonly powerClampService: PowerClampService,
|
private readonly powerClampService: PowerClampService,
|
||||||
|
private readonly occupancyService: OccupancyService,
|
||||||
private deviceStatusLogRepository: DeviceStatusLogRepository,
|
private deviceStatusLogRepository: DeviceStatusLogRepository,
|
||||||
) {
|
) {
|
||||||
const accessKey = this.configService.get<string>('auth-config.ACCESS_KEY');
|
const accessKey = this.configService.get<string>('auth-config.ACCESS_KEY');
|
||||||
@ -240,6 +243,23 @@ export class DeviceStatusFirebaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
addDeviceStatusDto.productType === ProductType.CPS ||
|
||||||
|
addDeviceStatusDto.productType === ProductType.WPS
|
||||||
|
) {
|
||||||
|
const occupancyCodes = new Set([PresenceSensorEnum.PRESENCE_STATE]);
|
||||||
|
|
||||||
|
const occupancyStatus = addDeviceStatusDto?.log?.properties?.find(
|
||||||
|
(status) => occupancyCodes.has(status.code),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (occupancyStatus) {
|
||||||
|
await this.occupancyService.updateOccupancySensorHistoricalData(
|
||||||
|
addDeviceStatusDto.deviceUuid,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Return the updated data
|
// Return the updated data
|
||||||
const snapshot: DataSnapshot = await get(dataRef);
|
const snapshot: DataSnapshot = await get(dataRef);
|
||||||
return snapshot.val();
|
return snapshot.val();
|
||||||
|
49
libs/common/src/helper/services/occupancy.service.ts
Normal file
49
libs/common/src/helper/services/occupancy.service.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { SqlLoaderService } from './sql-loader.service';
|
||||||
|
import { DataSource } from 'typeorm';
|
||||||
|
import { SQL_PROCEDURES_PATH } from '@app/common/constants/sql-query-path';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class OccupancyService {
|
||||||
|
constructor(
|
||||||
|
private readonly sqlLoader: SqlLoaderService,
|
||||||
|
private readonly dataSource: DataSource,
|
||||||
|
private readonly deviceRepository: DeviceRepository,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async updateOccupancySensorHistoricalData(deviceUuid: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const now = new Date();
|
||||||
|
const dateStr = now.toLocaleDateString('en-CA'); // YYYY-MM-DD
|
||||||
|
const device = await this.deviceRepository.findOne({
|
||||||
|
where: { uuid: deviceUuid },
|
||||||
|
relations: ['spaceDevice'],
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.executeProcedure('procedure_update_fact_space_occupancy', [
|
||||||
|
dateStr,
|
||||||
|
device.spaceDevice?.uuid,
|
||||||
|
]);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to insert or update occupancy data:', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async executeProcedure(
|
||||||
|
procedureFileName: string,
|
||||||
|
params: (string | number | null)[],
|
||||||
|
): Promise<void> {
|
||||||
|
const query = this.loadQuery(
|
||||||
|
'fact_space_occupancy_count',
|
||||||
|
procedureFileName,
|
||||||
|
);
|
||||||
|
await this.dataSource.query(query, params);
|
||||||
|
console.log(`Procedure ${procedureFileName} executed successfully.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadQuery(folderName: string, fileName: string): string {
|
||||||
|
return this.sqlLoader.loadQuery(folderName, fileName, SQL_PROCEDURES_PATH);
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,15 @@
|
|||||||
WITH params AS (
|
WITH params AS (
|
||||||
SELECT
|
SELECT
|
||||||
$1::uuid AS space_id,
|
$1::uuid AS space_id,
|
||||||
TO_DATE(NULLIF($2, ''), 'YYYY-MM') AS event_month
|
TO_DATE(NULLIF($2, ''), 'YYYY') AS event_year
|
||||||
)
|
)
|
||||||
|
|
||||||
select psdsd.*
|
SELECT psdsd.*
|
||||||
from public."presence-sensor-daily-space-detection" psdsd
|
FROM public."presence-sensor-daily-space-detection" psdsd
|
||||||
JOIN params P ON true
|
JOIN params P ON true
|
||||||
where psdsd.space_uuid = P.space_id
|
WHERE psdsd.space_uuid = P.space_id
|
||||||
AND (P.event_month IS NULL OR TO_CHAR(psdsd.event_date, 'YYYY-MM') = TO_CHAR(P.event_month, 'YYYY-MM'))
|
AND (
|
||||||
|
P.event_year IS NULL
|
||||||
|
OR TO_CHAR(psdsd.event_date, 'YYYY') = TO_CHAR(P.event_year, 'YYYY')
|
||||||
|
)
|
||||||
ORDER BY space_uuid, event_date
|
ORDER BY space_uuid, event_date
|
@ -1,7 +1,7 @@
|
|||||||
WITH params AS (
|
WITH params AS (
|
||||||
SELECT
|
SELECT
|
||||||
TO_DATE(NULLIF($2, ''), 'YYYY-MM-DD') AS event_date,
|
TO_DATE(NULLIF($1, ''), 'YYYY-MM-DD') AS event_date,
|
||||||
$4::uuid AS space_id
|
$2::uuid AS space_id
|
||||||
),
|
),
|
||||||
|
|
||||||
device_logs AS (
|
device_logs AS (
|
||||||
|
@ -37,12 +37,13 @@ import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler';
|
|||||||
import { HealthModule } from './health/health.module';
|
import { HealthModule } from './health/health.module';
|
||||||
|
|
||||||
import { winstonLoggerOptions } from '../libs/common/src/logger/services/winston.logger';
|
import { winstonLoggerOptions } from '../libs/common/src/logger/services/winston.logger';
|
||||||
|
import { OccupancyModule } from './occupancy/occupancy.module';
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
ConfigModule.forRoot({
|
ConfigModule.forRoot({
|
||||||
load: config,
|
load: config,
|
||||||
}),
|
}),
|
||||||
/* ThrottlerModule.forRoot({
|
/* ThrottlerModule.forRoot({
|
||||||
throttlers: [{ ttl: 100000, limit: 30 }],
|
throttlers: [{ ttl: 100000, limit: 30 }],
|
||||||
}), */
|
}), */
|
||||||
WinstonModule.forRoot(winstonLoggerOptions),
|
WinstonModule.forRoot(winstonLoggerOptions),
|
||||||
@ -77,13 +78,14 @@ import { winstonLoggerOptions } from '../libs/common/src/logger/services/winston
|
|||||||
DeviceCommissionModule,
|
DeviceCommissionModule,
|
||||||
PowerClampModule,
|
PowerClampModule,
|
||||||
HealthModule,
|
HealthModule,
|
||||||
|
OccupancyModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: APP_INTERCEPTOR,
|
provide: APP_INTERCEPTOR,
|
||||||
useClass: LoggingInterceptor,
|
useClass: LoggingInterceptor,
|
||||||
},
|
},
|
||||||
/* {
|
/* {
|
||||||
provide: APP_GUARD,
|
provide: APP_GUARD,
|
||||||
useClass: ThrottlerGuard,
|
useClass: ThrottlerGuard,
|
||||||
}, */
|
}, */
|
||||||
|
@ -28,6 +28,7 @@ import {
|
|||||||
} from '@app/common/modules/power-clamp/repositories';
|
} from '@app/common/modules/power-clamp/repositories';
|
||||||
import { PowerClampService } from '@app/common/helper/services/power.clamp.service';
|
import { PowerClampService } from '@app/common/helper/services/power.clamp.service';
|
||||||
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
||||||
|
import { OccupancyService } from '@app/common/helper/services/occupancy.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ConfigModule, SpaceRepositoryModule],
|
imports: [ConfigModule, SpaceRepositoryModule],
|
||||||
@ -55,6 +56,7 @@ import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service
|
|||||||
PowerClampDailyRepository,
|
PowerClampDailyRepository,
|
||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
SqlLoaderService,
|
SqlLoaderService,
|
||||||
|
OccupancyService,
|
||||||
],
|
],
|
||||||
exports: [],
|
exports: [],
|
||||||
})
|
})
|
||||||
|
@ -63,6 +63,7 @@ import {
|
|||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
} from '@app/common/modules/power-clamp/repositories';
|
} from '@app/common/modules/power-clamp/repositories';
|
||||||
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
||||||
|
import { OccupancyService } from '@app/common/helper/services/occupancy.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule],
|
imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule],
|
||||||
@ -114,6 +115,7 @@ import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service
|
|||||||
PowerClampDailyRepository,
|
PowerClampDailyRepository,
|
||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
SqlLoaderService,
|
SqlLoaderService,
|
||||||
|
OccupancyService,
|
||||||
],
|
],
|
||||||
exports: [CommunityService, SpacePermissionService],
|
exports: [CommunityService, SpacePermissionService],
|
||||||
})
|
})
|
||||||
|
@ -27,6 +27,7 @@ import {
|
|||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
} from '@app/common/modules/power-clamp/repositories';
|
} from '@app/common/modules/power-clamp/repositories';
|
||||||
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
||||||
|
import { OccupancyService } from '@app/common/helper/services/occupancy.service';
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ConfigModule, DeviceRepositoryModule],
|
imports: [ConfigModule, DeviceRepositoryModule],
|
||||||
controllers: [DoorLockController],
|
controllers: [DoorLockController],
|
||||||
@ -52,6 +53,7 @@ import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service
|
|||||||
PowerClampDailyRepository,
|
PowerClampDailyRepository,
|
||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
SqlLoaderService,
|
SqlLoaderService,
|
||||||
|
OccupancyService,
|
||||||
],
|
],
|
||||||
exports: [DoorLockService],
|
exports: [DoorLockService],
|
||||||
})
|
})
|
||||||
|
@ -25,6 +25,7 @@ import {
|
|||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
} from '@app/common/modules/power-clamp/repositories';
|
} from '@app/common/modules/power-clamp/repositories';
|
||||||
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
||||||
|
import { OccupancyService } from '@app/common/helper/services/occupancy.service';
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ConfigModule, DeviceRepositoryModule],
|
imports: [ConfigModule, DeviceRepositoryModule],
|
||||||
controllers: [GroupController],
|
controllers: [GroupController],
|
||||||
@ -49,6 +50,7 @@ import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service
|
|||||||
PowerClampDailyRepository,
|
PowerClampDailyRepository,
|
||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
SqlLoaderService,
|
SqlLoaderService,
|
||||||
|
OccupancyService,
|
||||||
],
|
],
|
||||||
exports: [GroupService],
|
exports: [GroupService],
|
||||||
})
|
})
|
||||||
|
@ -83,6 +83,7 @@ import {
|
|||||||
} from '@app/common/modules/power-clamp/repositories';
|
} from '@app/common/modules/power-clamp/repositories';
|
||||||
import { PowerClampService } from '@app/common/helper/services/power.clamp.service';
|
import { PowerClampService } from '@app/common/helper/services/power.clamp.service';
|
||||||
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
||||||
|
import { OccupancyService } from '@app/common/helper/services/occupancy.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ConfigModule, InviteUserRepositoryModule, CommunityModule],
|
imports: [ConfigModule, InviteUserRepositoryModule, CommunityModule],
|
||||||
@ -152,6 +153,7 @@ import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service
|
|||||||
PowerClampDailyRepository,
|
PowerClampDailyRepository,
|
||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
SqlLoaderService,
|
SqlLoaderService,
|
||||||
|
OccupancyService,
|
||||||
],
|
],
|
||||||
exports: [InviteUserService],
|
exports: [InviteUserService],
|
||||||
})
|
})
|
||||||
|
1
src/occupancy/controllers/index.ts
Normal file
1
src/occupancy/controllers/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './occupancy.controller';
|
46
src/occupancy/controllers/occupancy.controller.ts
Normal file
46
src/occupancy/controllers/occupancy.controller.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { Controller, Get, Param, Query, UseGuards } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
ApiTags,
|
||||||
|
ApiBearerAuth,
|
||||||
|
ApiOperation,
|
||||||
|
ApiParam,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import { EnableDisableStatusEnum } from '@app/common/constants/days.enum';
|
||||||
|
import { ControllerRoute } from '@app/common/constants/controller-route';
|
||||||
|
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
|
||||||
|
import { OccupancyService } from '../services/occupancy.service';
|
||||||
|
import { GetOccupancyHeatMapBySpaceDto } from '../dto/get-occupancy.dto';
|
||||||
|
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||||
|
import { SpaceParamsDto } from '../dto/occupancy-params.dto';
|
||||||
|
|
||||||
|
@ApiTags('Occupancy Module')
|
||||||
|
@Controller({
|
||||||
|
version: EnableDisableStatusEnum.ENABLED,
|
||||||
|
path: ControllerRoute.Occupancy.ROUTE,
|
||||||
|
})
|
||||||
|
export class OccupancyController {
|
||||||
|
constructor(private readonly occupancyService: OccupancyService) {}
|
||||||
|
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Get('heat-map/space/:spaceUuid')
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ControllerRoute.Occupancy.ACTIONS.GET_OCCUPANCY_HEAT_MAP_SUMMARY,
|
||||||
|
description:
|
||||||
|
ControllerRoute.Occupancy.ACTIONS.GET_OCCUPANCY_HEAT_MAP_DESCRIPTION,
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: 'spaceUuid',
|
||||||
|
description: 'UUID of the Space',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
async getOccupancyHeatMapDataBySpace(
|
||||||
|
@Param() params: SpaceParamsDto,
|
||||||
|
@Query() query: GetOccupancyHeatMapBySpaceDto,
|
||||||
|
): Promise<BaseResponseDto> {
|
||||||
|
return await this.occupancyService.getOccupancyHeatMapDataBySpace(
|
||||||
|
params,
|
||||||
|
query,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
15
src/occupancy/dto/get-occupancy.dto.ts
Normal file
15
src/occupancy/dto/get-occupancy.dto.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
|
import { Matches, IsNotEmpty } from 'class-validator';
|
||||||
|
|
||||||
|
export class GetOccupancyHeatMapBySpaceDto {
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Input year in YYYY format to filter the data',
|
||||||
|
example: '2025',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsNotEmpty()
|
||||||
|
@Matches(/^\d{4}$/, {
|
||||||
|
message: 'Year must be in YYYY format',
|
||||||
|
})
|
||||||
|
year: string;
|
||||||
|
}
|
7
src/occupancy/dto/occupancy-params.dto.ts
Normal file
7
src/occupancy/dto/occupancy-params.dto.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { IsNotEmpty, IsUUID } from 'class-validator';
|
||||||
|
|
||||||
|
export class SpaceParamsDto {
|
||||||
|
@IsUUID('4', { message: 'Invalid UUID format' })
|
||||||
|
@IsNotEmpty()
|
||||||
|
spaceUuid: string;
|
||||||
|
}
|
11
src/occupancy/occupancy.module.ts
Normal file
11
src/occupancy/occupancy.module.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
import { OccupancyController } from './controllers';
|
||||||
|
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
||||||
|
import { OccupancyService } from './services';
|
||||||
|
@Module({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
controllers: [OccupancyController],
|
||||||
|
providers: [OccupancyService, SqlLoaderService],
|
||||||
|
})
|
||||||
|
export class OccupancyModule {}
|
1
src/occupancy/services/index.ts
Normal file
1
src/occupancy/services/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './occupancy.service';
|
69
src/occupancy/services/occupancy.service.ts
Normal file
69
src/occupancy/services/occupancy.service.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import { GetOccupancyHeatMapBySpaceDto } from '../dto/get-occupancy.dto';
|
||||||
|
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
||||||
|
import { SpaceParamsDto } from '../dto/occupancy-params.dto';
|
||||||
|
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
||||||
|
import { DataSource } from 'typeorm';
|
||||||
|
import { SQL_PROCEDURES_PATH } from '@app/common/constants/sql-query-path';
|
||||||
|
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class OccupancyService {
|
||||||
|
constructor(
|
||||||
|
private readonly sqlLoader: SqlLoaderService,
|
||||||
|
private readonly dataSource: DataSource,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async getOccupancyHeatMapDataBySpace(
|
||||||
|
params: SpaceParamsDto,
|
||||||
|
query: GetOccupancyHeatMapBySpaceDto,
|
||||||
|
): Promise<BaseResponseDto> {
|
||||||
|
const { year } = query;
|
||||||
|
const { spaceUuid } = params;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await this.executeProcedure(
|
||||||
|
'proceduce_select_fact_space_occupancy',
|
||||||
|
[spaceUuid, year],
|
||||||
|
);
|
||||||
|
const formattedData = data.map((item) => ({
|
||||||
|
...item,
|
||||||
|
event_date: new Date(item.event_date).toLocaleDateString('en-CA'), // YYYY-MM-DD
|
||||||
|
}));
|
||||||
|
return this.buildResponse(
|
||||||
|
`Occupancy heat map data fetched successfully for ${spaceUuid ? 'space' : 'community'}`,
|
||||||
|
formattedData,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch occupancy heat map data', {
|
||||||
|
error,
|
||||||
|
spaceUuid,
|
||||||
|
});
|
||||||
|
throw new HttpException(
|
||||||
|
error.response?.message || 'Failed to fetch occupancy heat map data',
|
||||||
|
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildResponse(message: string, data: any[]) {
|
||||||
|
return new SuccessResponseDto({
|
||||||
|
message,
|
||||||
|
data,
|
||||||
|
statusCode: HttpStatus.OK,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
private async executeProcedure(
|
||||||
|
procedureFileName: string,
|
||||||
|
params: (string | number | null)[],
|
||||||
|
): Promise<any[]> {
|
||||||
|
const query = this.loadQuery(
|
||||||
|
'fact_space_occupancy_count',
|
||||||
|
procedureFileName,
|
||||||
|
);
|
||||||
|
return await this.dataSource.query(query, params);
|
||||||
|
}
|
||||||
|
private loadQuery(folderName: string, fileName: string): string {
|
||||||
|
return this.sqlLoader.loadQuery(folderName, fileName, SQL_PROCEDURES_PATH);
|
||||||
|
}
|
||||||
|
}
|
@ -60,6 +60,7 @@ import { SubspaceProductAllocationService } from 'src/space/services/subspace/su
|
|||||||
import { NewTagRepository } from '@app/common/modules/tag/repositories/tag-repository';
|
import { NewTagRepository } from '@app/common/modules/tag/repositories/tag-repository';
|
||||||
import { SpaceModelProductAllocationService } from 'src/space-model/services/space-model-product-allocation.service';
|
import { SpaceModelProductAllocationService } from 'src/space-model/services/space-model-product-allocation.service';
|
||||||
import { SubspaceModelProductAllocationService } from 'src/space-model/services/subspace/subspace-model-product-allocation.service';
|
import { SubspaceModelProductAllocationService } from 'src/space-model/services/subspace/subspace-model-product-allocation.service';
|
||||||
|
import { OccupancyService } from '@app/common/helper/services/occupancy.service';
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ConfigModule],
|
imports: [ConfigModule],
|
||||||
controllers: [PowerClampController],
|
controllers: [PowerClampController],
|
||||||
@ -109,6 +110,7 @@ import { SubspaceModelProductAllocationService } from 'src/space-model/services/
|
|||||||
SubspaceModelProductAllocationService,
|
SubspaceModelProductAllocationService,
|
||||||
SpaceModelProductAllocationRepoitory,
|
SpaceModelProductAllocationRepoitory,
|
||||||
SubspaceModelProductAllocationRepoitory,
|
SubspaceModelProductAllocationRepoitory,
|
||||||
|
OccupancyService,
|
||||||
],
|
],
|
||||||
exports: [PowerClamp],
|
exports: [PowerClamp],
|
||||||
})
|
})
|
||||||
|
@ -67,6 +67,7 @@ import {
|
|||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
} from '@app/common/modules/power-clamp/repositories';
|
} from '@app/common/modules/power-clamp/repositories';
|
||||||
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
||||||
|
import { OccupancyService } from '@app/common/helper/services/occupancy.service';
|
||||||
|
|
||||||
const CommandHandlers = [CreateOrphanSpaceHandler];
|
const CommandHandlers = [CreateOrphanSpaceHandler];
|
||||||
|
|
||||||
@ -124,6 +125,7 @@ const CommandHandlers = [CreateOrphanSpaceHandler];
|
|||||||
PowerClampDailyRepository,
|
PowerClampDailyRepository,
|
||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
SqlLoaderService,
|
SqlLoaderService,
|
||||||
|
OccupancyService,
|
||||||
],
|
],
|
||||||
exports: [ProjectService, CqrsModule],
|
exports: [ProjectService, CqrsModule],
|
||||||
})
|
})
|
||||||
|
@ -65,6 +65,7 @@ import {
|
|||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
} from '@app/common/modules/power-clamp/repositories';
|
} from '@app/common/modules/power-clamp/repositories';
|
||||||
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
||||||
|
import { OccupancyService } from '@app/common/helper/services/occupancy.service';
|
||||||
|
|
||||||
const CommandHandlers = [
|
const CommandHandlers = [
|
||||||
PropogateUpdateSpaceModelHandler,
|
PropogateUpdateSpaceModelHandler,
|
||||||
@ -124,6 +125,7 @@ const CommandHandlers = [
|
|||||||
PowerClampDailyRepository,
|
PowerClampDailyRepository,
|
||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
SqlLoaderService,
|
SqlLoaderService,
|
||||||
|
OccupancyService,
|
||||||
],
|
],
|
||||||
exports: [CqrsModule, SpaceModelService],
|
exports: [CqrsModule, SpaceModelService],
|
||||||
})
|
})
|
||||||
|
@ -90,6 +90,7 @@ import {
|
|||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
} from '@app/common/modules/power-clamp/repositories';
|
} from '@app/common/modules/power-clamp/repositories';
|
||||||
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
||||||
|
import { OccupancyService } from '@app/common/helper/services/occupancy.service';
|
||||||
|
|
||||||
export const CommandHandlers = [DisableSpaceHandler];
|
export const CommandHandlers = [DisableSpaceHandler];
|
||||||
|
|
||||||
@ -166,6 +167,7 @@ export const CommandHandlers = [DisableSpaceHandler];
|
|||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
PowerClampService,
|
PowerClampService,
|
||||||
SqlLoaderService,
|
SqlLoaderService,
|
||||||
|
OccupancyService,
|
||||||
],
|
],
|
||||||
exports: [SpaceService],
|
exports: [SpaceService],
|
||||||
})
|
})
|
||||||
|
@ -29,6 +29,7 @@ import {
|
|||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
} from '@app/common/modules/power-clamp/repositories';
|
} from '@app/common/modules/power-clamp/repositories';
|
||||||
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
||||||
|
import { OccupancyService } from '@app/common/helper/services/occupancy.service';
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ConfigModule, DeviceRepositoryModule, DoorLockModule],
|
imports: [ConfigModule, DeviceRepositoryModule, DoorLockModule],
|
||||||
controllers: [VisitorPasswordController],
|
controllers: [VisitorPasswordController],
|
||||||
@ -55,6 +56,7 @@ import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service
|
|||||||
PowerClampDailyRepository,
|
PowerClampDailyRepository,
|
||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
SqlLoaderService,
|
SqlLoaderService,
|
||||||
|
OccupancyService,
|
||||||
],
|
],
|
||||||
exports: [VisitorPasswordService],
|
exports: [VisitorPasswordService],
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user