Skip to content

Commit

Permalink
feat: initiated calculation of CropSAR
Browse files Browse the repository at this point in the history
  • Loading branch information
JanssenBrm committed Nov 11, 2023
1 parent 60b6ba1 commit dea3fab
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 0 deletions.
3 changes: 3 additions & 0 deletions functions/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import * as sgMail from '@sendgrid/mail';
import { stripeWebhooks } from './controllers/stripe.controller';
import { sharingHooks } from './controllers/sharing.controller';
import { getUserEmail } from './services/user.service';
import { cropsarHook, cropsarPubsub } from './pubsub/cropsar.pubsub';

sgMail.setApiKey(process.env.SENDGRID_API_KEY || '');

Expand Down Expand Up @@ -117,6 +118,8 @@ const execUpdateMeteoStats = async () => {
exports.updateMeteoStats = functions.pubsub.schedule('0 0 * * *').onRun(async () => {
return execUpdateMeteoStats();
});
exports.calculateCropSAR = cropsarPubsub;
exports.cropSARHook = cropsarHook;

exports.stripeWebhooks = stripeWebhooks;
exports.sharingHooks = sharingHooks;
86 changes: 86 additions & 0 deletions functions/src/pubsub/cropsar.pubsub.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import * as functions from 'firebase-functions';
import { getPremiumUsers, getVineyard, getVineyardActions, getVineyards } from '../services/utils.service';
import { Vineyard } from '../models/vineyard.model';
import * as moment from 'moment/moment';
import { Action } from '../models/action.model';
import { executeGraphSync } from '../services/openeo.service';

const createGraph = (polygon: any, start: string, end: string) => {
return {
cropsar: {
process_id: 'CropSAR',
arguments: {
biopar_type: 'ndvi',
date: [start, end],
polygon,
},
namespace: 'vito',
},
save: {
process_id: 'save_result',
arguments: {
data: {
from_node: 'cropsar',
},
format: 'JSON',
},
result: true,
},
};
};
const calculateCropSAR = async (polygon: any, start: string, end: string) => {
try {
console.log(`Calculating CropSAR for polgyon with start ${start} and end ${end}`);
const graph = createGraph(polygon, start, end);
const result = await executeGraphSync(graph, 'json');
console.log(result);
} catch (e) {
console.error(`Could not calculate CropSAR`, e);
}
};
export const getCropSARDates = (actions: Action[]): [string, string] | undefined => {
const dates = actions
.map((a: Action) => moment(a.date).year())
.filter((year: number, index: number, years: number[]) => years.indexOf(year) === index)
.sort();
if (dates.length > 0) {
return [`${dates[0]}-01-01`, `${dates[dates.length - 1]}-12-31`];
} else {
return undefined;
}
};

const calculateAllCropSAR = async () => {
console.log('Starting calculation of CropSAR');

const users: string[] = await getPremiumUsers();
console.log(`Found ${users.length} users to process`);

for (const uid of users) {
const vineyards: string[] = await getVineyards(uid);
console.log(`Found ${vineyards.length} vineyards for user ${uid}`);

for (const id of vineyards) {
try {
const v: Vineyard = await getVineyard(uid, id);
const actions = await getVineyardActions(uid, id);
const dates = getCropSARDates(actions);

if (dates) {
await calculateCropSAR(v.location, dates[0], dates[1]);
} else {
console.warn(`No dates found for calculating CropSAR for vineyard ${v.id}`);
}
} catch (error) {
console.error(`Error processing vineyard ${id} of ${uid}`, error);
}
}
}
};

export const cropsarPubsub = functions.pubsub.schedule('0 1 * * *').onRun(async () => {
return calculateAllCropSAR();
});
export const cropsarHook = functions.https.onRequest(async () => {
return calculateAllCropSAR();
});
60 changes: 60 additions & 0 deletions functions/src/services/openeo.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const OPENEO_URL = process.env.OPENEO_URL || '';
const OPENEO_USER = process.env.OPENEO_USER || '';
const OPENEO_PASSWORD = process.env.OPENEO_PASSWORD || '';

let AUTH_TOKEN: string;

const authenticate = async (username: string, password: string): Promise<string> => {
try {
const response: any = await fetch(`${OPENEO_URL}/credentials/basic`, {
headers: {
Authorization: 'Basic ' + Buffer.from(username + ':' + password).toString('base64'),
},
});

if (!response.ok) {
throw new Error(await response.text());
}
const data = await response.json();
return `basic//${data.access_token}`;
} catch (e) {
console.error('Could not authenticate with OpenEO', e);
throw new Error('Could not authenticate with OpenEO service');
}
};

const getToken = async (): Promise<string> => {
if (!AUTH_TOKEN) {
AUTH_TOKEN = await authenticate(OPENEO_USER, OPENEO_PASSWORD);
}
return AUTH_TOKEN;
};

const getHeaders = async (): Promise<any> => {
const token = await getToken();
return {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
};
};
export const executeGraphSync = async (graph: any, format: string): Promise<string> => {
const response = await fetch(`${OPENEO_URL}/result`, {
method: 'POST',
headers: await getHeaders(),
body: JSON.stringify({
log_level: 'debug',
process: {
process_graph: graph,
},
}),
});
if (!response.ok) {
throw new Error(await response.text());
}

if (format === 'json') {
return response.json();
} else {
throw new Error(`Unknown output format ${format}`);
}
};

0 comments on commit dea3fab

Please sign in to comment.