Saltar al contenido principal

Como construir una extensión de cero en Auravant con React y Vite

· 10 min de lectura
Guille Gonzalez
Project Leader

Imagino que pediste ser desarrollador en Auravant y arrancaste tu proyecto con alguna idea brillante. Luego estuviste un ratito dando vueltas para saber cómo encarar la extensión. ¿De dónde tengo que sacar la información que quiero? ¿Qué tengo que usar para interactuar con la plataforma?

En esta guía te cuento cuál puede ser tu punto de partida.

thumbnail image

¿Cómo leer la información de Auravant?

Auravant ofrece a los desarrolladores de extensiones interactuar con la plataforma. Podemos crear lotes, mapear labores, dibujar en el mapa polígonos, y una lista un poco larga.

Pero, ¿cómo accedo a todas estas funciones?

Antes de adentrarnos en el cómo, entendamos de qué manera está organizada la información en Auravant.

Si ya conoces Auravant, y ya sabes cómo funciona el SDK, la API y su modelo de datos, ve directo a cómo construir una extensión

Modelo de datos

La información que un usuario carga y maneja, se distribuye de la siguiente manera:

modelo de datos modelo de datos

Lo simplifico, en Auravant tenemos lotes, estos se agrupan en campos.

Por otro lado, tenemos campañas, las cuales nuclean labores para cada conjunto Lote/Yeargroup. El yeargroup es simplemente el año del que se está hablando.

Finalmente, tenemos las labores, las cuales tienen un tipo, un estado, insumos asociados y datos extra propios de cada labor.

¡Ahora sí vamos al cómo! No te vayas...

SDK

La principal herramienta que ofrece Auravant para la interacción con la plataforma desde una extensión, es el SDK. Para los que no saben que es un SDK (Kit de Desarrollo de Software), acá les dejo un poco de información.

El SDK de Auravant está dividido en módulos.

Cada módulo agrupa herramientas que trabajan en la misma área de aplicación. Por ejemplo, el módulo map se utiliza para interactuar con el mapa principal.

Actualmente, estos son los módulos que están disponibles en nuestro SDK:

Además, el SDK tiene algunas variables globales que te servirán para ciertas aplicaciones. Estas son, platform y token.

La primera te indica en que plataforma se está corriendo la extensión. Puede ser web, android o ios. La segunda variable es el token único perteneciente a la extensión que es necesario para acceder a la API de Auravant.

Es necesario tenerlas en cuenta por dos razones. La primera es que no deben ser usadas en la extensión si el SDK está cargado porque se podrían pisar. Y, por otro lado, el token no debe estar hardcodeado.

API

Existen funciones específicas de la plataforma que no se pueden acceder desde el SDK. Para esto, Auravant tiene disponible una API con una serie de endpoints que permiten obtener cierta información y realizar operaciones en la plataforma.

Sin embargo, hay dos cosas que se deben tener en cuenta para utilizar estos endpoints desde una extensión. La primera es utilizar el token provisto por el SDK para realizar las consultas a la API. La segunda, es habilitar las funciones en el claimset.

El claimset es una lista de permisos, correspondientes a cada funcionalidad, que debes habilitar desde el espacio del desarrollador. Cada extensión tiene su propio claimset. El usuario que utilizará la extensión será avisado de las funcionalidades que la extensión solicita utilizar, y este debe autorizar estas opoeraciones al instalar la extensión.

Sin más que agregar, se puede acceder a la referencia de la API desde aquí

Set-up en React y Vite

Pasemos a construir una extensión desde cero.

Si bien en este artículo usaremos React + Vite, una extensión puede estar hecha en la tecnología que quieras.

Lo primero que haremos será crear nuestro proyecto en Vite:

foo > npm create vite@latest ext-example -- --template react
foo > cd ext-example
foo > npm install

Una vez creado el proyecto en Vite, vamos a abrirlo con nuestro editor de código preferido, y nos dirigimos al archivo vite.config.js. Aquí le vamos a agregar la configuración base: "" que servirá para luego subir nuestra extensión a Auravant y que funcione correctamente.

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
base: ""
});

Para terminar con las configuraciones, ahora vamos al index.html y cargamos el SDK copiando al final del body el siguiente script:

<script src="https://auraview.auravant.com/sdk/v1.0/aura-package_v1.0.js"></script>

Muy bien, hagamos algo simple y subamos el primer commit a la plataforma.

Voy a eliminar todo lo que Vite me genera como boilerplate, y me quedarán los siguientes archivos.

skafolding

En main.jsx pegamos este código:

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

if(avt.platform === "web"){
window.addEventListener("readySDK", loadExt);
} else {
loadExt();
};

function loadExt() {
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
};

Debido a que el SDK comienza a cargar cuando se abre la extensión, debemos esperar a que finalice su carga para comenzar a cargar nuestro código. Para esto, escuchamos un evento que provee el mismo SDK llamado readySDK. Para el caso de Android o IOS no es necesario esperar este evento, ya que en estos entornos el SDK automaticamente carga previo a la extensión.

En App.jsx vamos a pegar este código, que simplemente muestra un título con el texto "Hola mundo!"

const App = () => {
return (
<h1
style={{
color: "white",
textAlign: "center"
}}
>Hola mundo!</h1>
)
};

export default App;

Ahora ya podemos compilar y subir nuestra extensión

foo > npm run build

Nos dirigimos a la carpeta dist y zippeamos su contenido.

Set-up de extensión en Auravant

Ahora debemos crear la extensión en Auravant. Para esto vamos al espacio desarrollador en el menu:

menu

En el menú encontrarás una lista de extensiones (en caso de tener) y un botón verde debajo de todo que dirá "Crear nueva". Al clickear nos sale esto

crear extension

Una vez hecho esto podremos ver la extension desde el menu de extensiones. Un cartel amarillo que dice "DEV" figura en el icono de la extensión.

Al abrir la extensión en dev vamos a ver también un cartel de Modo desarrollador:

modo desarrollador

Aquí debemos arrastrar el zip que obtuvimos cuando seteamos nuestra extensión en React. Y vamos a ver nuestra primera extensión corriendo correctamente en Auravant.

primer commit

Agregamos la primera función del SDK

Demosle un poco más de vida a la extensión conectandola con Auravant. Podemos replicar la funcionalidad de selección de campo y lote utilizando el SDK. Luego con la información obtenida mostraremos cuantas hectáreas tiene el lote seleccionado.

Nota - Para los selectores se utiliza la biblioteca React Select

En App.jsx copiamos el siguiente código:

// Importamos los hooks necesarios y el componente Select de react-select
import { useEffect, useState } from 'react';
import Select from 'react-select';

const App = () => {

// Seteamos los state que necesitamos para renderizar la extensión
const [farms, setFarms] = useState(avt.generalData.getFarms()) // Toda la info de los campos

const [farmOptions, setFarmOptions] = useState([]); // Opciones de campos para el Selector de campos
const [fieldOptions, setFieldOptions] = useState([]); // Opciones de lotes para el Selector de lotes

const [farmSelected, setFarmSelected] = useState({}); // Info del campo seleccionado
const [fieldSelected, setFieldSelected] = useState({}); // Info del lote seleccionado

const [area, setArea] = useState(); // Area del lote seleccionado

useEffect(() => {
// Al iniciar la extension se cargan las opciones con la info de "farms"
setFarmOptions(farms.map(farm => {
return {
value: farm,
label: farm.nombre
}
}));
},[]);

// Una vez seleccionado el campo, llenamos las opciones de lotes
useEffect(()=> {
if(Object.keys(farmSelected).length){
let fields = farmSelected.lotes;
setFieldOptions(fields.map(field => {
return {
value: field,
label: field.nombre
}
}));
}
},[farmSelected]);

// Cuando cambia el campo y el lote manejamos los estados para setear el campo seleccionado y limpiar los estados de lote
const handleFarmChange = (option) => {
setFarmSelected(option.value);
setFieldOptions([]);
setFieldSelected({});
};

// Cuando cambia el lote seleccionado cambiamos el area
const handleFieldChange = (option) => {
setFieldSelected(option.value);
setArea(option.value.area);
};

return (
<div>
<h1
style={{
color: "white",
textAlign: "center"
}}
>Selección de campo y lote</h1>
<div style={{ marginBottom: "8px"}}>
<span style={{ color: "white" }}>Seleccione Campo:</span>
<Select onChange={(option) => handleFarmChange(option)} options={farmOptions} />
</div>
{/* Una vez seleecionado el campo, renderizamos el selector de lotes */}
{
Object.keys(farmSelected).length ?
<div style={{ marginBottom: "8px"}}>
<span style={{ color: "white" }}>Seleccione Lote:</span>
<Select onChange={(option) => handleFieldChange(option)} options={fieldOptions} />
</div> : ""
}
{/* Una vez seleecionado el lote renderizamos el texto de área del lote */}
{
Object.keys(fieldSelected).length && area ?
<div style={{ marginBottom: "8px"}}>
<span style={{ color: "white" }}>
El lote seleccionado tiene {area} ha
</span>
</div> : ""
}
</div>
)
};

export default App;

Aquí utilizamos avt.generalData.getFarms() para obtener toda la información de los campos del usuario y sus correspondientes lotes.

Consulta a una API

Ahora que tenemos disponible la información de un lote en particular, podemos utilizar esta información para realizar consultas más detalladas por medio de la API.

En este ejemplo obtendremos los valores promedio de NDVI del lote seleccionado, en el últmo mes.

Para ello, usaremos la API Lotes.

Partiendo del ejemplo anterior modificamos App.jsx, quedando el siguiente código:

import { useEffect, useState } from 'react';
import Select from 'react-select';

const App = () => {

// Seteamos los state que necesitamos para renderizar la extensión
const [farms, setFarms] = useState(avt.generalData.getFarms())
const [farmOptions, setFarmOptions] = useState([]);
const [fieldOptions, setFieldOptions] = useState([]);

const [farmSelected, setFarmSelected] = useState({});

const [NDVIMean, setNDVIMean] = useState([]);

useEffect(() => {
setFarmOptions(farms.map(farm => {
return {
value: farm,
label: farm.nombre
}
}));
},[]);

useEffect(()=> {
if(Object.keys(farmSelected).length){
let fields = farmSelected.lotes;
setFieldOptions(fields.map(field => {
return {
value: field,
label: field.nombre
}
}));
}
},[farmSelected]);

const handleFarmChange = (option) => {
setFarmSelected(option.value);
setFieldOptions([]);
setNDVIMean([]);
};

// Ahora cuando se selecciona el lote buscamos los datos de NDVI para el lote seleccionado desde hace 30 días hasta ahora
const handleFieldChange = (option) => {

let field = option.value;
let today = new Date().toISOString().slice(0, 10);
let sevenDaysAgo = new Date(new Date().setDate(new Date().getDate() - 30))
sevenDaysAgo = sevenDaysAgo.toISOString().slice(0, 10);

// Busco información de NDVI de la API:
fetch(
`https://api.auravant.com/api/fields/ndvi?field_id=${field.id}&date_from=${sevenDaysAgo}&date_to=${today}`,
{
headers: {
"Authorization": `Bearer ${token}`
}
}
)
.then((response) => {
if (response.ok) {
return response.json();
} else {
throw new Error();
}
})
.then((response) => {
setNDVIMean(response.ndvi);
})
.catch(() => {
// Si algo sale mal usamos la función async_toast del SDK para avisar al usuario
avt.interface.async_toast({
type: "error",
message: "No se pudo obtener información de NDVI",
options: {
timeOut: 5000,
closeButton: true,
},
});
});
};

return (
<div>
<h1
style={{
color: "white",
textAlign: "center"
}}
>Selección de campo y lote</h1>
{/* Si existen opciones para campos muestro el selector de campos */}
{
farmOptions.length ?
<div style={{ marginBottom: "8px"}}>
<span style={{ color: "white" }}>Seleccione Campo:</span>
<Select onChange={(option) => handleFarmChange(option)} options={farmOptions} />
</div> : ""
}
{/* Una vez seleecionado el campo y se cargan sus opciones, renderizamos el selector de lotes */}
{
Object.keys(farmSelected).length && fieldOptions.length ?
<div style={{ marginBottom: "8px"}}>
<span style={{ color: "white" }}>Seleccione Lote:</span>
<Select onChange={(option) => handleFieldChange(option)} options={fieldOptions} />
</div> : ""
}
{/* Una vez seleecionado el lote y se buscan los datos de NDVI se renderizan todos los datos obtenidos */}
{
NDVIMean.length ?
NDVIMean.map(ndvi => {
return (
<div>
<span style={{ color: "white" }}>Fecha: {ndvi.date} / NDVI Promedio: {ndvi.ndvi_mean}</span>
</div>
)
})
: ""
}
</div>
)
};

export default App;

Y con esto finalizamos nuestra extensión que informa al usuario el promedio de NDVI a lo largo de un mes.

Ahora te animo a que explores tus propias ideas, que otras APIs podrías usar, o que funciones del SDK te facilitarían el trabajo y de qué manera podrías aprovechar Auravant para ser el Sistema Operativo de tus aplicaciones para el Agro.