Skip to content

Commit

Permalink
Merge pull request #3 from skybluesofa/Atomic-Caching
Browse files Browse the repository at this point in the history
Atomic caching
  • Loading branch information
skybluesofa authored Feb 4, 2024
2 parents 9b60bcd + bf58348 commit 5c11a53
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 84 deletions.
18 changes: 18 additions & 0 deletions app/Apis/ColorNames/ColorNames.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace App\Apis\ColorNames;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;

class ColorNames
{
protected $ttl = 60 * 60 * 24;

public function get(string $hex): string
{
return Cache::remember('color-name.'.$hex, $this->ttl, function () use ($hex) {
return Http::get('https://colornames.org/search/json/?hex='.$hex)['name'] ?? $hex;
});
}
}
26 changes: 26 additions & 0 deletions app/Apis/GeoNames/GeoNames.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace App\Apis\GeoNames;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;

class GeoNames
{
protected $ttl = 60 * 60 * 24;

public function get(string $geonameId): array
{
return Cache::remember('geoName.'.$geonameId, $this->ttl, function () use ($geonameId) {
$url = 'https://public.opendatasoft.com/api/explore/v2.1/catalog/datasets/geonames-all-cities-with-a-population-500/records?select=coordinates,timezone&where=geoname_id%3D'.$geonameId.'&limit=1' ?? null;
$response = Http::get($url)->body();

$json = json_decode($response, true);
if (! isset($json['results'][0])) {
return null;
}

return $json['results'][0];
});
}
}
86 changes: 51 additions & 35 deletions app/Apis/OpenMeteo/OpenMeteo.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use App\Facades\TemperatureBlanketConfig;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;

Expand All @@ -18,6 +17,8 @@ class OpenMeteo

protected string $timezone;

protected int $ttl = 60 * 60 * 24;

public function __construct(?Carbon $date)
{
$this->date = $date ?? new Carbon();
Expand All @@ -26,52 +27,67 @@ public function __construct(?Carbon $date)
$this->timezone = TemperatureBlanketConfig::get('timezone');
}

public function get(): Collection
public function get(?Carbon $date = null): ?array
{
return collect(Cache::remember('openmeteo.'.$this->date->format('Ymd'), 60 * 60, function () {
$this->date = $date ?? new Carbon();

if (! Cache::has($this->getCacheKey($this->date))) {
$now = Carbon::now();
$today = new Carbon($this->date);
$startDate = $today->clone()->subMonth()->format('Y-m-01');
$endDate = $today->clone()->addMonth();
$endDate = ($endDate > $now) ? $now->format('Y-m-d') : $endDate->format('Y-m-01');
$baseUrl = 'https://archive-api.open-meteo.com/v1/archive?latitude='.$this->latitude.'&longitude='.$this->longitude.'&start_date='.$startDate.'&end_date='.$endDate.'&daily=temperature_2m_max,temperature_2m_min,temperature_2m_mean,daylight_duration,rain_sum,snowfall_sum&temperature_unit=fahrenheit&precipitation_unit=inch&timezone='.$this->timezone;
$response = Http::get($baseUrl);

$data = [];
$weatherInfo = json_decode($response, true);

$weatherInfo = json_decode(Http::get($baseUrl), true);
foreach ($weatherInfo['daily']['time'] as $key => $date) {
$data[$date] = [
'date' => $date,
'temp' => [
'high' => null,
'low' => null,
'avg' => null,
],
'daylight' => number_format(($weatherInfo['daily']['daylight_duration'][$key] / 3600), 2),
'precipitation' => [
'rain' => null,
'snow' => null,
],
];

if ((new Carbon($date))->format('Ymd') <= (new Carbon())->format('Ymd')) {
$data[$date]['precipitation']['rain'] = number_format($weatherInfo['daily']['rain_sum'][$key], 2);
$data[$date]['precipitation']['snow'] = number_format($weatherInfo['daily']['snowfall_sum'][$key], 2);
$data[$date]['temp']['high'] = $weatherInfo['daily']['temperature_2m_max'][$key];
$data[$date]['temp']['low'] = $weatherInfo['daily']['temperature_2m_min'][$key];

$average = $weatherInfo['daily']['temperature_2m_mean'][$key];
if (empty($average) && ! is_null($weatherInfo['daily']['temperature_2m_max'][$key]) && ! is_null($weatherInfo['daily']['temperature_2m_min'][$key])) {
$average = ($weatherInfo['daily']['temperature_2m_max'][$key] + $weatherInfo['daily']['temperature_2m_min'][$key]) / 2;
}
$data[$date]['temp']['avg'] = $average;
$weatherDate = new Carbon($date);
if ($weatherDate->format('Ymd') > $now->format('Ymd')) {
continue;
}

$average = $weatherInfo['daily']['temperature_2m_mean'][$key];
if (empty($average) && ! is_null($weatherInfo['daily']['temperature_2m_max'][$key]) && ! is_null($weatherInfo['daily']['temperature_2m_min'][$key])) {
$average = ($weatherInfo['daily']['temperature_2m_max'][$key] + $weatherInfo['daily']['temperature_2m_min'][$key]) / 2;
}

$data = null;
if (! is_null($average)) {
$data = [
'date' => $date,
'temp' => [
'high' => $weatherInfo['daily']['temperature_2m_max'][$key],
'low' => $weatherInfo['daily']['temperature_2m_min'][$key],
'avg' => $average,
],
'daylight' => number_format(($weatherInfo['daily']['daylight_duration'][$key] / 3600), 2),
'precipitation' => [
'rain' => number_format($weatherInfo['daily']['rain_sum'][$key], 2),
'snow' => number_format($weatherInfo['daily']['snowfall_sum'][$key], 2),
],
];
}

Cache::set($this->getCacheKey($weatherDate), $data, $this->ttl);
Cache::set($this->getCacheWrittenKey($weatherDate), $now, $this->ttl);
}
}

Cache::set('openmeteo.'.$today->format('Ymd').'.written', Carbon::now('America/Chicago'));
return Cache::get($this->getCacheKey($this->date));
}

public function cachedDate(Carbon $date): ?Carbon
{
return Cache::get($this->getCacheWrittenKey($date));
}

return $data;
}));
protected function getCacheKey(Carbon $date): string
{
return 'openmeteo.'.$this->latitude.'.'.$this->longitude.'.'.$date->format('Ymd');
}

protected function getCacheWrittenKey(Carbon $date): string
{
return $this->getCacheKey($date).'.written';
}
}
29 changes: 7 additions & 22 deletions app/Apis/TemperatureBlanketDotCom/TemperatureBlanketDotCom.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

namespace App\Apis\TemperatureBlanketDotCom;

use App\Facades\ColorNames;
use App\Facades\GeoNames;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;

class TemperatureBlanketDotCom
{
Expand Down Expand Up @@ -120,10 +120,10 @@ protected function generateColors(string $key, array $config): array

if (isset($config[$key])) {
foreach ($config[$key] as $hex => $range) {
$colorName = Cache::remember('color.'.$hex, $this->cacheTime, function () use ($hex) {
return Http::get('https://colornames.org/search/json/?hex='.$hex)['name'] ?? $hex;
});
$colors[min($range)] = ['#'.$hex, $colorName];
$colors[min($range)] = [
'#'.$hex,
ColorNames::get($hex),
];
}
}

Expand All @@ -143,26 +143,11 @@ protected function parseMeta(array $config): array

return [
'geoname_id' => $geoNameId,
'location' => $this->getCoordinatesForGeonameId($geoNameId),
'location' => GeoNames::get($geoNameId),
'date' => rtrim($meta[1], '!'),
];
}

protected function getCoordinatesForGeonameId($geoNameId): ?array
{
return Cache::remember('geoName.'.$geoNameId, $this->cacheTime, function () use ($geoNameId) {
$url = 'https://public.opendatasoft.com/api/explore/v2.1/catalog/datasets/geonames-all-cities-with-a-population-500/records?select=coordinates,timezone&where=geoname_id%3D'.$geoNameId.'&limit=1' ?? null;
$response = Http::get($url)->body();

$json = json_decode($response, true);
if (! isset($json['results'][0])) {
return null;
}

return $json['results'][0];
});
}

protected function parseTemperaturesColors(array $config): array
{
if (! isset($config['temp'])) {
Expand Down
14 changes: 14 additions & 0 deletions app/Facades/ColorNames.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace App\Facades;

use App\Apis\ColorNames\ColorNames as ColorNamesApi;
use Illuminate\Support\Facades\Facade;

class ColorNames extends Facade
{
protected static function getFacadeAccessor()
{
return ColorNamesApi::class;
}
}
14 changes: 14 additions & 0 deletions app/Facades/GeoNames.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace App\Facades;

use App\Apis\GeoNames\GeoNames as GeoNamesApi;
use Illuminate\Support\Facades\Facade;

class GeoNames extends Facade
{
protected static function getFacadeAccessor()
{
return GeoNamesApi::class;
}
}
17 changes: 7 additions & 10 deletions app/Http/Controllers/TemperatureBlanketController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
use App\Facades\TemperatureBlanketConfig;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;

class TemperatureBlanketController extends Controller
{
Expand All @@ -31,23 +30,21 @@ public function show()

protected function getWeatherData(): array
{
$cachedWeatherData = OpenMeteo::get($this->date);

$yesterday = $this->date->clone()->subDay(1);
$tomorrow = $this->date->clone()->addDay(1);
$this->weatherData['current_row'] = collect([
$yesterday->format('Y-m-d') => $cachedWeatherData[$yesterday->format('Y-m-d')] ?? null,
$this->date->format('Y-m-d') => $cachedWeatherData[$this->date->format('Y-m-d')] ?? null,
$tomorrow->format('Y-m-d') => $cachedWeatherData[$tomorrow->format('Y-m-d')] ?? null,
$yesterday->format('Y-m-d') => OpenMeteo::get($yesterday) ?? null,
$this->date->format('Y-m-d') => OpenMeteo::get($this->date) ?? null,
$tomorrow->format('Y-m-d') => OpenMeteo::get($tomorrow) ?? null,
]);

$yesterdayPreviousRowDate = $yesterday->clone()->subDay(TemperatureBlanketConfig::get('columns'));
$todayPreviousRowDate = $this->date->clone()->subDay(TemperatureBlanketConfig::get('columns'));
$tomorrowPreviousRowDate = $tomorrow->clone()->subDay(TemperatureBlanketConfig::get('columns'));
$this->weatherData['previous_row'] = collect([
$yesterdayPreviousRowDate->format('Y-m-d') => $cachedWeatherData[$yesterdayPreviousRowDate->format('Y-m-d')] ?? null,
$todayPreviousRowDate->format('Y-m-d') => $cachedWeatherData[$todayPreviousRowDate->format('Y-m-d')] ?? null,
$tomorrowPreviousRowDate->format('Y-m-d') => $cachedWeatherData[$tomorrowPreviousRowDate->format('Y-m-d')] ?? null,
$yesterdayPreviousRowDate->format('Y-m-d') => OpenMeteo::get($yesterdayPreviousRowDate) ?? null,
$todayPreviousRowDate->format('Y-m-d') => OpenMeteo::get($todayPreviousRowDate) ?? null,
$tomorrowPreviousRowDate->format('Y-m-d') => OpenMeteo::get($tomorrowPreviousRowDate) ?? null,
]);

return $this->weatherData;
Expand All @@ -57,7 +54,7 @@ protected function getJsonOutput()
{
return [
'meta' => [
'cachedDate' => Cache::get('openmeteo.'.$this->date->format('Ymd').'.written')?->format('Y-m-d h:i:sa') ?? null,
'cachedDate' => OpenMeteo::cachedDate($this->date)?->format('Y-m-d h:i:sa') ?? null,
'columns' => TemperatureBlanketConfig::get('columns'),
'design' => TemperatureBlanketConfig::design(),
'colors' => TemperatureBlanketConfig::colors(),
Expand Down
45 changes: 28 additions & 17 deletions resources/views/c2c/index.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<link rel="icon" href="favicon.svg">
<link rel="mask-icon" href="favicon.svg" color="#fff">
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
</head>
<body class="antialiased bg-stone-800">
<div class="fixed top-0 left-0 right-0 bg-stone-900 p-8 flex flex-row h-24">
Expand All @@ -21,31 +22,41 @@
</div>

<div class="grid grid-cols-3 gap-4 p-8 pt-32">
@foreach ($info['rows']['previous']['cells'] as $position => $positionInformation)
<div class="grid grid-cols-4 gap-1 @if ($position!='current') opacity-50 @else opacity-75 @endif">
@foreach (end($info['meta']['design']) as $cellDesign)
@foreach ($info['rows']['previous']['cells'] as $position => $positionInformation)
@if (empty($positionInformation['weather']))
<div></div>
@else
<div class="grid grid-cols-4 gap-1 @if ($position!='current') opacity-50 @else opacity-75 @endif">
@foreach (end($info['meta']['design']) as $cellDesign)
@include('c2c.grid.cell', [
'cellDesign' => $cellDesign,
'positionInformation' => $positionInformation,
'colorInformation' => $info['meta']['colors'],
])
@endforeach
</div>
@endforeach
</div>
@endif
@endforeach

@foreach ($info['rows']['current']['cells'] as $position => $positionInformation)
<div class="grid grid-cols-4 gap-1 @if ($position!='current') opacity-75 @else bg-stone-700 bg-opacity-50 outline outline-stone-700/50 outline-4 @endif">
@foreach ($info['meta']['design'] as $rows)
@foreach ($rows as $cellDesign)
@include('c2c.grid.cell', [
'cellDesign' => $cellDesign,
'positionInformation' => $positionInformation,
'colorInformation' => $info['meta']['colors'],
])
@if (empty($positionInformation['weather']))
<div class="bg-stone-900 text-stone-600 p-4 flex flex-row min-h-96"><div class="grow self-center text-center"><i class="bi bi-slash-circle block text-3xl pb-2"></i>No data available for this date</div></div>
@else
<div class="grid grid-cols-4 gap-1 @if ($position!='current') opacity-75 @else bg-stone-700 bg-opacity-50 outline outline-stone-700/50 outline-4 @endif">
@foreach ($info['meta']['design'] as $rows)
@foreach ($rows as $cellDesign)
@include('c2c.grid.cell', [
'cellDesign' => $cellDesign,
'positionInformation' => $positionInformation,
'colorInformation' => $info['meta']['colors'],
])
@endforeach
@endforeach
@endforeach
</div>
@endforeach
@include('c2c.grid.cell.metadata', [
</div>
@endif
@endforeach

@include('c2c.grid.cell.metadata', [
'meta' => $info['meta'],
'date' => $info['rows']['current']['cells']['previous']['date'],
])
Expand Down

0 comments on commit 5c11a53

Please sign in to comment.