Skip to content

Commit

Permalink
history select, download and multi-select working
Browse files Browse the repository at this point in the history
  • Loading branch information
CropWatchDevelopment committed May 10, 2024
1 parent 8d5313d commit 2085657
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 12 deletions.
1 change: 0 additions & 1 deletion src/lib/components/ui/sensors/CW_SS_TME.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@
</div>
<DarkCard title={$_('soil_temperature')} value={temperature} optimalValue={20} unit={'ºC'}>
<div class="chart" use:Highcharts={tempConfig} />
{data.length}
</DarkCard>

<DarkCard title={$_('soil_moisture')} value={moisture} optimalValue={40} unit={'%'}>
Expand Down
Binary file modified src/lib/images/weather/night/c01n.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 21 additions & 6 deletions src/routes/(api)/api/v1/devices/[dev_eui]/data/+server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { redirect, type RequestHandler } from "@sveltejs/kit";
import moment from "moment";

export const GET: RequestHandler = async ({ url, params, locals: { supabase, safeGetSession } }) => {
const { session } = await safeGetSession();
Expand All @@ -10,6 +11,10 @@ export const GET: RequestHandler = async ({ url, params, locals: { supabase, saf
const query = new URLSearchParams(url.search);
const startingPage = parseInt(query.get('page') || '0');
const itemsPerPage = parseInt(query.get('count') || '10');
const from = query.get('from');
const to = query.get('to');
const dataPoints = query.get('data-points');
const csv = query.get('csv');

if (!dev_eui) {
return new Response(
Expand All @@ -22,36 +27,46 @@ export const GET: RequestHandler = async ({ url, params, locals: { supabase, saf
let deviceType = await getDeviceDataTable(dev_eui, session, supabase);
let baseQuery = supabase
.from(deviceType.data_table)
.select('*')
.select(dataPoints ?? '*')
.eq('dev_eui', dev_eui)
.order('created_at', { ascending: false })
.range(startingPage, startingPage + itemsPerPage - 1);

if (from) {
baseQuery = baseQuery.gte('created_at', moment(from).toISOString());
}
if (to) {
baseQuery = baseQuery.lte('created_at', moment(to).toISOString());
}

baseQuery.range(startingPage, startingPage + itemsPerPage - 1);

// Conditionally apply `.single()` if itemsPerPage is 1
let finalQuery = itemsPerPage === 1 ? baseQuery.single() : baseQuery;

const { data, error } = await finalQuery;
finalQuery = csv ? finalQuery.csv() : finalQuery;

const { data, error } = await finalQuery

if (error) {
return new Response(
JSON.stringify({ error: error.message }),
{
status: 500,
headers: {
'Content-Type': 'application/json',
'Content-Type': csv ? 'application/csv' : 'application/json',
}
});
}

if (data) {
if (data && !csv) {
data.primaryData = deviceType.primary_data;
data.secondaryData = deviceType.secondary_data;
data.primary_data_notation = deviceType.primary_data_notation;
data.secondary_data_notation = deviceType.secondary_data_notation;
}

return new Response(
JSON.stringify(data || { error: 'No data found' }),
data ? (csv ? data : JSON.stringify(data)) : error,
{
status: data ? 200 : 404,
headers: {
Expand Down
14 changes: 14 additions & 0 deletions src/routes/app/devices/[dev_eui]/history/+page.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { redirect } from "@sveltejs/kit";

/** @type {import('./$types').PageLoad} */
export async function load({ params, fetch, locals: { supabase, safeGetSession } }) {
let session = await safeGetSession();
if (!session || !session?.user) throw redirect(304, 'auth/login');

const sensorDataRequest = await fetch(`/api/v1/devices/${params.dev_eui}/data/latest`);
const sensorData = await sensorDataRequest.json();

return {
sensorData: sensorData,
};
}
169 changes: 164 additions & 5 deletions src/routes/app/devices/[dev_eui]/history/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,8 +1,129 @@
<script>
import Back from "$lib/components/ui/Back.svelte";
import historyImage from "$lib/images/UI/cw_history.svg";
<script lang="ts">
import Back from '$lib/components/ui/Back.svelte';
import historyImage from '$lib/images/UI/cw_history.svg';
import { Button, DateRangeField, MultiSelect, PeriodType } from 'svelte-ux';
import { subDays, subWeeks } from 'date-fns';
import { mdiDownload } from '@mdi/js';
import { _ } from 'svelte-i18n';
import { HighChartsTimeSeriesChart } from '$lib/charts/highcharts/timeseries.js';
import Highcharts from '$lib/actions/highcharts.action';
import { page } from '$app/stores';
import DarkCard from '$lib/components/ui/DarkCard.svelte';
import DarkCard2 from '$lib/components/ui/DarkCard2.svelte';
export let data;
export let sensorName = 'NS';
let today = new Date();
let value = {
from: subWeeks(today, 1),
to: today,
periodType: PeriodType.Day
};
let chartKey = 0;
let options = Object.keys(data.sensorData).map((key) => {
return {
value: key,
name: $_(key)
};
});
let selectedDataPoints: string[] = [];
$: chartConfig = HighChartsTimeSeriesChart(chartData.series, chartData.yAxes, '');
let chartData = {
series: [],
yAxes: []
};
// Define a list of colors for the chart lines and Y-axes
const colors = ['lightblue', 'lightgreen', 'red', 'purple', 'orange', 'grey', 'pink', 'cyan', 'yellow', 'magenta'];
function createChartSeries(res) {
const series = {};
const yAxes = {};
res.forEach((d) => {
Object.keys(d).forEach((key, i) => {
if (key !== 'created_at') {
if (!series[key]) {
let colorIndex = Object.keys(yAxes).length % colors.length; // Cycle through colors
series[key] = {
type: 'line',
yAxis: colorIndex, // Assign to new yAxis index
name: $_(key),
data: [],
color: colors[colorIndex] // Use color from the array
};
yAxes[key] = {
title: {
text: $_(key),
style: { color: colors[colorIndex] }
},
labels: {
format: '{value}', // Customize if needed
style: { color: colors[colorIndex] }
},
opposite: i % 2 === 0
};
}
series[key].data.push([new Date(d.created_at).valueOf(), d[key]]);
}
});
});
return {
series: Object.values(series),
yAxes: Object.values(yAxes)
};
}
const loadSelectedDataRange = async () => {
const response = await fetch(
`/api/v1/devices/${$page.params.dev_eui}/data?from=${value.from}&to=${value.to}&data-points=${selectedDataPoints}&page=0&count=10000`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
}
);
const res = await response.json();
chartData = createChartSeries(res);
chartKey++; // Increment key to force re-render
};
const downloadCSV = (sensorName) => {
const date = new Date();
const formattedDate = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date.getSeconds().toString().padStart(2, '0')}`;
const fileName = `${sensorName}-${formattedDate}.csv`;
fetch(
`/api/v1/devices/${$page.params.dev_eui}/data?from=${value.from}&to=${value.to}&data-points=${selectedDataPoints}&page=0&count=10000&csv=1`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
}
)
.then((response) => response.blob())
.then((blob) => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
})
.catch((error) => console.error('Error downloading the file:', error));
};
</script>

<div class="flex flex-row bg-emerald-300 p-4 text-center justify-center">
<img src={historyImage} alt="History" class="w-10 h-10" />
<p class="text-surface-100 text-3xl ml-2">History and Downloads</p>
Expand All @@ -13,5 +134,43 @@
</div>

<div class="m-6">

</div>
<div>
<DateRangeField bind:value stepper rounded center />
</div>

<div class="grid lg:grid-flow-col grid-flow-rowgrid-cols-1 md:grid-cols-2 my-4 gap-2">
<DarkCard2>
<MultiSelect
{options}
value={selectedDataPoints}
on:change={(e) => {
selectedDataPoints = e.detail.value;
selectedDataPoints.push('created_at');
loadSelectedDataRange();
}}
inlineSearch
/>
</DarkCard2>
<DarkCard2>
<h2 class="text-xl text-neutral-content">Data Preview:</h2>
{#if chartKey === 0}
<p class="text-center text-neutral-content">No data to display</p>
{/if}
{#key chartKey}
<div class="chart" use:Highcharts={chartConfig} />
{/key}
</DarkCard2>
</div>

<div class="mt-4">
<Button
variant="fill"
color="info"
icon={mdiDownload}
on:click={() => downloadCSV('sensor')}
size="lg"
class="w-full"
rounded>Download Selected Range</Button
>
</div>
</div>
1 change: 1 addition & 0 deletions src/routes/app/devices/[dev_eui]/history/+page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const ssr = false;

0 comments on commit 2085657

Please sign in to comment.