Skip to content

Commit

Permalink
Merge pull request #229 from kilowatts-io/228-handle-elexon-outages
Browse files Browse the repository at this point in the history
228 handle elexon outages
  • Loading branch information
BenjaminWatts authored Feb 28, 2024
2 parents 0bb9cc8 + f1f9471 commit f7656c8
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 17 deletions.
7 changes: 6 additions & 1 deletion backend/lambda/gb_snapshot/bm/boalf.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,13 @@ def _parse_raw_data(self, raw: RawBoalfResponse) -> pd.DataFrame:
logging.info(
f"parsing {len(raw.data)} pn records for {self.params.model_dump_json()}..."
)
data = [r.model_dump() for r in raw.data]
if len(data) == 0:
logging.info(f"no boalf data for {self.params}")
output = {}
return pd.DataFrame(columns=["level", "delta"], index=output.keys())

df = pd.DataFrame([r.model_dump() for r in raw.data])
df = pd.DataFrame(data)
df = df.dropna(subset=["bmUnit"])
output = {}
for bmUnit, group in df.groupby("bmUnit"):
Expand Down
8 changes: 7 additions & 1 deletion backend/lambda/gb_snapshot/bm/mels.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,14 @@ def _parse_raw_data(self, raw: RawMelsResponse) -> pd.Series:
logging.info(
f"parsing {len(raw.data)} mels records for {self.params.model_dump_json()}..."
)
data = [r.model_dump() for r in raw.data]
if len(data) == 0:
raise Exception(
"no data returned from mels request. Likely a problem with the API."
)

df = pd.DataFrame(data)

df = pd.DataFrame([r.model_dump() for r in raw.data])
df = df.dropna(subset=["bmUnit"])
output = {}
for bmUnit, group in df.groupby("bmUnit"):
Expand Down
8 changes: 7 additions & 1 deletion backend/lambda/gb_snapshot/bm/pn.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,13 @@ def _parse_raw_data(self, raw: RawPnResponse) -> pd.DataFrame:
logging.info(
f"parsing {len(raw.data)} pn records for {self.params.model_dump_json()}..."
)
df = pd.DataFrame([r.model_dump() for r in raw.data])

data = [r.model_dump() for r in raw.data]
if len(data) == 0:
raise Exception(
"no data returned from mels request. Likely a problem with the API."
)
df = pd.DataFrame(data)
df = df.dropna(subset=["bmUnit"])
output = {}
for bmUnit, group in df.groupby("bmUnit"):
Expand Down
2 changes: 1 addition & 1 deletion backend/lib/kilowatts-grid-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export class KilowattsGridStack extends cdk.Stack {
functionName: "gb-snapshot",
code: lambda.Code.fromAsset("lambda"),
handler: "gb_snapshot.handler.handler",
timeout: cdk.Duration.seconds(60),
timeout: cdk.Duration.seconds(90),
memorySize: 512,
environment: {
BUCKET_NAME: bucket.bucketName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { GbLiveListItemBalancingTotal } from "../../live-list-item/live-list-ite

export const GbBalancingTotals = () => {
const { data } = useGbSummaryOutputQuery(undefined, {
pollingInterval: 1000 * 15
pollingInterval: 1000 * 60
});

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from "react";
import { FlatList, StyleSheet, View } from "react-native";
import { Button, Card, Icon, Text } from "@rneui/themed";
import { FlatList, Platform } from "react-native";

import { useGbSummaryOutputQuery } from "../../../../state/apis/cloudfront/api";
import {
Expand All @@ -9,6 +8,7 @@ import {
calculateCycleSeconds
} from "../../../../state/utils";
import { ErrorDataRetryCard } from "../../error-data-retry-card";
import StaleDataCard from "../../stale-data-card";
import { GbLiveListItem } from "../live-list-item/live-list-item";

import { GbBalancingTotals } from "./balancing-totals/balancing-totals";
Expand All @@ -19,7 +19,7 @@ export const GbTotalsList: React.FC = () => {
const { data, isLoading, refetch, isError } = useGbSummaryOutputQuery(
undefined,
{
pollingInterval: 1000 * 15,
pollingInterval: 1000 * 60,
refetchOnReconnect: true
}
);
Expand All @@ -33,6 +33,7 @@ export const GbTotalsList: React.FC = () => {
data={data && data.totals}
refreshing={isLoading}
onRefresh={() => refetch()}
ListHeaderComponent={Platform.OS !== "web" && StaleDataCard}
renderItem={({ item }) => (
<GbLiveListItem
type={item.code}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
calculateCycleSeconds
} from "../../../../state/utils";
import { ErrorDataRetryCard } from "../../error-data-retry-card";
import StaleDataCard from "../../stale-data-card";
import { GbLiveListItem } from "../live-list-item/live-list-item";

import Pagination from "./pagination";
Expand All @@ -23,7 +24,7 @@ export const GbUnitGroupsList: React.FC = () => {
const { data, isLoading, refetch, isError } = useGbSummaryOutputQuery(
undefined,
{
pollingInterval: 1000 * 15,
pollingInterval: 1000 * 60,
refetchOnReconnect: true
}
);
Expand Down Expand Up @@ -72,6 +73,7 @@ export const GbUnitGroupsList: React.FC = () => {
code={item.code}
/>
)}
ListHeaderComponent={Platform.OS !== "web" && StaleDataCard}
ListFooterComponent={
Platform.OS === "web" ? (
<Pagination
Expand Down
1 change: 1 addition & 0 deletions components/gb-live/live.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { selectors } from "../../state/gb/live";
import { GbLiveBottomSheetTabs } from "./bottom-sheet-tabs/tabs";
import SvgMap from "./svg-map/svg-map";
import { WithTermsAndConditionsAccepted } from "./terms-and-conditions/acceptance";
import StaleDataCard from "./stale-data-card";

const SNAP_POINTS = ["10%", "20%", "30%", "40%", "50%", "75%", "90%"];
const INITIAL_SNAP_POINT_INDEX = 2;
Expand Down
20 changes: 12 additions & 8 deletions components/gb-live/live.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { GbTotalsList } from "./bottom-sheet-tabs/totals-list/totals-list";
import { GbUnitGroupsList } from "./bottom-sheet-tabs/unit-groups-list/unit-groups-list";
import SvgMap from "./svg-map/svg-map";
import NativeAppDownloadLinks from "./native-app-download-links";
import StaleDataCard from "./stale-data-card";

const MapTab: React.FC = () => {
return (
Expand Down Expand Up @@ -42,14 +43,17 @@ const Live: React.FC = () => {
<Tab.Item>List</Tab.Item>
<Tab.Item>App</Tab.Item>
</Tab>
{currentTab === 0 && <MapTab />}
{currentTab === 1 && <GbTotalsList />}
{currentTab === 2 && (
<div style={styles.listWrapper}>
<GbUnitGroupsList />
</div>
)}
{currentTab === 3 && <NativeAppDownloadLinks />}
<>
<StaleDataCard />
{currentTab === 0 && <MapTab />}
{currentTab === 1 && <GbTotalsList />}
{currentTab === 2 && (
<div style={styles.listWrapper}>
<GbUnitGroupsList />
</div>
)}
{currentTab === 3 && <NativeAppDownloadLinks />}
</>
</>
);
};
Expand Down
56 changes: 56 additions & 0 deletions components/gb-live/stale-data-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from "react";
import { Linking, View } from "react-native";
import { Button, Card, Text } from "@rneui/themed";

import { useGbSummaryOutputQuery } from "../../state/apis/cloudfront/api";
import { GbSummaryOutputResponse } from "../../state/apis/cloudfront/types";

interface StaleDataCardProps {}

const THRESHOLD_MINUTES = 10;

const determineIfDataIsStale = (data: GbSummaryOutputResponse) => {
if (!data) return false;
const lastUpdated = new Date(data.dt);
const now = new Date();
const diff = now.getTime() - lastUpdated.getTime();
return diff > THRESHOLD_MINUTES * 60 * 1000;
};

const StaleDataCard: React.FC<StaleDataCardProps> = () => {
const data = useGbSummaryOutputQuery();
if (!data.currentData) return undefined;
const isStale = determineIfDataIsStale(data.data);
if (!isStale) return undefined;
return (
<View style={{ paddingBottom: 10 }}>
<Card>
<Card.Title>Data Error</Card.Title>
<Text>
{`Data shown is from ${new Date(Date.parse(data.data.dt)).toLocaleString()} and is stale/out-of-date. There may be an outage affecting Elexon/BMRS data, our data pipeline, or your internet connection. `}
</Text>
<View style={{ height: 20 }} />

<Button
type="outline"
size="sm"
onPress={() => {
Linking.openURL("https://elexonexternal.newsweaver.com/");
}}
>
Elexon/BMRS Outage Notices
</Button>

<View style={{ height: 10 }} />

<Button
size="sm"
onPress={() => data.refetch()}
>
Retry
</Button>
</Card>
</View>
);
};
export default StaleDataCard;

0 comments on commit f7656c8

Please sign in to comment.