From b4623c74a8ce3e40522bd4b7cbfe8e22c2fe39dd Mon Sep 17 00:00:00 2001 From: Matthew Petro Date: Thu, 15 Feb 2024 13:08:55 -0700 Subject: [PATCH] Refactoring and code improvements --- .../sunrise-sunset-response.interface.ts | 16 ++++ .../interfaces/sunrise-sunset.interface.ts | 8 ++ src/sunrise-sunset/queries.ts | 20 +++++ src/sunrise-sunset/sunrise-sunset.service.ts | 85 ++++++++++++++----- 4 files changed, 109 insertions(+), 20 deletions(-) create mode 100644 src/sunrise-sunset/interfaces/sunrise-sunset-response.interface.ts create mode 100644 src/sunrise-sunset/interfaces/sunrise-sunset.interface.ts create mode 100644 src/sunrise-sunset/queries.ts diff --git a/src/sunrise-sunset/interfaces/sunrise-sunset-response.interface.ts b/src/sunrise-sunset/interfaces/sunrise-sunset-response.interface.ts new file mode 100644 index 0000000..18bdd03 --- /dev/null +++ b/src/sunrise-sunset/interfaces/sunrise-sunset-response.interface.ts @@ -0,0 +1,16 @@ +export interface SunriseSunsetResponse { + results: { + sunrise: string + sunset: string + solar_noon?: string + day_length?: number + civil_twilight_begin?: string + civil_twilight_end?: string + nautical_twilight_begin?: string + nautical_twilight_end?: string + astronomical_twilight_begin?: string + astronomical_twilight_end?: string + } + status?: string + tzid?: string +} diff --git a/src/sunrise-sunset/interfaces/sunrise-sunset.interface.ts b/src/sunrise-sunset/interfaces/sunrise-sunset.interface.ts new file mode 100644 index 0000000..25fc189 --- /dev/null +++ b/src/sunrise-sunset/interfaces/sunrise-sunset.interface.ts @@ -0,0 +1,8 @@ +export interface SunriseSunset { + sunrise: string + sunset: string +} + +export interface SunriseSunsets { + [date: string]: SunriseSunset +} diff --git a/src/sunrise-sunset/queries.ts b/src/sunrise-sunset/queries.ts new file mode 100644 index 0000000..2e89d1e --- /dev/null +++ b/src/sunrise-sunset/queries.ts @@ -0,0 +1,20 @@ +import { MangoQuery } from 'nano' + +export const sunriseSunsetQueryBuilder = (startDate: string, endDate: string): MangoQuery => ({ + selector: { + $and: [ + { + _id: { + $gte: startDate, + }, + }, + { + _id: { + $lte: endDate, + }, + }, + ], + }, + sort: [{ _id: 'asc' }], + limit: 10000, +}) diff --git a/src/sunrise-sunset/sunrise-sunset.service.ts b/src/sunrise-sunset/sunrise-sunset.service.ts index 3a13cc0..06a9508 100644 --- a/src/sunrise-sunset/sunrise-sunset.service.ts +++ b/src/sunrise-sunset/sunrise-sunset.service.ts @@ -3,27 +3,33 @@ import EnvironmentVariables from '@/environment-variables' import { Injectable, OnModuleInit } from '@nestjs/common' import { ConfigService } from '@nestjs/config' import { DocumentScope } from 'nano' -import { SunriseSunsetDocument } from './interfaces/sunrise-sunset-document.interface' import axios, { AxiosInstance } from 'axios' +import { SunriseSunsetDocument } from './interfaces/sunrise-sunset-document.interface' +import { SunriseSunsetResponse } from './interfaces/sunrise-sunset-response.interface' +import { sunriseSunsetQueryBuilder } from './queries' +import { addDays, format, parseISO } from 'date-fns' +import { SunriseSunsets } from './interfaces/sunrise-sunset.interface' -type SunriseSunsetResponse = - | { - results: { - sunrise: string - sunset: string - solar_noon: string - day_length: number - civil_twilight_begin: string - civil_twilight_end: string - nautical_twilight_begin: string - nautical_twilight_end: string - astronomical_twilight_begin: string - astronomical_twilight_end: string - } - status: string - tzid: string +const sunriseSunsetToDbDocument = ({ results }: SunriseSunsetResponse) => { + const { sunrise, sunset } = results + return { + // The ID is the date of the sunrise/sunset. + // Slicing off the first 10 characters gives us that without having to + // parse and reformat the date. + _id: sunrise.slice(0, 10), + sunrise, + sunset, + } as SunriseSunsetDocument +} + +const dbDocumentsToSunriseSunsets = (documents: SunriseSunsetDocument[]) => + documents.reduce((accumulator, document) => { + accumulator[document._id] = { + sunrise: document.sunrise, + sunset: document.sunset, } - | undefined + return accumulator + }, {} as SunriseSunsets) @Injectable() export class SunriseSunsetService implements OnModuleInit { @@ -42,6 +48,7 @@ export class SunriseSunsetService implements OnModuleInit { lat: this.configService.get('LATITUDE', { infer: true }), lng: this.configService.get('LONGITUDE', { infer: true }), formatted: 0, + tzid: 'America/Phoenix', }, }) this.db = this.databaseService.getDatabaseConnection( @@ -49,8 +56,46 @@ export class SunriseSunsetService implements OnModuleInit { ) } - private async getSunriseSunsetFromApi(date: string): Promise { - const response = await this.axiosInstance.get('/', { params: { date } }) + public async getSunriseSunsets(startDate: string, endDate: string) { + const dbDocs = await this.getSunriseSunsetsFromDb(startDate, endDate) + const sunriseSunsets = dbDocumentsToSunriseSunsets(dbDocs) + + const start = parseISO(startDate) + const end = parseISO(endDate) + + // Iterate through each day between the start and end dates + for (let date = start; date <= end; date = addDays(date, 1)) { + const dateString = format(date, 'yyyy-MM-dd') + + // If we don't have a sunrise/sunset for the date, fetch it from the API, + // save it to the database and add to the sunriseSunsets object. + if (!sunriseSunsets[dateString]) { + const sunriseSunsetResponse = await this.getSunriseSunsetFromApi(dateString) + sunriseSunsets[dateString] = { + sunrise: sunriseSunsetResponse.results.sunrise, + sunset: sunriseSunsetResponse.results.sunset, + } + // We don't need to wait for the insert to complete, so we don't await it. + // If it fails, log it and move on. + this.insertSunriseSunset(sunriseSunsetToDbDocument(sunriseSunsetResponse)).catch((e) => { + console.error(e, `Failed to insert sunrise/sunset for ${dateString}`) + }) + } + } + return sunriseSunsets + } + + private async getSunriseSunsetFromApi(date: string) { + const response = await this.axiosInstance.get('', { params: { date } }) return response.data } + + private async getSunriseSunsetsFromDb(startDate: string, endDate: string) { + const result = await this.db.find(sunriseSunsetQueryBuilder(startDate, endDate)) + return result.docs + } + + private async insertSunriseSunset(sunriseSunsetDocument: SunriseSunsetDocument) { + await this.db.insert(sunriseSunsetDocument) + } }