From feb6ba49ed8935b12bce4b2b6ea64ebed3a638fe Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Tue, 19 Sep 2023 20:06:34 +0300 Subject: [PATCH] graph query --- src/app.controller.ts | 6 ++ src/app.service.ts | 108 ++++++++++++++++++++++++++++++---- src/dtos/graph-query.dto.ts | 5 ++ src/dtos/history-query.dto.ts | 1 + src/main.ts | 2 +- src/module/ws1080.ts | 2 +- 6 files changed, 109 insertions(+), 15 deletions(-) create mode 100644 src/dtos/graph-query.dto.ts diff --git a/src/app.controller.ts b/src/app.controller.ts index dd4e321..17acbf1 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -2,6 +2,7 @@ import { Controller, Get, Query, UseInterceptors } from '@nestjs/common'; import { AppService } from './app.service'; import { CacheInterceptor, CacheTTL } from '@nestjs/cache-manager'; import { HistoryQueryDto } from './dtos/history-query.dto'; +import { GraphQueryDto } from './dtos/graph-query.dto'; @Controller() export class AppController { @@ -18,4 +19,9 @@ export class AppController { getWeatherHistory(@Query() query: HistoryQueryDto) { return this.appService.getWeatherHistory(query); } + + @Get('graph') + makeGraph(@Query() query: GraphQueryDto) { + return this.appService.graphWeatherHistory(query); + } } diff --git a/src/app.service.ts b/src/app.service.ts index 73b2253..f907493 100644 --- a/src/app.service.ts +++ b/src/app.service.ts @@ -5,11 +5,12 @@ import { OnApplicationShutdown, } from '@nestjs/common'; import WS1080 from './module/ws1080'; -import { MoreThan, Repository } from 'typeorm'; +import { Between, LessThan, MoreThan, Repository } from 'typeorm'; import { WeatherEntity } from './entities/weather.entity'; import { InjectRepository } from '@nestjs/typeorm'; import { Cron } from '@nestjs/schedule'; import { HistoryQueryDto } from './dtos/history-query.dto'; +import { GraphQueryDto } from './dtos/graph-query.dto'; @Injectable() export class AppService implements OnApplicationShutdown { @@ -21,10 +22,14 @@ export class AppService implements OnApplicationShutdown { private readonly weatherRepository: Repository, ) {} + /** + * Get current weather information. + * @returns Weather data + */ async getWeather() { try { // Retrieve from USB - if (!this.station) this.station = WS1080.fromDevice(); + await this.createStation(); const data = await this.station.read(); // Save weather data to database @@ -50,26 +55,54 @@ export class AppService implements OnApplicationShutdown { this.logger.error('Failed to retrieve weather data:', error.stack); // Retrieve previous entry on error - const [previous] = await this.weatherRepository.find({ - order: { date: -1 }, - take: 1, - }); - + const previous = await this.getPreviousEntry(); if (!previous) throw new InternalServerErrorException(); - previous.rain24h = await this.rainFall24h(previous.date); - previous.fresh = false; return previous; } } - async getWeatherHistory(query: HistoryQueryDto) { + /** + * Get previous weather entry. + * @returns Previous weather data entry + */ + async getPreviousEntry() { + const [previous] = await this.weatherRepository.find({ + order: { date: -1 }, + take: 1, + }); + + if (!previous) return null; + + previous.rain24h = await this.rainFall24h(previous.date); + previous.fresh = false; + return previous; + } + + /** + * Get weather history. + * @param query Weather history search + * @returns Paginated history list + */ + async getWeatherHistory( + query: HistoryQueryDto, + select?: (keyof WeatherEntity)[], + ) { const pageSize = Number(query.pageSize) || 100; const page = Number(query.page) || 1; const [list, rowCount] = await this.weatherRepository.findAndCount({ - where: query.since - ? { date: MoreThan(new Date(query.since)) } - : undefined, + select, + where: + query.since || query.until + ? { + date: + query.since && query.until + ? Between(new Date(query.since), new Date(query.until)) + : query.since + ? MoreThan(new Date(query.since)) + : LessThan(new Date(query.until)), + } + : undefined, order: { date: -1 }, take: pageSize, skip: (page - 1) * pageSize, @@ -87,6 +120,42 @@ export class AppService implements OnApplicationShutdown { }; } + /** + * Get graph datasets for weather. + * @param query Weather history search + * @returns Paginated graph dataset + */ + async graphWeatherHistory(query: GraphQueryDto) { + const { list, pagination } = await this.getWeatherHistory(query, [ + ...(query.columns as (keyof WeatherEntity)[]), + 'date', + ]); + + const datasets = query.columns.reduce( + (mass, key: keyof WeatherEntity) => ({ + ...mass, + [key]: { label: String(key), data: [] }, + }), + {}, + ); + + list.forEach((entry) => { + Object.keys(entry) + .filter((key) => !['id', 'date'].includes(key)) + .forEach((key) => { + datasets[key].data.push({ + x: entry.date.getTime(), + y: entry[key], + }); + }); + }); + + return { + list: Object.values(datasets), + pagination, + }; + } + /** * Get rainfall in the last 24h since `since` start point. * @param since Time start point @@ -103,6 +172,19 @@ export class AppService implements OnApplicationShutdown { return Number(rainfall) || 0; } + /** + * Create station instance. + */ + async createStation() { + if (this.station) return; + this.station = WS1080.fromDevice(); + + const previous = await this.getPreviousEntry(); + if (previous) { + this.station.previousRain = previous.totalRain; + } + } + @Cron('0 * * * *') scheduledPulls() { this.getWeather().catch(() => { diff --git a/src/dtos/graph-query.dto.ts b/src/dtos/graph-query.dto.ts new file mode 100644 index 0000000..9930933 --- /dev/null +++ b/src/dtos/graph-query.dto.ts @@ -0,0 +1,5 @@ +import { HistoryQueryDto } from './history-query.dto'; + +export interface GraphQueryDto extends HistoryQueryDto { + columns: string[]; +} diff --git a/src/dtos/history-query.dto.ts b/src/dtos/history-query.dto.ts index 99d6012..a22c9dd 100644 --- a/src/dtos/history-query.dto.ts +++ b/src/dtos/history-query.dto.ts @@ -1,5 +1,6 @@ export class HistoryQueryDto { since?: string; + until?: string; page?: string; pageSize?: string; } diff --git a/src/main.ts b/src/main.ts index 13cad38..8d9718c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,7 +2,7 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { - const app = await NestFactory.create(AppModule); + const app = await NestFactory.create(AppModule, { cors: true }); await app.listen(3000); } bootstrap(); diff --git a/src/module/ws1080.ts b/src/module/ws1080.ts index 4cdf615..4f3c61a 100644 --- a/src/module/ws1080.ts +++ b/src/module/ws1080.ts @@ -44,7 +44,7 @@ const WIND_DIRS = [ * Then remove your device from your machine and plug it back in again */ class WS1080 { - private previousRain = 0; + public previousRain = 0; constructor(private device: Device, private dInterface: Interface) {}