Skip to content

Commit

Permalink
feat: gdk stats (#269)
Browse files Browse the repository at this point in the history
* feat: first gdk stats

* feat: monthly waterings

* feat: mostFrequentTreeSpecies

* fix: typo

* chore: refactoring

* fix: error handling and env

* chore: comments

* feat: more database functions

* feat: env var existence check

* feat: more distinguishable errors

* fix: console.error instead of console.log for errors

* chore: cleanup

* fix: env variables

* fix: more restrictive cors, chore: cleanup
  • Loading branch information
Jaszkowic committed Jul 4, 2024
1 parent b5e5281 commit c34008c
Show file tree
Hide file tree
Showing 15 changed files with 559 additions and 67 deletions.
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ deno test --allow-all supabase/functions/tests/submit-contact-request-tests.ts -
- **(Not recommended but possible)** Link your local project directly to the remote `supabase link --project-ref <YOUR PROJECT REF>` (will ask you for your database password from the creation process)
- **(Not recommended but possible)** Push your local state directly to your remote project `supabase db push` (will ask you for your database password from the creation process)

#### Supabase
#### Supabase Auth

Some of the requests need a authorized user. You can create a new user using email password via the Supabase API.

Expand All @@ -142,6 +142,17 @@ curl --request POST \

See the [docs/api.http](./docs/api.http) file for more examples or take a look into the API documentation in your local supabase instance under http://localhost:54323/project/default/api?page=users

#### Supabase Edge Functions
To run the Supabase Edge Functions locally:

- Setup the .env file in [supabase/.env](supabase/.env) according to [supabase/.env.sample](supabase/.env.sample)
- Note: The env variables `SUPABASE_SERVICE_ROLE_KEY` and `SUPABASE_URL` are injected automatically and can't be set the in the [supabase/.env](supabase/.env) file. If you want to overwrite them, you have to rename the environment variables to not start with `SUPABASE_`. For reference, see: https://supabase.com/docs/guides/functions/secrets
- With the environment variables setup correctly, execute `supabase functions serve --no-verify-jwt --env-file supabase/.env`

To deploy the Edge Functions in your linked remote Supabase project, execute:
- `supabase functions deploy`
- Make sure that you set the proper environment variables in the remote Supabase project too

## Tests

Locally you will need supabase running and a `.env` file with the right values in it.
Expand Down
52 changes: 26 additions & 26 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"node": ">=18"
},
"dependencies": {
"@supabase/supabase-js": "2.43.2"
"@supabase/supabase-js": "2.43.5"
},
"devDependencies": {
"@saithodev/semantic-release-backmerge": "4.0.1",
Expand Down
95 changes: 95 additions & 0 deletions src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,66 @@ export type Database = {
},
]
}
daily_weather_data: {
Row: {
avg_cloud_cover_percentage: number | null
avg_dew_point_celcius: number | null
avg_pressure_msl: number | null
avg_relative_humidity_percentage: number | null
avg_temperature_celsius: number | null
avg_visibility_m: number | null
avg_wind_direction_deg: number | null
avg_wind_gust_direction_deg: number | null
avg_wind_gust_speed_kmh: number | null
avg_wind_speed_kmh: number | null
created_at: string
day_finished: boolean
id: number
measure_day: string
source_dwd_station_ids: string[] | null
sum_precipitation_mm_per_sqm: number | null
sum_sunshine_minutes: number | null
}
Insert: {
avg_cloud_cover_percentage?: number | null
avg_dew_point_celcius?: number | null
avg_pressure_msl?: number | null
avg_relative_humidity_percentage?: number | null
avg_temperature_celsius?: number | null
avg_visibility_m?: number | null
avg_wind_direction_deg?: number | null
avg_wind_gust_direction_deg?: number | null
avg_wind_gust_speed_kmh?: number | null
avg_wind_speed_kmh?: number | null
created_at?: string
day_finished?: boolean
id?: number
measure_day: string
source_dwd_station_ids?: string[] | null
sum_precipitation_mm_per_sqm?: number | null
sum_sunshine_minutes?: number | null
}
Update: {
avg_cloud_cover_percentage?: number | null
avg_dew_point_celcius?: number | null
avg_pressure_msl?: number | null
avg_relative_humidity_percentage?: number | null
avg_temperature_celsius?: number | null
avg_visibility_m?: number | null
avg_wind_direction_deg?: number | null
avg_wind_gust_direction_deg?: number | null
avg_wind_gust_speed_kmh?: number | null
avg_wind_speed_kmh?: number | null
created_at?: string
day_finished?: boolean
id?: number
measure_day?: string
source_dwd_station_ids?: string[] | null
sum_precipitation_mm_per_sqm?: number | null
sum_sunshine_minutes?: number | null
}
Relationships: []
}
profiles: {
Row: {
id: string
Expand Down Expand Up @@ -311,6 +371,41 @@ export type Database = {
[_ in never]: never
}
Functions: {
accumulated_weather_per_month: {
Args: {
limit_monts: number
}
Returns: {
measure_day: string
sum_precipitation_mm_per_sqm: number
avg_temperature_celsius: number
avg_pressure_msl: number
sum_sunshine_minutes: number
avg_wind_direction_deg: number
avg_wind_speed_kmh: number
avg_cloud_cover_percentage: number
avg_dew_point_celcius: number
avg_relative_humidity_percentage: number
avg_visibility_m: number
avg_wind_gust_direction_deg: number
avg_wind_gust_speed_kmh: number
}[]
}
calculate_avg_waterings_per_month: {
Args: Record<PropertyKey, never>
Returns: {
month: string
watering_count: number
avg_amount_per_watering: number
}[]
}
calculate_top_tree_species: {
Args: Record<PropertyKey, never>
Returns: {
gattung_deutsch: string
percentage: number
}[]
}
count_by_age: {
Args: {
start_year: number
Expand Down
3 changes: 0 additions & 3 deletions supabase/.env.sample
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
URL=http://host.docker.internal:54321
ANON_KEY=ey..
SERVICE_ROLE_KEY=ey...
ALLOWED_ORIGIN=http://localhost:5173
SMTP_HOST=...
SMTP_USER=...
Expand Down
7 changes: 7 additions & 0 deletions supabase/functions/_shared/check-env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const loadEnvVars = (vars: string[]) => {
const missingVars = vars.filter((v) => !Deno.env.get(v));
if (missingVars.length > 0) {
throw new Error(`Missing environment variables: ${missingVars.join(", ")}`);
}
return vars.map((v) => Deno.env.get(v));
};
43 changes: 43 additions & 0 deletions supabase/functions/_shared/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
export interface TreeSpecies {
speciesName?: string;
percentage: number;
}

export interface Monthly {
month: string;
wateringCount: number;
averageAmountPerWatering: number;
totalSum: number;
}

export interface Watering {
id: string;
lat: number;
lng: number;
amount: number;
timestamp: string;
}

export interface TreeAdoptions {
count: number;
veryThirstyCount: number;
}

export interface GdkStats {
numTrees: number;
numPumps: number;
numActiveUsers: number;
numWateringsThisYear: number;
monthlyWaterings: Monthly[];
treeAdoptions: TreeAdoptions;
mostFrequentTreeSpecies: TreeSpecies[];
totalTreeSpeciesCount: number;
waterings: Watering[];
monthlyWeather: MonthlyWeather[];
}

export interface MonthlyWeather {
month: string;
averageTemperatureCelsius: number;
totalRainfallLiters: number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@ export async function checkIfContactRequestIsAllowed(
const { data: senderData, error: senderDataError } =
await supabaseClient.auth.getUser(token);

console.log(senderData);

if (senderDataError) {
console.log(senderDataError);
return { isAllowed: false, reason: "unauthorized", lookupData: undefined };
}

Expand All @@ -39,10 +36,7 @@ export async function checkIfContactRequestIsAllowed(
.eq("id", senderData.user.id)
.single();

console.log(senderLookupData);

if (senderLookupDataError) {
console.log(senderLookupDataError);
return { isAllowed: false, reason: "not_found", lookupData: undefined };
}

Expand All @@ -55,7 +49,6 @@ export async function checkIfContactRequestIsAllowed(
.single();

if (recipientDataError) {
console.log(recipientDataError);
return { isAllowed: false, reason: "not_found", lookupData: undefined };
}

Expand All @@ -69,7 +62,6 @@ export async function checkIfContactRequestIsAllowed(
.not("contact_mail_id", "is", null); // only count sent emails

if (requestsToRecipientError) {
console.log(requestsToRecipientError);
return {
isAllowed: false,
reason: "internal_server_error",
Expand All @@ -95,7 +87,6 @@ export async function checkIfContactRequestIsAllowed(
.gt("created_at", sub(new Date(), { days: 1 }).toISOString());

if (requestsOfLast24hError) {
console.log(requestsOfLast24hError);
return {
isAllowed: false,
reason: "internal_server_error",
Expand Down
2 changes: 1 addition & 1 deletion supabase/functions/_shared/cors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const ALLOWED_ORIGIN = Deno.env.get("ALLOWED_ORIGIN");

export const corsHeaders = {
"Access-Control-Allow-Origin": ALLOWED_ORIGIN,
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
"Access-Control-Allow-Headers":
"Content-Type,Authorization,x-client-info,apikey",
};
14 changes: 14 additions & 0 deletions supabase/functions/_shared/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export enum ErrorTypes {
GdkStatsPump = "gdk_stats_pumps",
GdkStatsUser = "gdk_stats_users",
GdkStatsWatering = "gdk_stats_waterings",
GdkStatsAdoption = "gdk_stats_adoptions",
GdkStatsTreeSpecie = "gdk_stats_tree_species",
GdkStatsWeather = "gdk_stats_weather",
}

export class GdkError extends Error {
constructor(message: string, public errorType: ErrorTypes) {
super(message);
}
}
Loading

0 comments on commit c34008c

Please sign in to comment.