graph query
This commit is contained in:
parent
1ec89c72c1
commit
feb6ba49ed
@ -2,6 +2,7 @@ import { Controller, Get, Query, UseInterceptors } from '@nestjs/common';
|
|||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
import { CacheInterceptor, CacheTTL } from '@nestjs/cache-manager';
|
import { CacheInterceptor, CacheTTL } from '@nestjs/cache-manager';
|
||||||
import { HistoryQueryDto } from './dtos/history-query.dto';
|
import { HistoryQueryDto } from './dtos/history-query.dto';
|
||||||
|
import { GraphQueryDto } from './dtos/graph-query.dto';
|
||||||
|
|
||||||
@Controller()
|
@Controller()
|
||||||
export class AppController {
|
export class AppController {
|
||||||
@ -18,4 +19,9 @@ export class AppController {
|
|||||||
getWeatherHistory(@Query() query: HistoryQueryDto) {
|
getWeatherHistory(@Query() query: HistoryQueryDto) {
|
||||||
return this.appService.getWeatherHistory(query);
|
return this.appService.getWeatherHistory(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get('graph')
|
||||||
|
makeGraph(@Query() query: GraphQueryDto) {
|
||||||
|
return this.appService.graphWeatherHistory(query);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,12 @@ import {
|
|||||||
OnApplicationShutdown,
|
OnApplicationShutdown,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import WS1080 from './module/ws1080';
|
import WS1080 from './module/ws1080';
|
||||||
import { MoreThan, Repository } from 'typeorm';
|
import { Between, LessThan, MoreThan, Repository } from 'typeorm';
|
||||||
import { WeatherEntity } from './entities/weather.entity';
|
import { WeatherEntity } from './entities/weather.entity';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Cron } from '@nestjs/schedule';
|
import { Cron } from '@nestjs/schedule';
|
||||||
import { HistoryQueryDto } from './dtos/history-query.dto';
|
import { HistoryQueryDto } from './dtos/history-query.dto';
|
||||||
|
import { GraphQueryDto } from './dtos/graph-query.dto';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AppService implements OnApplicationShutdown {
|
export class AppService implements OnApplicationShutdown {
|
||||||
@ -21,10 +22,14 @@ export class AppService implements OnApplicationShutdown {
|
|||||||
private readonly weatherRepository: Repository<WeatherEntity>,
|
private readonly weatherRepository: Repository<WeatherEntity>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current weather information.
|
||||||
|
* @returns Weather data
|
||||||
|
*/
|
||||||
async getWeather() {
|
async getWeather() {
|
||||||
try {
|
try {
|
||||||
// Retrieve from USB
|
// Retrieve from USB
|
||||||
if (!this.station) this.station = WS1080.fromDevice();
|
await this.createStation();
|
||||||
const data = await this.station.read();
|
const data = await this.station.read();
|
||||||
|
|
||||||
// Save weather data to database
|
// Save weather data to database
|
||||||
@ -50,25 +55,53 @@ export class AppService implements OnApplicationShutdown {
|
|||||||
this.logger.error('Failed to retrieve weather data:', error.stack);
|
this.logger.error('Failed to retrieve weather data:', error.stack);
|
||||||
|
|
||||||
// Retrieve previous entry on error
|
// Retrieve previous entry on error
|
||||||
|
const previous = await this.getPreviousEntry();
|
||||||
|
if (!previous) throw new InternalServerErrorException();
|
||||||
|
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get previous weather entry.
|
||||||
|
* @returns Previous weather data entry
|
||||||
|
*/
|
||||||
|
async getPreviousEntry() {
|
||||||
const [previous] = await this.weatherRepository.find({
|
const [previous] = await this.weatherRepository.find({
|
||||||
order: { date: -1 },
|
order: { date: -1 },
|
||||||
take: 1,
|
take: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!previous) throw new InternalServerErrorException();
|
if (!previous) return null;
|
||||||
|
|
||||||
previous.rain24h = await this.rainFall24h(previous.date);
|
previous.rain24h = await this.rainFall24h(previous.date);
|
||||||
previous.fresh = false;
|
previous.fresh = false;
|
||||||
return previous;
|
return previous;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async getWeatherHistory(query: HistoryQueryDto) {
|
/**
|
||||||
|
* 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 pageSize = Number(query.pageSize) || 100;
|
||||||
const page = Number(query.page) || 1;
|
const page = Number(query.page) || 1;
|
||||||
const [list, rowCount] = await this.weatherRepository.findAndCount({
|
const [list, rowCount] = await this.weatherRepository.findAndCount({
|
||||||
where: query.since
|
select,
|
||||||
? { date: MoreThan(new Date(query.since)) }
|
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,
|
: undefined,
|
||||||
order: { date: -1 },
|
order: { date: -1 },
|
||||||
take: pageSize,
|
take: 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.
|
* Get rainfall in the last 24h since `since` start point.
|
||||||
* @param since Time start point
|
* @param since Time start point
|
||||||
@ -103,6 +172,19 @@ export class AppService implements OnApplicationShutdown {
|
|||||||
return Number(rainfall) || 0;
|
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 * * * *')
|
@Cron('0 * * * *')
|
||||||
scheduledPulls() {
|
scheduledPulls() {
|
||||||
this.getWeather().catch(() => {
|
this.getWeather().catch(() => {
|
||||||
|
5
src/dtos/graph-query.dto.ts
Normal file
5
src/dtos/graph-query.dto.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { HistoryQueryDto } from './history-query.dto';
|
||||||
|
|
||||||
|
export interface GraphQueryDto extends HistoryQueryDto {
|
||||||
|
columns: string[];
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
export class HistoryQueryDto {
|
export class HistoryQueryDto {
|
||||||
since?: string;
|
since?: string;
|
||||||
|
until?: string;
|
||||||
page?: string;
|
page?: string;
|
||||||
pageSize?: string;
|
pageSize?: string;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { NestFactory } from '@nestjs/core';
|
|||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create(AppModule, { cors: true });
|
||||||
await app.listen(3000);
|
await app.listen(3000);
|
||||||
}
|
}
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
@ -44,7 +44,7 @@ const WIND_DIRS = [
|
|||||||
* Then remove your device from your machine and plug it back in again
|
* Then remove your device from your machine and plug it back in again
|
||||||
*/
|
*/
|
||||||
class WS1080 {
|
class WS1080 {
|
||||||
private previousRain = 0;
|
public previousRain = 0;
|
||||||
|
|
||||||
constructor(private device: Device, private dInterface: Interface) {}
|
constructor(private device: Device, private dInterface: Interface) {}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user