mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-07-15 10:25:23 +00:00
Add Weather module with controller, service, and DTO for fetching weather details
This commit is contained in:
@ -465,7 +465,16 @@ export class ControllerRoute {
|
||||
'This endpoint retrieves the terms and conditions for the application.';
|
||||
};
|
||||
};
|
||||
static WEATHER = class {
|
||||
public static readonly ROUTE = 'weather';
|
||||
|
||||
static ACTIONS = class {
|
||||
public static readonly FETCH_WEATHER_DETAILS_SUMMARY =
|
||||
'Fetch Weather Details';
|
||||
public static readonly FETCH_WEATHER_DETAILS_DESCRIPTION =
|
||||
'This endpoint retrieves the current weather details for a specified location like temperature, humidity, etc.';
|
||||
};
|
||||
};
|
||||
static PRIVACY_POLICY = class {
|
||||
public static readonly ROUTE = 'policy';
|
||||
|
||||
|
18
libs/common/src/util/calculate.aqi.ts
Normal file
18
libs/common/src/util/calculate.aqi.ts
Normal file
@ -0,0 +1,18 @@
|
||||
export function calculateAQI(pm2_5: number): number {
|
||||
const breakpoints = [
|
||||
{ pmLow: 0.0, pmHigh: 12.0, aqiLow: 0, aqiHigh: 50 },
|
||||
{ pmLow: 12.1, pmHigh: 35.4, aqiLow: 51, aqiHigh: 100 },
|
||||
{ pmLow: 35.5, pmHigh: 55.4, aqiLow: 101, aqiHigh: 150 },
|
||||
{ pmLow: 55.5, pmHigh: 150.4, aqiLow: 151, aqiHigh: 200 },
|
||||
{ pmLow: 150.5, pmHigh: 250.4, aqiLow: 201, aqiHigh: 300 },
|
||||
{ pmLow: 250.5, pmHigh: 500.4, aqiLow: 301, aqiHigh: 500 },
|
||||
];
|
||||
|
||||
const bp = breakpoints.find((b) => pm2_5 >= b.pmLow && pm2_5 <= b.pmHigh);
|
||||
if (!bp) return pm2_5 > 500.4 ? 500 : 0; // Handle out-of-range values
|
||||
|
||||
return Math.round(
|
||||
((bp.aqiHigh - bp.aqiLow) / (bp.pmHigh - bp.pmLow)) * (pm2_5 - bp.pmLow) +
|
||||
bp.aqiLow,
|
||||
);
|
||||
}
|
@ -38,6 +38,7 @@ import { HealthModule } from './health/health.module';
|
||||
|
||||
import { winstonLoggerOptions } from '../libs/common/src/logger/services/winston.logger';
|
||||
import { OccupancyModule } from './occupancy/occupancy.module';
|
||||
import { WeatherModule } from './weather/weather.module';
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
@ -79,6 +80,7 @@ import { OccupancyModule } from './occupancy/occupancy.module';
|
||||
PowerClampModule,
|
||||
HealthModule,
|
||||
OccupancyModule,
|
||||
WeatherModule,
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
import AuthConfig from './auth.config';
|
||||
import AppConfig from './app.config';
|
||||
import JwtConfig from './jwt.config';
|
||||
export default [AuthConfig, AppConfig, JwtConfig];
|
||||
import WeatherOpenConfig from './weather.open.config';
|
||||
export default [AuthConfig, AppConfig, JwtConfig, WeatherOpenConfig];
|
||||
|
8
src/config/weather.open.config.ts
Normal file
8
src/config/weather.open.config.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { registerAs } from '@nestjs/config';
|
||||
|
||||
export default registerAs(
|
||||
'openweather-config',
|
||||
(): Record<string, any> => ({
|
||||
OPEN_WEATHER_MAP_API_KEY: process.env.OPEN_WEATHER_MAP_API_KEY,
|
||||
}),
|
||||
);
|
1
src/weather/controllers/index.ts
Normal file
1
src/weather/controllers/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './weather.controller';
|
28
src/weather/controllers/weather.controller.ts
Normal file
28
src/weather/controllers/weather.controller.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { Controller, Get, Query } from '@nestjs/common';
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { EnableDisableStatusEnum } from '@app/common/constants/days.enum';
|
||||
import { ControllerRoute } from '@app/common/constants/controller-route'; // Assuming this is where the routes are defined
|
||||
import { WeatherService } from '../services';
|
||||
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||
import { GetWeatherDetailsDto } from '../dto/get.weather.dto';
|
||||
|
||||
@ApiTags('Weather Module')
|
||||
@Controller({
|
||||
version: EnableDisableStatusEnum.ENABLED,
|
||||
path: ControllerRoute.WEATHER.ROUTE, // use the static route constant
|
||||
})
|
||||
export class WeatherController {
|
||||
constructor(private readonly weatherService: WeatherService) {}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({
|
||||
summary: ControllerRoute.WEATHER.ACTIONS.FETCH_WEATHER_DETAILS_SUMMARY,
|
||||
description:
|
||||
ControllerRoute.WEATHER.ACTIONS.FETCH_WEATHER_DETAILS_DESCRIPTION,
|
||||
})
|
||||
async fetchWeatherDetails(
|
||||
@Query() query: GetWeatherDetailsDto,
|
||||
): Promise<BaseResponseDto> {
|
||||
return await this.weatherService.fetchWeatherDetails(query);
|
||||
}
|
||||
}
|
21
src/weather/dto/get.weather.dto.ts
Normal file
21
src/weather/dto/get.weather.dto.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsNumber } from 'class-validator';
|
||||
|
||||
export class GetWeatherDetailsDto {
|
||||
@ApiProperty({
|
||||
description: 'Latitude coordinate',
|
||||
example: 35.6895,
|
||||
})
|
||||
@IsNumber()
|
||||
@Type(() => Number)
|
||||
lat: number;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Longitude coordinate',
|
||||
example: 139.6917,
|
||||
})
|
||||
@IsNumber()
|
||||
@Type(() => Number)
|
||||
lon: number;
|
||||
}
|
1
src/weather/services/index.ts
Normal file
1
src/weather/services/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './weather.service';
|
39
src/weather/services/weather.service.ts
Normal file
39
src/weather/services/weather.service.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { GetWeatherDetailsDto } from '../dto/get.weather.dto';
|
||||
import { calculateAQI } from '@app/common/util/calculate.aqi';
|
||||
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
||||
|
||||
@Injectable()
|
||||
export class WeatherService {
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
private readonly httpService: HttpService,
|
||||
) {}
|
||||
|
||||
async fetchWeatherDetails(
|
||||
query: GetWeatherDetailsDto,
|
||||
): Promise<BaseResponseDto> {
|
||||
const { lat, lon } = query;
|
||||
const weatherApiKey = this.configService.get<string>(
|
||||
'OPEN_WEATHER_MAP_API_KEY',
|
||||
);
|
||||
const url = `http://api.weatherapi.com/v1/current.json?key=${weatherApiKey}&q=${lat},${lon}&aqi=yes`;
|
||||
|
||||
const response = await firstValueFrom(this.httpService.get(url));
|
||||
const pm2_5 = response.data.current.air_quality.pm2_5; // Raw PM2.5 (µg/m³)
|
||||
|
||||
return new SuccessResponseDto({
|
||||
message: `Weather details fetched successfully`,
|
||||
data: {
|
||||
aqi: calculateAQI(pm2_5), // Converted AQI (0-500)
|
||||
temperature: response.data.current.temp_c,
|
||||
humidity: response.data.current.humidity,
|
||||
},
|
||||
statusCode: HttpStatus.OK,
|
||||
});
|
||||
}
|
||||
}
|
12
src/weather/weather.module.ts
Normal file
12
src/weather/weather.module.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { HttpModule } from '@nestjs/axios'; // <-- Import this!
|
||||
import { WeatherController } from './controllers';
|
||||
import { WeatherService } from './services';
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule, HttpModule],
|
||||
controllers: [WeatherController],
|
||||
providers: [WeatherService],
|
||||
})
|
||||
export class WeatherModule {}
|
Reference in New Issue
Block a user