diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index 852c4a8..dcc7b5e 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -498,10 +498,6 @@ export class ControllerRoute { 'Get power clamp historical data'; public static readonly GET_ENERGY_DESCRIPTION = 'This endpoint retrieves the historical data of a power clamp device based on the provided parameters.'; - public static readonly GET_ENERGY_BY_COMMUNITY_OR_SPACE_SUMMARY = - 'Get power clamp historical data by community or space'; - public static readonly GET_ENERGY_BY_COMMUNITY_OR_SPACE_DESCRIPTION = - 'This endpoint retrieves the historical data of power clamp devices based on the provided community or space UUID.'; }; }; static DEVICE = class { diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index f65cb1d..fa83c76 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -51,7 +51,6 @@ import { PowerClampHourlyEntity, PowerClampMonthlyEntity, } from '../modules/power-clamp/entities/power-clamp.entity'; -import { PresenceSensorDailyEntity } from '../modules/presence-sensor/entities'; @Module({ imports: [ TypeOrmModule.forRootAsync({ @@ -110,7 +109,6 @@ import { PresenceSensorDailyEntity } from '../modules/presence-sensor/entities'; PowerClampHourlyEntity, PowerClampDailyEntity, PowerClampMonthlyEntity, - PresenceSensorDailyEntity, ], namingStrategy: new SnakeNamingStrategy(), synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))), diff --git a/libs/common/src/helper/date-format.ts b/libs/common/src/helper/date-format.ts deleted file mode 100644 index 6366849..0000000 --- a/libs/common/src/helper/date-format.ts +++ /dev/null @@ -1,38 +0,0 @@ -export function toDDMMYYYY(dateString?: string | null): string | null { - if (!dateString) return null; - - // Ensure dateString is valid format YYYY-MM-DD - const regex = /^\d{4}-\d{2}-\d{2}$/; - if (!regex.test(dateString)) { - throw new Error( - `Invalid date format: ${dateString}. Expected format is YYYY-MM-DD`, - ); - } - - const [year, month, day] = dateString.split('-'); - return `${day}-${month}-${year}`; -} -export function toMMYYYY(dateString?: string | null): string | null { - if (!dateString) return null; - - // Ensure dateString is valid format YYYY-MM - const regex = /^\d{4}-\d{2}$/; - if (!regex.test(dateString)) { - throw new Error( - `Invalid date format: ${dateString}. Expected format is YYYY-MM`, - ); - } - - const [year, month] = dateString.split('-'); - return `${month}-${year}`; -} -export function filterByMonth(data: any[], monthDate: string) { - const [year, month] = monthDate.split('-').map(Number); - - return data.filter((item) => { - const itemDate = new Date(item.date); - return ( - itemDate.getUTCFullYear() === year && itemDate.getUTCMonth() + 1 === month - ); - }); -} diff --git a/libs/common/src/helper/services/power.clamp.service.ts b/libs/common/src/helper/services/power.clamp.service.ts index 7c83208..b0e852f 100644 --- a/libs/common/src/helper/services/power.clamp.service.ts +++ b/libs/common/src/helper/services/power.clamp.service.ts @@ -22,20 +22,21 @@ export class PowerClampService { }) .replace('/', '-'); // MM-YYYY - await this.executeProcedure( - 'fact_hourly_device_energy_consumed_procedure', - [deviceUuid, dateStr, hour], - ); + await this.executeProcedure('fact_hourly_energy_consumed_procedure', [ + deviceUuid, + dateStr, + hour, + ]); - await this.executeProcedure( - 'fact_daily_device_energy_consumed_procedure', - [deviceUuid, dateStr], - ); + await this.executeProcedure('fact_daily_energy_consumed_procedure', [ + deviceUuid, + dateStr, + ]); - await this.executeProcedure( - 'fact_monthly_device_energy_consumed_procedure', - [deviceUuid, monthYear], - ); + await this.executeProcedure('fact_monthly_energy_consumed_procedure', [ + deviceUuid, + monthYear, + ]); } catch (err) { console.error('Failed to insert or update energy data:', err); throw err; @@ -46,15 +47,15 @@ export class PowerClampService { procedureFileName: string, params: (string | number | null)[], ): Promise { - const query = this.loadQuery( - 'fact_device_energy_consumed', - procedureFileName, - ); + const query = this.loadQuery(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); + private loadQuery(fileName: string): string { + return this.sqlLoader.loadQuery( + 'fact_energy_consumed', + fileName, + SQL_PROCEDURES_PATH, + ); } } diff --git a/libs/common/src/logger/services/winston.logger.ts b/libs/common/src/logger/services/winston.logger.ts index 9e8fa11..34b6a75 100644 --- a/libs/common/src/logger/services/winston.logger.ts +++ b/libs/common/src/logger/services/winston.logger.ts @@ -1,43 +1,26 @@ import { utilities as nestWinstonModuleUtilities } from 'nest-winston'; import * as winston from 'winston'; -const environment = process.env.NODE_ENV || 'local'; export const winstonLoggerOptions: winston.LoggerOptions = { level: - environment === 'local' - ? 'debug' - : environment === 'development' - ? 'warn' - : 'error', + process.env.AZURE_POSTGRESQL_DATABASE === 'development' ? 'debug' : 'error', transports: [ new winston.transports.Console({ - level: - environment === 'local' - ? 'debug' - : environment === 'development' - ? 'warn' - : 'error', format: winston.format.combine( winston.format.timestamp(), nestWinstonModuleUtilities.format.nestLike('MyApp', { - prettyPrint: environment === 'local', + prettyPrint: true, }), ), }), - // Only create file logs if NOT local - ...(environment !== 'local' - ? [ - new winston.transports.File({ - filename: 'logs/error.log', - level: 'error', - format: winston.format.json(), - }), - new winston.transports.File({ - filename: 'logs/combined.log', - level: 'info', - format: winston.format.json(), - }), - ] - : []), + new winston.transports.File({ + filename: 'logs/error.log', + level: 'error', + format: winston.format.json(), + }), + new winston.transports.File({ + filename: 'logs/combined.log', + format: winston.format.json(), + }), ], }; diff --git a/libs/common/src/modules/device/entities/device.entity.ts b/libs/common/src/modules/device/entities/device.entity.ts index 4dc9519..9015c40 100644 --- a/libs/common/src/modules/device/entities/device.entity.ts +++ b/libs/common/src/modules/device/entities/device.entity.ts @@ -18,7 +18,6 @@ import { SpaceEntity } from '../../space/entities/space.entity'; import { SubspaceEntity } from '../../space/entities/subspace/subspace.entity'; import { NewTagEntity } from '../../tag'; import { PowerClampHourlyEntity } from '../../power-clamp/entities/power-clamp.entity'; -import { PresenceSensorDailyEntity } from '../../presence-sensor/entities'; @Entity({ name: 'device' }) @Unique(['deviceTuyaUuid']) @@ -83,8 +82,6 @@ export class DeviceEntity extends AbstractEntity { public tag: NewTagEntity; @OneToMany(() => PowerClampHourlyEntity, (powerClamp) => powerClamp.device) powerClampHourly: PowerClampHourlyEntity[]; - @OneToMany(() => PresenceSensorDailyEntity, (sensor) => sensor.device) - presenceSensorDaily: PresenceSensorDailyEntity[]; constructor(partial: Partial) { super(); Object.assign(this, partial); diff --git a/libs/common/src/modules/presence-sensor/dtos/index.ts b/libs/common/src/modules/presence-sensor/dtos/index.ts deleted file mode 100644 index 9993c83..0000000 --- a/libs/common/src/modules/presence-sensor/dtos/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './presence-sensor.dto'; diff --git a/libs/common/src/modules/presence-sensor/dtos/presence-sensor.dto.ts b/libs/common/src/modules/presence-sensor/dtos/presence-sensor.dto.ts deleted file mode 100644 index e37f9db..0000000 --- a/libs/common/src/modules/presence-sensor/dtos/presence-sensor.dto.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; - -export class PresenceSensorDto { - @IsString() - @IsNotEmpty() - public uuid: string; - - @IsString() - @IsNotEmpty() - public deviceUuid: string; - - @IsString() - @IsNotEmpty() - public eventDate: string; - - @IsNumber() - @IsNotEmpty() - public CountMotionDetected: number; - - @IsNumber() - @IsNotEmpty() - public CountPresenceDetected: number; - - @IsNumber() - @IsNotEmpty() - public CountTotalPresenceDetected: number; -} diff --git a/libs/common/src/modules/presence-sensor/entities/index.ts b/libs/common/src/modules/presence-sensor/entities/index.ts deleted file mode 100644 index b578244..0000000 --- a/libs/common/src/modules/presence-sensor/entities/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './presence-sensor.entity'; diff --git a/libs/common/src/modules/presence-sensor/entities/presence-sensor.entity.ts b/libs/common/src/modules/presence-sensor/entities/presence-sensor.entity.ts deleted file mode 100644 index 710b245..0000000 --- a/libs/common/src/modules/presence-sensor/entities/presence-sensor.entity.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Column, Entity, ManyToOne, Unique } from 'typeorm'; -import { AbstractEntity } from '../../abstract/entities/abstract.entity'; -import { PresenceSensorDto } from '../dtos'; -import { DeviceEntity } from '../../device/entities/device.entity'; - -@Entity({ name: 'presence-sensor-daily-detection' }) -@Unique(['deviceUuid', 'eventDate']) -export class PresenceSensorDailyEntity extends AbstractEntity { - @Column({ nullable: false }) - public deviceUuid: string; - - @Column({ nullable: false, type: 'date' }) - public eventDate: string; - - @Column({ nullable: false }) - public CountMotionDetected: number; - - @Column({ nullable: false }) - public CountPresenceDetected: number; - - @Column({ nullable: false }) - public CountTotalPresenceDetected: number; - - @ManyToOne(() => DeviceEntity, (device) => device.presenceSensorDaily) - device: DeviceEntity; - - constructor(partial: Partial) { - super(); - Object.assign(this, partial); - } -} diff --git a/libs/common/src/modules/presence-sensor/presence-sensor.repository.module.ts b/libs/common/src/modules/presence-sensor/presence-sensor.repository.module.ts deleted file mode 100644 index 54849b2..0000000 --- a/libs/common/src/modules/presence-sensor/presence-sensor.repository.module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { PresenceSensorDailyEntity } from './entities/presence-sensor.entity'; - -@Module({ - providers: [], - exports: [], - controllers: [], - imports: [TypeOrmModule.forFeature([PresenceSensorDailyEntity])], -}) -export class PresenceSensorRepositoryModule {} diff --git a/libs/common/src/modules/presence-sensor/repositories/index.ts b/libs/common/src/modules/presence-sensor/repositories/index.ts deleted file mode 100644 index 8b64ee8..0000000 --- a/libs/common/src/modules/presence-sensor/repositories/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './presence-sensor.repository'; diff --git a/libs/common/src/modules/presence-sensor/repositories/presence-sensor.repository.ts b/libs/common/src/modules/presence-sensor/repositories/presence-sensor.repository.ts deleted file mode 100644 index 2dcd8bc..0000000 --- a/libs/common/src/modules/presence-sensor/repositories/presence-sensor.repository.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { DataSource, Repository } from 'typeorm'; -import { Injectable } from '@nestjs/common'; -import { PresenceSensorDailyEntity } from '../entities'; - -@Injectable() -export class PresenceSensorDailyRepository extends Repository { - constructor(private dataSource: DataSource) { - super(PresenceSensorDailyEntity, dataSource.createEntityManager()); - } -} diff --git a/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_daily_device_energy_consumed_procedure.sql b/libs/common/src/sql/procedures/fact_energy_consumed/fact_daily_energy_consumed_procedure.sql similarity index 100% rename from libs/common/src/sql/procedures/fact_device_energy_consumed/fact_daily_device_energy_consumed_procedure.sql rename to libs/common/src/sql/procedures/fact_energy_consumed/fact_daily_energy_consumed_procedure.sql diff --git a/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_hourly_device_energy_consumed_procedure.sql b/libs/common/src/sql/procedures/fact_energy_consumed/fact_hourly_energy_consumed_procedure.sql similarity index 100% rename from libs/common/src/sql/procedures/fact_device_energy_consumed/fact_hourly_device_energy_consumed_procedure.sql rename to libs/common/src/sql/procedures/fact_energy_consumed/fact_hourly_energy_consumed_procedure.sql diff --git a/libs/common/src/sql/procedures/fact_device_energy_consumed/fact_monthly_device_energy_consumed_procedure.sql b/libs/common/src/sql/procedures/fact_energy_consumed/fact_monthly_energy_consumed_procedure.sql similarity index 100% rename from libs/common/src/sql/procedures/fact_device_energy_consumed/fact_monthly_device_energy_consumed_procedure.sql rename to libs/common/src/sql/procedures/fact_energy_consumed/fact_monthly_energy_consumed_procedure.sql diff --git a/libs/common/src/sql/procedures/fact_space_energy_consumed/fact_daily_space_energy_consumed_procedure.sql b/libs/common/src/sql/procedures/fact_space_energy_consumed/fact_daily_space_energy_consumed_procedure.sql deleted file mode 100644 index 7cd49a5..0000000 --- a/libs/common/src/sql/procedures/fact_space_energy_consumed/fact_daily_space_energy_consumed_procedure.sql +++ /dev/null @@ -1,20 +0,0 @@ -WITH params AS ( - SELECT - TO_DATE(NULLIF($1, ''), 'MM-YYYY') AS month, - string_to_array(NULLIF($2, ''), ',') AS device_ids -) - -SELECT - A.date, - SUM(A.energy_consumed_kW::numeric) AS total_energy_consumed_KW, - SUM(A.energy_consumed_A::numeric) AS total_energy_consumed_A, - SUM(A.energy_consumed_B::numeric) AS total_energy_consumed_B, - SUM(A.energy_consumed_C::numeric) AS total_energy_consumed_C -FROM public."power-clamp-energy-consumed-daily" AS A -JOIN public.device AS B - ON A.device_uuid::TEXT = B."uuid"::TEXT -JOIN params P ON TRUE -WHERE B."uuid"::TEXT = ANY(P.device_ids) - AND (P.month IS NULL OR date_trunc('month', A.date)= P.month) -GROUP BY A.date -ORDER BY A.date; diff --git a/libs/common/src/sql/procedures/fact_space_energy_consumed/fact_monthly_space_energy_consumed_procedure.sql b/libs/common/src/sql/procedures/fact_space_energy_consumed/fact_monthly_space_energy_consumed_procedure.sql deleted file mode 100644 index 15c5765..0000000 --- a/libs/common/src/sql/procedures/fact_space_energy_consumed/fact_monthly_space_energy_consumed_procedure.sql +++ /dev/null @@ -1,21 +0,0 @@ -WITH params AS ( - SELECT - TO_DATE(NULLIF($1, ''), 'DD-MM-YYYY') AS start_date, - TO_DATE(NULLIF($2, ''), 'DD-MM-YYYY') AS end_date, - string_to_array(NULLIF($3, ''), ',') AS device_ids -) - -SELECT TO_CHAR(A.date, 'MM-YYYY') AS month, - SUM(A.energy_consumed_kW::numeric) AS total_energy_consumed_KW, - SUM(A.energy_consumed_A::numeric) AS total_energy_consumed_A, - SUM(A.energy_consumed_B::numeric) AS total_energy_consumed_B, - SUM(A.energy_consumed_C::numeric) AS total_energy_consumed_C -FROM public."power-clamp-energy-consumed-daily" AS A -JOIN public.device AS B - ON A.device_uuid::TEXT = B."uuid"::TEXT -JOIN params P ON TRUE -WHERE B."uuid"::TEXT = ANY(P.device_ids) - AND (P.start_date IS NULL OR A.date >= P.start_date) - AND (P.end_date IS NULL OR A.date <= P.end_date) -GROUP BY 1 - diff --git a/libs/common/src/sql/procedures/fact_space_occupancy/procedure_insert_fact_daily_space_occupancy.sql b/libs/common/src/sql/procedures/fact_space_occupancy/procedure_insert_fact_daily_space_occupancy.sql deleted file mode 100644 index 95036eb..0000000 --- a/libs/common/src/sql/procedures/fact_space_occupancy/procedure_insert_fact_daily_space_occupancy.sql +++ /dev/null @@ -1,115 +0,0 @@ -WITH params AS ( - SELECT - TO_DATE(NULLIF($2, ''), 'YYYY-MM-DD') AS event_date, - $4::text AS device_id -), - -device_logs AS ( - SELECT - device.uuid AS device_id, - device.created_at, - device.device_tuya_uuid, - device.space_device_uuid AS space_id, - "device-status-log".event_id, - "device-status-log".event_time::timestamp, - "device-status-log".code, - "device-status-log".value, - "device-status-log".log, - LAG("device-status-log".event_time::timestamp) - OVER (PARTITION BY device.uuid - ORDER BY "device-status-log".event_time) AS prev_timestamp, - LAG("device-status-log".value) - OVER (PARTITION BY device.uuid - ORDER BY "device-status-log".event_time) AS prev_value - FROM device - LEFT JOIN "device-status-log" - ON device.uuid = "device-status-log".device_id - LEFT JOIN product - ON product.uuid = device.product_device_uuid - WHERE product.cat_name = 'hps' - AND "device-status-log".code = 'presence_state' - AND device.uuid::text = P.device_id - -), - -presence_detection AS ( - SELECT *, - CASE - WHEN value = 'motion' AND prev_value = 'none' THEN 1 ELSE 0 - END AS motion_detected, - CASE - WHEN value = 'presence' AND prev_value = 'none' THEN 1 ELSE 0 - END AS presence_detected - FROM device_logs -), - -presence_detection_summary AS ( - SELECT - pd.device_id, - d.subspace_id, - pd.space_id, - pd.event_time::date AS event_date, - EXTRACT(HOUR FROM pd.event_time)::int AS event_hour, - SUM(motion_detected) AS count_motion_detected, - SUM(presence_detected) AS count_presence_detected, - SUM(motion_detected + presence_detected) AS count_total_presence_detected - FROM presence_detection pd - LEFT JOIN device d ON d.uuid = pd.device_id - JOIN params P ON TRUE - AND (P.event_date IS NULL OR pd.event_time::date = P.event_date) - GROUP BY 1, 2, 3, 4, 5 -), - -all_dates_and_hours AS ( - SELECT device_id, subspace_id, space_id, event_date, event_hour - FROM ( - SELECT DISTINCT device_id, subspace_id, space_id, event_date - FROM presence_detection_summary - ) d - CROSS JOIN generate_series(0, 23) AS event_hour -), - -table_final AS ( - SELECT - adah.device_id, - adah.event_date, - COALESCE(pds.count_motion_detected, 0) AS count_motion_detected, - COALESCE(pds.count_presence_detected, 0) AS count_presence_detected, - COALESCE(pds.count_total_presence_detected, 0) AS count_total_presence_detected - FROM all_dates_and_hours adah - LEFT JOIN presence_detection_summary pds - ON pds.device_id = adah.device_id - AND pds.event_date = adah.event_date - AND pds.event_hour = adah.event_hour -), - -daily_aggregates AS ( - SELECT - device_id, - event_date, - SUM(count_motion_detected) AS count_motion_detected, - SUM(count_presence_detected) AS count_presence_detected, - SUM(count_total_presence_detected) AS count_total_presence_detected - FROM table_final - GROUP BY device_id, event_date -) - -INSERT INTO public."presence-sensor-daily-detection" ( - device_uuid, - event_date, - count_motion_detected, - count_presence_detected, - count_total_presence_detected -) -SELECT - device_id, - event_date, - count_motion_detected, - count_presence_detected, - count_total_presence_detected -FROM daily_aggregates -ON CONFLICT (device_uuid, event_date) DO UPDATE -SET - count_motion_detected = EXCLUDED.count_motion_detected, - count_presence_detected = EXCLUDED.count_presence_detected, - count_total_presence_detected = EXCLUDED.count_total_presence_detected; diff --git a/libs/common/src/sql/procedures/fact_space_occupancy/procedure_select_fact_daily_space_occupancy.sql b/libs/common/src/sql/procedures/fact_space_occupancy/procedure_select_fact_daily_space_occupancy.sql deleted file mode 100644 index 4b084c4..0000000 --- a/libs/common/src/sql/procedures/fact_space_occupancy/procedure_select_fact_daily_space_occupancy.sql +++ /dev/null @@ -1,19 +0,0 @@ --- will return the presence metrics for the days of the selected month -WITH params AS ( - SELECT - TO_DATE(NULLIF($2, ''), 'YYYY-MM') AS month, - string_to_array(NULLIF($4, ''), ',') AS device_ids -) - - SELECT - A.device_uuid, - A.event_date, - A.count_motion_detected, - A.count_presence_detected, - A.count_total_presence_detected - FROM public."presence-sensor-daily-detection" AS A - JOIN params P ON TRUE - WHERE A.device_uuid::text = ANY(P.device_ids) - AND (P.month IS NULL - OR date_trunc('month', A.event_date) = P.month - ) \ No newline at end of file diff --git a/libs/common/src/sql/queries/fact_daily_device_energy_consumed/fact_daily_device_energy_consumed.sql b/libs/common/src/sql/queries/fact_daily_energy_consumed/fact_daily_energy_consumed.sql similarity index 100% rename from libs/common/src/sql/queries/fact_daily_device_energy_consumed/fact_daily_device_energy_consumed.sql rename to libs/common/src/sql/queries/fact_daily_energy_consumed/fact_daily_energy_consumed.sql diff --git a/libs/common/src/sql/queries/fact_daily_space_energy_consumed/fact_daily_space_energy_consumed.sql b/libs/common/src/sql/queries/fact_daily_space_energy_consumed/fact_daily_space_energy_consumed.sql deleted file mode 100644 index d5a9910..0000000 --- a/libs/common/src/sql/queries/fact_daily_space_energy_consumed/fact_daily_space_energy_consumed.sql +++ /dev/null @@ -1,11 +0,0 @@ -SELECT - B.space_device_uuid AS space_id, - A."date", - SUM(A.energy_consumed_kW::numeric) AS total_energy_consumed_KW, - SUM(A.energy_consumed_A::numeric) AS total_energy_consumed_A, - SUM(A.energy_consumed_B::numeric) AS total_energy_consumed_B, - SUM(A.energy_consumed_C::numeric) AS total_energy_consumed_C -FROM public."power-clamp-energy-consumed-daily" AS A -- I want to change the source table in the future. -JOIN public.device AS B - ON A.device_uuid::TEXT = B."uuid"::TEXT -GROUP BY 1, 2; \ No newline at end of file diff --git a/libs/common/src/sql/queries/fact_hourly_device_active_energy/fact_hourly_device_active_energy.sql b/libs/common/src/sql/queries/fact_hourly_active_energy/fact_hourly_active_energy.sql similarity index 100% rename from libs/common/src/sql/queries/fact_hourly_device_active_energy/fact_hourly_device_active_energy.sql rename to libs/common/src/sql/queries/fact_hourly_active_energy/fact_hourly_active_energy.sql diff --git a/libs/common/src/sql/queries/fact_hourly_device_presence_detected/fact_daily_device_presence_detected_insert_statement.sql b/libs/common/src/sql/queries/fact_hourly_device_presence_detected/fact_daily_device_presence_detected_insert_statement.sql deleted file mode 100644 index ace575c..0000000 --- a/libs/common/src/sql/queries/fact_hourly_device_presence_detected/fact_daily_device_presence_detected_insert_statement.sql +++ /dev/null @@ -1,106 +0,0 @@ --- This model shows the number of times a presence was detected per hour, per day -WITH device_logs AS ( - SELECT - device.uuid AS device_id, - device.created_at, - device.device_tuya_uuid, - device.space_device_uuid AS space_id, - "device-status-log".event_id, - "device-status-log".event_time::timestamp, - "device-status-log".code, - "device-status-log".value, - "device-status-log".log, - LAG("device-status-log".event_time::timestamp) - OVER (PARTITION BY device.uuid - ORDER BY "device-status-log".event_time) AS prev_timestamp, - LAG("device-status-log".value) - OVER (PARTITION BY device.uuid - ORDER BY "device-status-log".event_time) AS prev_value - FROM device - LEFT JOIN "device-status-log" - ON device.uuid = "device-status-log".device_id - LEFT JOIN product - ON product.uuid = device.product_device_uuid - WHERE product.cat_name = 'hps' - AND "device-status-log".code = 'presence_state' -), - -presence_detection AS ( - SELECT *, - CASE - WHEN value = 'motion' AND prev_value = 'none' THEN 1 ELSE 0 - END AS motion_detected, - CASE - WHEN value = 'presence' AND prev_value = 'none' THEN 1 ELSE 0 - END AS presence_detected - FROM device_logs -), - -presence_detection_summary AS ( - SELECT - pd.device_id, - d.subspace_id, - pd.space_id, - pd.event_time::date AS event_date, - EXTRACT(HOUR FROM pd.event_time)::int AS event_hour, - SUM(motion_detected) AS count_motion_detected, - SUM(presence_detected) AS count_presence_detected, - SUM(motion_detected + presence_detected) AS count_total_presence_detected - FROM presence_detection pd - LEFT JOIN device d ON d.uuid = pd.device_id - GROUP BY 1, 2, 3, 4, 5 -), - -all_dates_and_hours AS ( - SELECT device_id, subspace_id, space_id, event_date, event_hour - FROM ( - SELECT DISTINCT device_id, subspace_id, space_id, event_date - FROM presence_detection_summary - ) d - CROSS JOIN generate_series(0, 23) AS event_hour -), - -table_final AS ( - SELECT - adah.device_id, - adah.event_date, - COALESCE(pds.count_motion_detected, 0) AS count_motion_detected, - COALESCE(pds.count_presence_detected, 0) AS count_presence_detected, - COALESCE(pds.count_total_presence_detected, 0) AS count_total_presence_detected - FROM all_dates_and_hours adah - LEFT JOIN presence_detection_summary pds - ON pds.device_id = adah.device_id - AND pds.event_date = adah.event_date - AND pds.event_hour = adah.event_hour -), - -daily_aggregate AS ( - SELECT - device_id, - event_date, - SUM(count_motion_detected) AS count_motion_detected, - SUM(count_presence_detected) AS count_presence_detected, - SUM(count_total_presence_detected) AS count_total_presence_detected - FROM table_final - GROUP BY device_id, event_date -) - -INSERT INTO public."presence-sensor-daily-detection" ( - device_uuid, - event_date, - count_motion_detected, - count_presence_detected, - count_total_presence_detected -) -SELECT - device_id, - event_date, - count_motion_detected, - count_presence_detected, - count_total_presence_detected -FROM daily_aggregate -ON CONFLICT (device_uuid, event_date) DO UPDATE -SET - count_motion_detected = EXCLUDED.count_motion_detected, - count_presence_detected = EXCLUDED.count_presence_detected, - count_total_presence_detected = EXCLUDED.count_total_presence_detected; diff --git a/src/community/services/community.service.ts b/src/community/services/community.service.ts index cb166e7..4debb98 100644 --- a/src/community/services/community.service.ts +++ b/src/community/services/community.service.ts @@ -1,9 +1,4 @@ -import { - Injectable, - HttpException, - HttpStatus, - NotFoundException, -} from '@nestjs/common'; +import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; import { AddCommunityDto, GetCommunityParams, ProjectParam } from '../dtos'; import { UpdateCommunityNameDto } from '../dtos/update.community.dto'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; @@ -21,8 +16,6 @@ import { ORPHAN_COMMUNITY_NAME } from '@app/common/constants/orphan-constant'; import { ILike, In, Not } from 'typeorm'; import { SpaceService } from 'src/space/services'; import { SpaceRepository } from '@app/common/modules/space'; -import { DeviceEntity } from '@app/common/modules/device/entities'; -import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; @Injectable() export class CommunityService { @@ -310,58 +303,4 @@ export class CommunityService { ); } } - async getAllDevicesByCommunity( - communityUuid: string, - ): Promise { - // Fetch the community and its top-level spaces - const community = await this.communityRepository.findOne({ - where: { uuid: communityUuid }, - relations: [ - 'spaces', - 'spaces.children', - 'spaces.devices', - 'spaces.devices.productDevice', - ], - }); - - if (!community) { - throw new NotFoundException('Community not found'); - } - - const allDevices: DeviceEntity[] = []; - - // Recursive fetch function for spaces - const fetchSpaceDevices = async (space: SpaceEntity) => { - if (space.devices && space.devices.length > 0) { - allDevices.push(...space.devices); - } - - if (space.children && space.children.length > 0) { - for (const childSpace of space.children) { - const fullChildSpace = await this.spaceRepository.findOne({ - where: { uuid: childSpace.uuid }, - relations: ['children', 'devices', 'devices.productDevice'], - }); - - if (fullChildSpace) { - await fetchSpaceDevices(fullChildSpace); - } - } - } - }; - - // Start recursive fetch for all top-level spaces - for (const space of community.spaces) { - const fullSpace = await this.spaceRepository.findOne({ - where: { uuid: space.uuid }, - relations: ['children', 'devices', 'devices.productDevice'], - }); - - if (fullSpace) { - await fetchSpaceDevices(fullSpace); - } - } - - return allDevices; - } } diff --git a/src/power-clamp/controllers/power-clamp.controller.ts b/src/power-clamp/controllers/power-clamp.controller.ts index eb89d11..ca7b664 100644 --- a/src/power-clamp/controllers/power-clamp.controller.ts +++ b/src/power-clamp/controllers/power-clamp.controller.ts @@ -4,21 +4,14 @@ import { ApiBearerAuth, ApiOperation, ApiParam, - ApiQuery, } 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 { PowerClampService } from '../services/power-clamp.service'; -import { - GetPowerClampBySpaceDto, - GetPowerClampDto, -} from '../dto/get-power-clamp.dto'; +import { GetPowerClampDto } from '../dto/get-power-clamp.dto'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; -import { - PowerClampParamsDto, - ResourceParamsDto, -} from '../dto/power-clamp-params.dto'; +import { PowerClampParamsDto } from '../dto/power-clamp-params.dto'; @ApiTags('Power Clamp Module') @Controller({ @@ -46,34 +39,4 @@ export class PowerClampController { ): Promise { return await this.powerClampService.getPowerClampData(params, query); } - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - @Get('historical') - @ApiOperation({ - summary: - ControllerRoute.PowerClamp.ACTIONS - .GET_ENERGY_BY_COMMUNITY_OR_SPACE_SUMMARY, - description: - ControllerRoute.PowerClamp.ACTIONS - .GET_ENERGY_BY_COMMUNITY_OR_SPACE_DESCRIPTION, - }) - @ApiQuery({ - name: 'spaceUuid', - description: 'UUID of the Space', - required: false, - }) - @ApiQuery({ - name: 'communityUuid', - description: 'UUID of the Community', - required: false, - }) - async getPowerClampDataBySpaceOrCommunity( - @Query() params: ResourceParamsDto, - @Query() query: GetPowerClampBySpaceDto, - ): Promise { - return await this.powerClampService.getPowerClampDataBySpaceOrCommunity( - params, - query, - ); - } } diff --git a/src/power-clamp/dto/get-power-clamp.dto.ts b/src/power-clamp/dto/get-power-clamp.dto.ts index d53b681..baa6839 100644 --- a/src/power-clamp/dto/get-power-clamp.dto.ts +++ b/src/power-clamp/dto/get-power-clamp.dto.ts @@ -1,5 +1,5 @@ import { ApiPropertyOptional } from '@nestjs/swagger'; -import { IsOptional, IsDateString, Matches, IsNotEmpty } from 'class-validator'; +import { IsOptional, IsDateString, Matches } from 'class-validator'; export class GetPowerClampDto { @ApiPropertyOptional({ @@ -33,13 +33,3 @@ export class GetPowerClampDto { }) year?: string; } -export class GetPowerClampBySpaceDto { - @ApiPropertyOptional({ - description: 'monthDate must be in YYYY-MM format', - example: '2025-04', - required: true, - }) - @IsDateString() - @IsNotEmpty() - public monthDate: string; -} diff --git a/src/power-clamp/dto/power-clamp-params.dto.ts b/src/power-clamp/dto/power-clamp-params.dto.ts index 7250540..42f8e11 100644 --- a/src/power-clamp/dto/power-clamp-params.dto.ts +++ b/src/power-clamp/dto/power-clamp-params.dto.ts @@ -1,23 +1,6 @@ -import { IsNotEmpty, IsOptional, IsUUID, ValidateIf } from 'class-validator'; +import { IsUUID } from 'class-validator'; export class PowerClampParamsDto { @IsUUID('4', { message: 'Invalid UUID format' }) powerClampUuid: string; } -export class SpaceParamsDto { - @IsUUID('4', { message: 'Invalid UUID format' }) - spaceUuid: string; -} -export class ResourceParamsDto { - @IsUUID('4', { message: 'Invalid UUID format' }) - @IsOptional() - spaceUuid?: string; - - @IsUUID('4', { message: 'Invalid UUID format' }) - @IsOptional() - communityUuid?: string; - - @ValidateIf((o) => !o.spaceUuid && !o.communityUuid) - @IsNotEmpty({ message: 'Either spaceUuid or communityUuid must be provided' }) - requireEither?: never; // This ensures at least one of them is provided -} diff --git a/src/power-clamp/power-clamp.module.ts b/src/power-clamp/power-clamp.module.ts index 3f0595c..9cd6496 100644 --- a/src/power-clamp/power-clamp.module.ts +++ b/src/power-clamp/power-clamp.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; -import { PowerClampService as PowerClamp } from './services/power-clamp.service'; +import { PowerClampService } from './services/power-clamp.service'; import { PowerClampController } from './controllers'; import { PowerClampDailyRepository, @@ -8,108 +8,16 @@ import { PowerClampMonthlyRepository, } from '@app/common/modules/power-clamp/repositories'; import { DeviceRepository } from '@app/common/modules/device/repositories'; -import { - SpaceDeviceService, - SpaceLinkService, - SpaceService, - SubspaceDeviceService, - SubSpaceService, - ValidationService, -} from 'src/space/services'; -import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; -import { DeviceService } from 'src/device/services'; -import { - InviteSpaceRepository, - SpaceLinkRepository, - SpaceProductAllocationRepository, - SpaceRepository, - TagRepository, -} from '@app/common/modules/space'; -import { CommunityService } from 'src/community/services'; -import { ProjectRepository } from '@app/common/modules/project/repositiories'; -import { CommunityRepository } from '@app/common/modules/community/repositories'; -import { - SpaceModelProductAllocationRepoitory, - SpaceModelRepository, - SubspaceModelProductAllocationRepoitory, - SubspaceModelRepository, -} from '@app/common/modules/space-model'; -import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories'; -import { ProductRepository } from '@app/common/modules/product/repositories'; -import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; -import { SceneService } from 'src/scene/services'; -import { PowerClampService } from '@app/common/helper/services/power.clamp.service'; -import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log/repositories'; -import { - SceneIconRepository, - SceneRepository, -} from '@app/common/modules/scene/repositories'; -import { AutomationRepository } from '@app/common/modules/automation/repositories'; -import { TagService } from 'src/tags/services'; -import { - SpaceModelService, - SubSpaceModelService, -} from 'src/space-model/services'; -import { SpaceProductAllocationService } from 'src/space/services/space-product-allocation.service'; -import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service'; -import { - SubspaceProductAllocationRepository, - SubspaceRepository, -} from '@app/common/modules/space/repositories/subspace.repository'; -import { SubspaceProductAllocationService } from 'src/space/services/subspace/subspace-product-allocation.service'; -import { NewTagRepository } from '@app/common/modules/tag/repositories/tag-repository'; -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'; @Module({ imports: [ConfigModule], controllers: [PowerClampController], providers: [ - PowerClamp, + PowerClampService, PowerClampDailyRepository, PowerClampHourlyRepository, PowerClampMonthlyRepository, DeviceRepository, - SpaceDeviceService, - TuyaService, - ValidationService, - DeviceService, - SpaceRepository, - CommunityService, - ProjectRepository, - CommunityRepository, - SpaceModelRepository, - SceneDeviceRepository, - ProductRepository, - DeviceStatusFirebaseService, - SceneService, - SpaceService, - PowerClampService, - DeviceStatusLogRepository, - SceneIconRepository, - SceneRepository, - AutomationRepository, - InviteSpaceRepository, - SpaceLinkService, - SubSpaceService, - TagService, - SpaceModelService, - SpaceProductAllocationService, - SqlLoaderService, - SpaceLinkRepository, - SubspaceRepository, - SubspaceDeviceService, - SubspaceProductAllocationService, - NewTagRepository, - SubSpaceModelService, - SpaceModelProductAllocationService, - SpaceProductAllocationRepository, - SubspaceProductAllocationRepository, - TagRepository, - SubspaceModelRepository, - SubspaceModelProductAllocationService, - SpaceModelProductAllocationRepoitory, - SubspaceModelProductAllocationRepoitory, ], - exports: [PowerClamp], + exports: [PowerClampService], }) export class PowerClampModule {} diff --git a/src/power-clamp/services/power-clamp.service.ts b/src/power-clamp/services/power-clamp.service.ts index 24bca95..b95c9f0 100644 --- a/src/power-clamp/services/power-clamp.service.ts +++ b/src/power-clamp/services/power-clamp.service.ts @@ -1,32 +1,13 @@ -import { - BadRequestException, - HttpException, - HttpStatus, - Injectable, -} from '@nestjs/common'; -import { - GetPowerClampBySpaceDto, - GetPowerClampDto, -} from '../dto/get-power-clamp.dto'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { GetPowerClampDto } from '../dto/get-power-clamp.dto'; import { PowerClampDailyRepository, PowerClampHourlyRepository, PowerClampMonthlyRepository, } from '@app/common/modules/power-clamp/repositories'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; -import { - PowerClampParamsDto, - ResourceParamsDto, -} from '../dto/power-clamp-params.dto'; +import { PowerClampParamsDto } from '../dto/power-clamp-params.dto'; import { DeviceRepository } from '@app/common/modules/device/repositories'; -import { SpaceDeviceService } from 'src/space/services'; -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 { filterByMonth, toMMYYYY } from '@app/common/helper/date-format'; -import { ProductType } from '@app/common/constants/product-type.enum'; -import { CommunityService } from 'src/community/services'; -import { BaseResponseDto } from '@app/common/dto/base.response.dto'; @Injectable() export class PowerClampService { @@ -35,85 +16,8 @@ export class PowerClampService { private readonly powerClampHourlyRepository: PowerClampHourlyRepository, private readonly powerClampMonthlyRepository: PowerClampMonthlyRepository, private readonly deviceRepository: DeviceRepository, - private readonly spaceDeviceService: SpaceDeviceService, - private readonly sqlLoader: SqlLoaderService, - private readonly dataSource: DataSource, - private readonly communityService: CommunityService, ) {} - async getPowerClampDataBySpaceOrCommunity( - params: ResourceParamsDto, - query: GetPowerClampBySpaceDto, - ): Promise { - const { monthDate } = query; - const { spaceUuid, communityUuid } = params; - - try { - // Validate we have at least one identifier - if (!spaceUuid && !communityUuid) { - throw new BadRequestException( - 'Either spaceUuid or communityUuid must be provided', - ); - } - - // Get devices based on space or community - const devices = spaceUuid - ? await this.spaceDeviceService.getAllDevicesBySpace(spaceUuid) - : await this.communityService.getAllDevicesByCommunity(communityUuid); - - if (!devices?.length) { - return this.buildResponse( - 'No power clamp devices found for the specified criteria', - [], - ); - } - - // Filter and prepare device UUIDs - const deviceUuids = devices - .filter((device) => device.productDevice?.prodType === ProductType.PC) - .map((device) => device.uuid); - - if (deviceUuids.length === 0) { - return this.buildResponse( - 'No power clamp devices (PC type) found for the specified criteria', - [], - ); - } - - // Execute procedure - const formattedMonthDate = toMMYYYY(monthDate); - const data = await this.executeProcedure( - 'fact_daily_space_energy_consumed_procedure', - [formattedMonthDate, deviceUuids.join(',')], - ); - - // Format and filter data - const formattedData = data.map((item) => ({ - ...item, - date: new Date(item.date).toLocaleDateString('en-CA'), // YYYY-MM-DD - })); - - const resultData = monthDate - ? filterByMonth(formattedData, monthDate) - : formattedData; - - return this.buildResponse( - `Power clamp data fetched successfully for ${spaceUuid ? 'space' : 'community'}`, - resultData, - ); - } catch (error) { - console.error('Error fetching power clamp data', { - error, - spaceUuid, - communityUuid, - }); - throw new HttpException( - error.response?.message || 'Failed to fetch power clamp data', - error.status || HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - async getPowerClampData( params: PowerClampParamsDto, query: GetPowerClampDto, @@ -195,17 +99,4 @@ export class PowerClampService { statusCode: HttpStatus.OK, }); } - private async executeProcedure( - procedureFileName: string, - params: (string | number | null)[], - ): Promise { - const query = this.loadQuery( - 'fact_space_energy_consumed', - procedureFileName, - ); - return await this.dataSource.query(query, params); - } - private loadQuery(folderName: string, fileName: string): string { - return this.sqlLoader.loadQuery(folderName, fileName, SQL_PROCEDURES_PATH); - } } diff --git a/src/space/services/space-device.service.ts b/src/space/services/space-device.service.ts index b257b1e..891df3e 100644 --- a/src/space/services/space-device.service.ts +++ b/src/space/services/space-device.service.ts @@ -1,11 +1,6 @@ import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; -import { - HttpException, - HttpStatus, - Injectable, - NotFoundException, -} from '@nestjs/common'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { GetSpaceParam } from '../dtos'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; @@ -14,9 +9,6 @@ import { ValidationService } from './space-validation.service'; import { ProductType } from '@app/common/constants/product-type.enum'; import { BatteryStatus } from '@app/common/constants/battery-status.enum'; import { DeviceService } from 'src/device/services'; -import { SpaceRepository } from '@app/common/modules/space'; -import { DeviceEntity } from '@app/common/modules/device/entities'; -import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; @Injectable() export class SpaceDeviceService { @@ -24,7 +16,6 @@ export class SpaceDeviceService { private readonly tuyaService: TuyaService, private readonly validationService: ValidationService, private readonly deviceService: DeviceService, - private readonly spaceRepository: SpaceRepository, ) {} async listDevicesInSpace(params: GetSpaceParam): Promise { @@ -130,37 +121,4 @@ export class SpaceDeviceService { ); return batteryStatus ? batteryStatus.value : null; } - async getAllDevicesBySpace(spaceUuid: string): Promise { - const space = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid }, - relations: ['children', 'devices', 'devices.productDevice'], - }); - - if (!space) { - throw new NotFoundException('Space not found'); - } - - const allDevices: DeviceEntity[] = [...space.devices]; - - // Recursive fetch function - const fetchChildren = async (parentSpace: SpaceEntity) => { - const children = await this.spaceRepository.find({ - where: { parent: { uuid: parentSpace.uuid } }, - relations: ['children', 'devices', 'devices.productDevice'], - }); - - for (const child of children) { - allDevices.push(...child.devices); - - if (child.children.length > 0) { - await fetchChildren(child); - } - } - }; - - // Start recursive fetch - await fetchChildren(space); - - return allDevices; - } }