feat: add occupancy duration data retrieval and update procedures

This commit is contained in:
faris Aljohari
2025-05-18 17:40:41 +03:00
parent dca3db0c59
commit 180d16eeb1
5 changed files with 108 additions and 15 deletions

View File

@ -257,6 +257,9 @@ export class DeviceStatusFirebaseService {
await this.occupancyService.updateOccupancySensorHistoricalData( await this.occupancyService.updateOccupancySensorHistoricalData(
addDeviceStatusDto.deviceUuid, addDeviceStatusDto.deviceUuid,
); );
await this.occupancyService.updateOccupancySensorHistoricalDurationData(
addDeviceStatusDto.deviceUuid,
);
} }
} }

View File

@ -11,7 +11,27 @@ export class OccupancyService {
private readonly dataSource: DataSource, private readonly dataSource: DataSource,
private readonly deviceRepository: DeviceRepository, private readonly deviceRepository: DeviceRepository,
) {} ) {}
async updateOccupancySensorHistoricalDurationData(
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(
'fact_daily_space_occupancy_duration',
'procedure_update_daily_space_occupancy_duration',
[dateStr, device.spaceDevice?.uuid],
);
} catch (err) {
console.error('Failed to insert or update occupancy duration data:', err);
throw err;
}
}
async updateOccupancySensorHistoricalData(deviceUuid: string): Promise<void> { async updateOccupancySensorHistoricalData(deviceUuid: string): Promise<void> {
try { try {
const now = new Date(); const now = new Date();
@ -21,10 +41,11 @@ export class OccupancyService {
relations: ['spaceDevice'], relations: ['spaceDevice'],
}); });
await this.executeProcedure('procedure_update_fact_space_occupancy', [ await this.executeProcedure(
dateStr, 'fact_space_occupancy_count',
device.spaceDevice?.uuid, 'procedure_update_fact_space_occupancy',
]); [dateStr, device.spaceDevice?.uuid],
);
} catch (err) { } catch (err) {
console.error('Failed to insert or update occupancy data:', err); console.error('Failed to insert or update occupancy data:', err);
throw err; throw err;
@ -32,13 +53,11 @@ export class OccupancyService {
} }
private async executeProcedure( private async executeProcedure(
procedureFolderName: string,
procedureFileName: string, procedureFileName: string,
params: (string | number | null)[], params: (string | number | null)[],
): Promise<void> { ): Promise<void> {
const query = this.loadQuery( const query = this.loadQuery(procedureFolderName, procedureFileName);
'fact_space_occupancy_count',
procedureFileName,
);
await this.dataSource.query(query, params); await this.dataSource.query(query, params);
console.log(`Procedure ${procedureFileName} executed successfully.`); console.log(`Procedure ${procedureFileName} executed successfully.`);
} }

View File

@ -9,7 +9,10 @@ import { EnableDisableStatusEnum } from '@app/common/constants/days.enum';
import { ControllerRoute } from '@app/common/constants/controller-route'; import { ControllerRoute } from '@app/common/constants/controller-route';
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
import { OccupancyService } from '../services/occupancy.service'; import { OccupancyService } from '../services/occupancy.service';
import { GetOccupancyHeatMapBySpaceDto } from '../dto/get-occupancy.dto'; import {
GetOccupancyDurationBySpaceDto,
GetOccupancyHeatMapBySpaceDto,
} from '../dto/get-occupancy.dto';
import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { BaseResponseDto } from '@app/common/dto/base.response.dto';
import { SpaceParamsDto } from '../dto/occupancy-params.dto'; import { SpaceParamsDto } from '../dto/occupancy-params.dto';
@ -43,4 +46,26 @@ export class OccupancyController {
query, query,
); );
} }
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Get('duration/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 getOccupancyDurationDataBySpace(
@Param() params: SpaceParamsDto,
@Query() query: GetOccupancyDurationBySpaceDto,
): Promise<BaseResponseDto> {
return await this.occupancyService.getOccupancyDurationDataBySpace(
params,
query,
);
}
} }

View File

@ -13,3 +13,15 @@ export class GetOccupancyHeatMapBySpaceDto {
}) })
year: string; year: string;
} }
export class GetOccupancyDurationBySpaceDto {
@ApiPropertyOptional({
description: 'Month and year in format YYYY-MM',
example: '2025-03',
required: true,
})
@Matches(/^\d{4}-(0[1-9]|1[0-2])$/, {
message: 'monthDate must be in YYYY-MM format',
})
@IsNotEmpty()
monthDate: string;
}

View File

@ -1,5 +1,8 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { GetOccupancyHeatMapBySpaceDto } from '../dto/get-occupancy.dto'; import {
GetOccupancyDurationBySpaceDto,
GetOccupancyHeatMapBySpaceDto,
} from '../dto/get-occupancy.dto';
import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
import { SpaceParamsDto } from '../dto/occupancy-params.dto'; import { SpaceParamsDto } from '../dto/occupancy-params.dto';
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service'; import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
@ -14,6 +17,38 @@ export class OccupancyService {
private readonly dataSource: DataSource, private readonly dataSource: DataSource,
) {} ) {}
async getOccupancyDurationDataBySpace(
params: SpaceParamsDto,
query: GetOccupancyDurationBySpaceDto,
): Promise<BaseResponseDto> {
const { monthDate } = query;
const { spaceUuid } = params;
try {
const data = await this.executeProcedure(
'fact_daily_space_occupancy_duration',
'procedure_select_daily_space_occupancy_duration',
[spaceUuid, monthDate],
);
const formattedData = data.map((item) => ({
...item,
event_date: new Date(item.event_date).toLocaleDateString('en-CA'), // YYYY-MM-DD
}));
return this.buildResponse(
`Occupancy duration data fetched successfully for ${spaceUuid} space`,
formattedData,
);
} catch (error) {
console.error('Failed to fetch occupancy duration data', {
error,
spaceUuid,
});
throw new HttpException(
error.response?.message || 'Failed to fetch occupancy duration data',
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
async getOccupancyHeatMapDataBySpace( async getOccupancyHeatMapDataBySpace(
params: SpaceParamsDto, params: SpaceParamsDto,
query: GetOccupancyHeatMapBySpaceDto, query: GetOccupancyHeatMapBySpaceDto,
@ -23,6 +58,7 @@ export class OccupancyService {
try { try {
const data = await this.executeProcedure( const data = await this.executeProcedure(
'fact_space_occupancy_count',
'proceduce_select_fact_space_occupancy', 'proceduce_select_fact_space_occupancy',
[spaceUuid, year], [spaceUuid, year],
); );
@ -31,7 +67,7 @@ export class OccupancyService {
event_date: new Date(item.event_date).toLocaleDateString('en-CA'), // YYYY-MM-DD event_date: new Date(item.event_date).toLocaleDateString('en-CA'), // YYYY-MM-DD
})); }));
return this.buildResponse( return this.buildResponse(
`Occupancy heat map data fetched successfully for ${spaceUuid ? 'space' : 'community'}`, `Occupancy heat map data fetched successfully for ${spaceUuid} space`,
formattedData, formattedData,
); );
} catch (error) { } catch (error) {
@ -54,13 +90,11 @@ export class OccupancyService {
}); });
} }
private async executeProcedure( private async executeProcedure(
procedureFolderName: string,
procedureFileName: string, procedureFileName: string,
params: (string | number | null)[], params: (string | number | null)[],
): Promise<any[]> { ): Promise<any[]> {
const query = this.loadQuery( const query = this.loadQuery(procedureFolderName, procedureFileName);
'fact_space_occupancy_count',
procedureFileName,
);
return await this.dataSource.query(query, params); return await this.dataSource.query(query, params);
} }
private loadQuery(folderName: string, fileName: string): string { private loadQuery(folderName: string, fileName: string): string {