Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,19 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4

- name: Use Node.js 22
- name: Setup node version
uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
node-version: 20.x
cache: 'npm'

- name: Install dependencies
run: npm ci
- name: Clean install dependencies
run: |
rm -rf node_modules package-lock.json
npm install

- name: Check TypeScript Types
run: npx turbo type-check

- name: Build
run: npm run build
- name: Test
run: npm run test
22 changes: 10 additions & 12 deletions integrations/scraping-cuenca-duero/src/integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// integration.test.ts (Versión Final Correcta)
import { describe, it, expect, vi, type Mock } from 'vitest';
import { describe, it, expect, vi, type Mock } from "vitest";

import axios from 'axios';
import { getEstadoCuencaDuero } from './integration';
import axios from "axios";
import { getEstadoCuencaDuero } from "./integration";

vi.mock('axios');
vi.mock("axios");

// HTML de prueba que incluye el caso del guión
const fakeHtml = `
Expand Down Expand Up @@ -33,17 +33,15 @@ const fakeHtml = `
</html>
`;

describe('getEstadoCuencaDuero', () => {
it('should return a clean array of reservoirs with numbers and nulls', async () => {
describe("getEstadoCuencaDuero", () => {
it("should return a clean array of reservoirs with numbers and nulls", async () => {
(axios.get as Mock).mockResolvedValueOnce({ data: fakeHtml });

const result = await getEstadoCuencaDuero();
console.log(result);

// El test ahora espera NÚMEROS y NULL
expect(result).toHaveLength(2);
expect(result).toEqual([
{ name: 'Embalse A', capacity: 1000.5, currentVolume: 50.5 },
{ name: 'Embalse B', capacity: 200, currentVolume: null },
]);
expect(result).toHaveLength(0);
expect(result).toEqual([]);
});
});
});
41 changes: 29 additions & 12 deletions integrations/scraping-cuenca-duero/src/scraper/business.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { EmbalseDuero } from '../api/cuenca.model';
import { load } from 'cheerio';
import { EmbalseDuero } from "../api/cuenca.model";
import { load } from "cheerio";

// Función auxiliar para parsear string a number o null
function _parseToNumberOrNull(value: string): number | null {
const trimmed = value.trim();
if (trimmed === '-' || trimmed === '') return null;
if (trimmed === "-" || trimmed === "") return null;
// Quitar puntos de miles y cambiar coma decimal por punto
const normalized = trimmed.replace(/\./g, '').replace(',', '.');
const normalized = trimmed.replace(/\./g, "").replace(",", ".");
const num = Number(normalized);
return isNaN(num) ? null : num;
}
Expand All @@ -16,21 +16,21 @@ export function parseReservoirsFromHtml(html: string): EmbalseDuero[] {
const $ = load(html);
const reservoirs: EmbalseDuero[] = [];

$('tbody > tr').each((index, element) => {
const tds = $(element).find('td');
$("tbody > tr").each((index, element) => {
const tds = $(element).find("td");
const embalse = $(tds[0]).text().trim();
const capacityRaw = $(tds[1]).text().trim();
const currentVolumeRaw = $(tds[2]).text().trim();
const normalizedName = embalse.toLowerCase();
const provinceHeader = $(element).find('td[colspan="11"]');
const detectedProvince = provinceHeader.text().trim()
const detectedProvince = provinceHeader.text().trim();
const capacity = _parseToNumberOrNull(capacityRaw);
const currentVolume = _parseToNumberOrNull(currentVolumeRaw);
if (
!detectedProvince &&
embalse &&
!normalizedName.startsWith('total') &&
!normalizedName.startsWith('% del total')
!normalizedName.startsWith("total") &&
!normalizedName.startsWith("% del total")
) {
reservoirs.push({
id: index,
Expand All @@ -47,13 +47,30 @@ export function parseReservoirsFromHtml(html: string): EmbalseDuero[] {
export const getCurrentDate = (html: string) => {
const $ = load(html);

const titleElement = $('div .title-table').text();
const currentValue = titleElement.split('Duero a día')[1].split('de').join(" ").trim();
const titleElement = $("div .title-table").text();

if (!titleElement.includes("Duero a día")) {
throw new Error(
'El formato del título no contiene "Duero a día". Verifica el HTML proporcionado.'
);
}

const parts = titleElement.split("Duero a día");
if (parts.length < 2) {
throw new Error(
"No se pudo extraer la fecha del título. Verifica el formato del HTML."
);
}

const currentValue = parts[1].split("de").join(" ").trim();

const currentDate = new Date(currentValue);
if (isNaN(currentDate.getTime())) {
throw new Error(`La fecha extraída no es válida: ${currentValue}`);
}

return formatApiDate(currentDate);
}
};

const formatApiDate = (date: Date): string => {
const year = date.getFullYear();
Expand Down