Cómo integrar Azure Text Analytics en una aplicación de análisis de feedback

  • Imagen de redactor Daniel J. Saldaña
  • 24 de marzo de 2024
Cómo integrar Azure Text Analytics en una aplicación de análisis de feedback

En la era digital, comprender las percepciones y emociones de los usuarios es esencial para mejorar productos y servicios. Microsoft Azure Text Analytics ofrece una solución robusta para analizar el sentimiento y las frases clave de los textos. En este post, te guiaré a través de una prueba de concepto para integrar Azure Text Analytics en una aplicación de análisis de feedback.

Paso 1: Configurar el entorno de desarrollo

Antes de comenzar, asegúrate de tener las siguientes herramientas y servicios configurados:

  • Node.js y npm instalados en tu máquina local.
  • Una cuenta de Azure con acceso al servicio Text Analytics.
  • Un entorno de base de datos PostgreSQL.

Paso 2: Preparar el frontend

Nuestra aplicación de feedback se construirá usando React. Crearemos un componente para enviar feedback y visualizar el análisis de sentimientos.

  1. Crea un nuevo proyecto React: Utiliza create-react-app para configurar tu entorno de frontend.

  2. Integra el componente AnalyzeFeedback: Este componente es responsable de enviar el título del feedback y mostrar los resultados del análisis.

    Archivo de ejemplo a integrar:

"AnalyzeFeedback.tsx
'use client'
import { useEffect, useState } from 'react';
import styles from './AnalyzeFeedback.module.scss';
import ClassName from '@/types/ClassName';
interface AnalyzeFeedbackProps extends ClassName {
title: string;
}
const AnalyzeFeedback = ({ className, title }: AnalyzeFeedbackProps) => {
const [score, setScore] = useState<number | null>(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
setIsLoading(true);
fetch('https://api.danieljsaldana.dev/api/getfeedbackscores', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ title }),
})
.then(response => response.json())
.then(data => {
if (data.compositeScore !== undefined) {
setScore(data.compositeScore);
} else {
setScore(null);
}
})
.catch(() => setScore(null))
.finally(() => setIsLoading(false));
}, [title]);
const getBackgroundColor = (score: number | null) => {
if (score === null) {
return '#d3d3d3';
} else if (score > 80) {
return '#0ac769';
} else if (score >= 50 && score <= 79) {
return '#ffad00';
} else {
return '#ea001e';
}
};
const backgroundColor = getBackgroundColor(score);
const circumference = 301.59289474462014;
const offset = score !== null ? ((100 - score) / 100) * circumference : 0;
return (
<div className={`rounded-full h-[24px] w-[22px] ${className ?? ''}`}>
<svg width="24" height="24" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg">
<g shapeRendering="geometricPrecision">
<circle cx="64" cy="64" fill={backgroundColor} r="64" />
<circle cx="64" cy="64" fill="none" r="48" stroke="rgba(0,0,0,.1)" strokeWidth="10" />
{!isLoading && score !== null && (
<circle
cx="64"
cy="64"
fill="none"
r="48"
stroke="white"
strokeDasharray={`${circumference} ${circumference}`}
strokeDashoffset={offset}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="10"
className="score_progress__quTNG"
/>
)}
{(isLoading || score === null) && (
<svg className={`${styles.withIconIcon__MHUeb} ${styles.fadeIn}`} data-testid="geist-icon" fill="none" height="70" shapeRendering="geometricPrecision" stroke="#999" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" viewBox="0 0 24 24" width="70" x="29" y="29">
<path d="M22 12h-4l-3 9L9 3l-3 9H2"></path>
</svg>
)}
{!isLoading && score !== null && (
<text
fill="white"
fontSize="42"
fontWeight="500"
textAnchor="middle"
x="64"
y="79"
>
{score}
</text>
)}
</g>
</svg>
</div>
);
};
export default AnalyzeFeedback;
  1. Estiliza tu componente: Usa los estilos predefinidos para hacer que tu componente sea visualmente atractivo y claro para los usuarios.

    Archivo de ejemplo a integrar:

AnalyzeFeedback.module.scss
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.fadeIn {
animation: fadeIn ease-in 1s;
animation-fill-mode: forwards;
}

Paso 3: Configurar el backend

El backend de nuestra aplicación se construirá con Node.js y se comunicará con los servicios de Azure para analizar el feedback.

  1. Establece la API de análisis de feedback: Crea una API que reciba el feedback, analice el sentimiento y las frases clave usando Azure Text Analytics, e inserte los resultados en la base de datos.

    Archivos de ejemplo a integrar: analyzeFeedback.js

import { TextAnalyticsClient, AzureKeyCredential } from '@azure/ai-text-analytics';
import { enableCors } from '@/src/middleware/enableCors';
import { methodValidator } from '@/src/utils/methodValidator';
import { insertFeedback } from '@/src/core/insertFeedback';
import dotenv from 'dotenv';
dotenv.config();
const textAnalyticsClient = new TextAnalyticsClient(
process.env.AZURE_TEXT_ANALYTICS_ENDPOINT,
new AzureKeyCredential(process.env.AZURE_TEXT_ANALYTICS_KEY)
);
async function analyzeFeedback(req, res) {
try {
await methodValidator(req, res, 'POST');
if (res.headersSent) return;
const { title, text, user } = req.body;
if (!title || !text || !user) {
return res.status(400).json({ error: 'Faltan datos requeridos para el análisis' });
}
const sentimentResult = await textAnalyticsClient.analyzeSentiment([text]);
const sentiment = sentimentResult[0];
const keyPhrasesResult = await textAnalyticsClient.extractKeyPhrases([text]);
const keyPhrases = keyPhrasesResult[0];
await insertFeedback(
title,
sentiment.sentiment,
sentiment.confidenceScores.positive,
sentiment.confidenceScores.neutral,
sentiment.confidenceScores.negative,
user
);
res.status(200).json({
title,
user,
sentiment: sentiment.sentiment,
confidenceScores: sentiment.confidenceScores,
keyPhrases: keyPhrases.keyPhrases,
});
} catch (error) {
console.error('Error al analizar el feedback:', error);
res.status(500).json({ error: `Error al analizar el feedback: ${error.message}` });
}
}
export default enableCors(analyzeFeedback);
  1. Desarrolla la API para obtener puntajes de feedback: Esta API recupera los promedios de los puntajes de feedback basados en el título proporcionado.

    Archivos de ejemplo a integrar:

getFeedbackScores.js
import { enableCors } from '@/src/middleware/enableCors';
import { methodValidator } from '@/src/utils/methodValidator';
import { sanitizeTitleForFilename } from '@/src/utils/sanitizeTitleForFilename';
import { getAverageFeedbackScores } from '@/src/core/getAverageFeedbackScores';
import dotenv from 'dotenv';
dotenv.config();
export async function getFeedbackScores(req, res) {
await methodValidator(req, res, 'POST');
if (res.headersSent) return;
const { title } = req.body;
if (!title) {
return res.status(400).json({ error: 'Falta el título para el análisis' });
}
const sanitizedTitle = sanitizeTitleForFilename(title);
console.log('Título sanitizado:', sanitizedTitle);
try {
const averages = await getAverageFeedbackScores(sanitizedTitle);
if (
!averages ||
(averages.average_positive === 0 && averages.average_neutral === 0 && averages.average_negative === 0)
) {
return res
.status(200)
.json({ error: 'No se encontraron datos para el título proporcionado', compositeScore: null });
}
const avgPositive = parseFloat(averages.average_positive);
const avgNeutral = parseFloat(averages.average_neutral);
const avgNegative = parseFloat(averages.average_negative);
console.log('Promedios convertidos:', avgPositive, avgNeutral, avgNegative);
let compositeScore = avgPositive + 0.5 * avgNeutral - avgNegative;
compositeScore = Math.max(0, Math.min(1, compositeScore)) * 100;
compositeScore = Math.round(compositeScore);
console.log('Puntuación compuesta:', compositeScore);
res.status(200).json({ compositeScore: compositeScore });
} catch (error) {
console.error('Error al obtener los promedios del feedback:', error);
res.status(500).json({ error: `Error al obtener los promedios del feedback: ${error.message}` });
}
}
export default enableCors(getFeedbackScores);
  1. Implementa funciones de base de datos: Usa estas funciones para interactuar con tu base de datos PostgreSQL y manejar la inserción y recuperación de datos de feedback.

    Archivos de ejemplo a integrar:

getAverageFeedbackScores.js
import { sql } from '@vercel/postgres';
export async function getAverageFeedbackScores(title) {
try {
const result = await sql`
SELECT
AVG(positive) AS average_positive,
AVG(neutral) AS average_neutral,
AVG(negative) AS average_negative
FROM post_feedback
WHERE title = ${title}
GROUP BY title;
`;
if (result.rows && result.rows.length > 0) {
return result.rows[0];
} else {
return { average_positive: 0, average_neutral: 0, average_negative: 0 };
}
} catch (error) {
console.error(`Error al obtener los promedios de feedback para el título: ${title}`, error);
throw error;
}
}

y

insertFeedback.js
import { sql } from '@vercel/postgres';
export async function insertFeedback(title, sentiment, positive, neutral, negative, user) {
console.log(`Insertando feedback para el título: ${title}`);
try {
await sql`
INSERT INTO post_feedback
(title, sentiment, positive, neutral, negative, "user", last_updated)
VALUES
(${title}, ${sentiment}, ${positive}, ${neutral}, ${negative}, ${user}, CURRENT_TIMESTAMP)
ON CONFLICT (title, "user") -- Actualiza esto para que coincida con la nueva restricción única
DO UPDATE SET
sentiment = EXCLUDED.sentiment,
positive = EXCLUDED.positive,
neutral = EXCLUDED.neutral,
negative = EXCLUDED.negative,
last_updated = CURRENT_TIMESTAMP;
`;
console.log(`Feedback insertado con éxito para el título: ${title}`);
} catch (error) {
console.error(`Error al insertar feedback para el título: ${title}`, error);
throw new Error(`Error al insertar feedback: ${error.message}`);
}
}

Paso 4: Configuración de la base de datos

Antes de que puedas comenzar a almacenar y recuperar datos de feedback, necesitas configurar tu base de datos PostgreSQL. Usa la siguiente query SQL para crear la tabla necesaria para almacenar los datos de feedback:

createTable.sql
CREATE TABLE post_feedback (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
sentiment VARCHAR(50) NOT NULL,
positive NUMERIC NOT NULL,
neutral NUMERIC NOT NULL,
negative NUMERIC NOT NULL,
"user" VARCHAR(255) NOT NULL,
last_updated TIMESTAMP NOT NULL,
UNIQUE(title, "user")
);

Esta tabla almacenará cada pieza de feedback junto con el análisis de sentimientos y la marca de tiempo de la última actualización. Asegúrate de que tu base de datos esté en funcionamiento y accesible desde tu backend antes de continuar.

Paso 5: Prueba de concepto

Para probar la integración completa de tu sistema, utiliza Postman para simular solicitudes de cliente a tu backend. A continuación, te proporciono una guía para configurar las solicitudes en Postman:

Configuración en Postman:

  1. POST para analizar feedback:

    • URL: http://localhost:3000/analyzeFeedback (ajusta el puerto según tu configuración).
    • Método: POST
    • Body: raw, JSON
      {
      "title": "Ejemplo de Título",
      "text": "Ejemplo de feedback del usuario",
      "user": "usuario_ejemplo"
      }
  2. POST para obtener puntajes de feedback:

    • URL: http://localhost:3000/getFeedbackScores
    • Método: POST
    • Body: raw, JSON
      {
      "title": "Ejemplo de Título"
      }

Asegúrate de tener tu servidor backend en ejecución antes de enviar las solicitudes desde Postman. Estas solicitudes simularán la interacción entre el frontend y el backend, permitiéndote evaluar la funcionalidad completa de tu aplicación de análisis de feedback.

Después de configurar y ejecutar estas pruebas, deberías poder ver cómo tu aplicación procesa y responde al feedback, cómo se almacena en la base de datos y cómo se recuperan y calculan los promedios de los puntajes de sentimiento. Esto completará tu prueba de concepto, demostrando la funcionalidad y la integración entre todas las partes de tu aplicación.

Y con esto, has completado la integración de Azure Text Analytics en tu aplicación de análisis de feedback. ¡Felicidades por llegar hasta aquí! Si tienes alguna pregunta o comentario, no dudes en dejarlo a continuación.

¡Suscríbete y recibe actualizaciones sobre tecnología, diseño, productividad, programación y mucho más!
0
0