Home Manual Reference Source

src/server/events_api.js

import cheerio from "cheerio";
import fetch from "node-fetch";
import apiKey from './apiKey.json';



export default class EventsApi {

    bingmaps = "http://dev.virtualearth.net/REST/v1/Locations";
    bingKey = apiKey["key"];

    /**
     * Bing method to get the city from the current location
     * @param {object} currentLocation - Current location coordinates (latitude and longitude)
     * @returns String with the city name and state abbreviation (or false if no location is provided)
     */
    getCityFromCoordinates = async (currentLocation) => {
        if (currentLocation && currentLocation.latitude && currentLocation.longitude) {
            const locUrl = `${this.bingmaps}/${currentLocation.latitude, currentLocation.longitude}?&key=${this.bingKey}`;
            const response = await fetch(locUrl);
            const json = await response.json();
            const dataAddress = json["resourceSets"][0]["resources"][0]["address"];
            const uf = dataAddress["adminDistrict"];
            const city = dataAddress["locality"];
            const adminDistrict2 = dataAddress["adminDistrict2"];
            const theplace = city ? city : adminDistrict2;
            return `${theplace.replace(" ", "-")}-${uf}`;
        } else {
            return false;
        }
    }

    /**
     * Cria um json com as informações dos eventos
     * @param {string} event - html da página
     * @returns json com os eventos
     */
    createEventsJson = (event) => {
        const $ = cheerio.load(event);
        const eventos = [];

        $('[class*=\"CardLink\"]').each((index, element) => {
            const link = $(element).attr("href").toString();
            const loc = $(element).children().find("[class*=\"EventLocation\"]").text();
            const title = $(element).children().find("[class*=\"EventTitle\"]").text();
            const dates = $(element).children().find("[class*=\"EventDate\"] div div");
            let finalDates;
            const numberOfElements = dates.length;
            if (numberOfElements > 1) {
                const start = $(dates[0]).text();
                const end = $(dates[1]).text();
                finalDates = start + " - " + end;
            } else {
                finalDates = $(dates[0]).text();
            }

            eventos.push({ "Nome": title, "Local": loc, "Date": finalDates, "Link": link });
        });
        return eventos;
    }

    /**
     * Faz o request e pega as informações dos eventos
     * @param {string} local - Formato: cidade-sem-espaco-siglaDoEstado (ex: juiz-de-fora-mg)
     * @returns json com os eventos
     */
    getEventsData = async (local) => {
        if(!local){
            local = "juiz-de-fora-mg";
        }

        const url = `https://www.sympla.com.br/eventos/${local}?s=financeiro`;
        try {
            const response = await fetch(``, {
                method: 'GET',
                headers: {
                    "Host": "www.sympla.com.br",
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0",
                    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
                    "Accept-Language": "pt-BR,pt;q=0.8,en-US;q=0.5,en;q=0.3",
                    "Accept-Encoding": "gzip, deflate, br",
                    "Referer": url,
                    "Upgrade-Insecure-Requests": "1",
                    "Sec-Fetch-Dest": "document",
                    "Sec-Fetch-Mode": "navigate",
                    "Sec-Fetch-Site": "same-origin",
                    "Sec-Fetch-User": "?1",
                    "Connection": "keep-alive"
                }
            })
            const html = await response.text();
            const json = this.createEventsJson(html);
            return json;
        } catch (error) {
            console.log(error);
        }
    }

    /**
     * Makes a request to get the events details
     * @param {object} eventJson - JSON representation of the events
     * @returns event object with more details
     */
    getEventsDetails = async (eventJson) => {
        console.log(eventJson);
        const response = await fetch(eventJson[index]["Link"], {
            method: 'GET',
            headers: {
                'Accept': 'text/html'
            }
        });
        const respTxt = await response.text();
        //console.log(respTxt);
        console.log(eventJson[index]["Link"]);
        const $ = cheerio.load(respTxt);
        
        let loc_details_els = $.find('body section:nth-of-type(3) #event-location + *:not(button) :not(button):not(svg):not(path)');
        final = "";
        loc_details_els.forEach(element => {
            final += element.text() + " - ";
        });
        console.log(final);
        console.log(loc_details_els);



        // const eventsDetails = [];
        // console.log(eventJson.length);
        // for (let index = 0; index < eventJson.length; index++) {
        //     if (index == 0) {
        //         const response = await fetch(eventJson[index]["Link"], {
        //             method: 'GET',
        //             headers: {
        //                 'Accept': 'text/html'
        //             }
        //         });
        //         const respTxt = await response.text();
        //         //console.log(respTxt);
        //         console.log(eventJson[index]["Link"]);

        //         const $ = cheerio.load(respTxt);
                // let loc_details_els = $('body section:nth-of-type(3) #event-location + *:not(button) :not(button):not(svg):not(path)').find();
                // final = "";
                // loc_details_els.forEach(element => {
                //     final += element.text() + " - ";
                // });
                // console.log(final);
                // console.log(loc_details_els);
            //}
        //}
        // eventJson.forEach(async event => {
        //     const response = await fetch(event["Link"]);
        //     console.log(await response.text());
        //     const $ = cheerio.load(await response.text());
        //     let loc_details = $('body section:nth-of-type(3) #event-location + *:not(button) :not(button):not(svg):not(path)').text();
        //     loc_details.replace("<h4>", "");
        //     loc_details.replace("</h4>", "");
        //     loc_details.replace("<p>", "");
        //     loc_details.replace("</p>", "");
        //     eventsDetails.push({
        //         ...event,
        //         "Local_Details": loc_details
        //     });
        // });
        return eventsDetails;
    }

    /**
     * Get the coordinates from the event place
     * @param {string} eventPlace - Place of the event that
     * @returns Coordinates of the event
     */
    getEventCoordinates = async (eventPlace) => {
        const locUrl = `${this.bingmaps}?countryRegion=BR&adminDistrict=MG&locality=Juiz de Fora&addressLine=olegário maciel&key=${this.bingKey}`;
        const response = await fetch(locUrl);
        const json = await response.json();
        const coordinates = json["resourceSets"][0]["resources"][0]["point"]["coordinates"];
        return { latitude: coordinates[0], longitude: coordinates[1] };
    }

    /**
     * Finds the closest events from the current location
     * @param {object} data - JSON representation of all the events
     * @returns List with 5 closest events
     */
    findClosestEvents = (data) => {
        data.forEach(evento => {
            const latEvento = evento.Local.latitude;
            const lonEvento = evento.Local.longitude;
            const distancia = this.calculateDistance(this.initialCoord.lat, this.initialCoord.lon, latEvento, lonEvento);
            
            evento['Distancia em km'] = distancia;
        });
        const closest = data.sort((a, b) => a['Distancia em km'] - b['Distancia em km']);

        return closest.slice(0,5);
    }

    /**
     * Calculates the distance between two events
     * @param {number} lat1 - First event latitude
     * @param {number} lon1 - First event longitude
     * @param {number} lat2 - Second event latitude
     * @param {number} lon2 - Second event longitude
     * @returns distance between events
     */
    calculateDistance(lat1, lon1, lat2, lon2) {
        const raioTerra = 6371; 

        const lat1Rad = this.toRadians(lat1);
        const lon1Rad = this.toRadians(lon1);
        const lat2Rad = this.toRadians(lat2);
        const lon2Rad = this.toRadians(lon2);
    

        const dLon = lon2Rad - lon1Rad;
        const dLat = lat2Rad - lat1Rad;
    
        const a = Math.sin(dLat / 2) ** 2 + Math.cos(lat1Rad) * Math.cos(lat2Rad) * Math.sin(dLon / 2) ** 2;
        const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    
        const distancia = raioTerra * c; //km
    
        return distancia;
    }
    
    /**
     * Convert degrees to radians
     * @param {number} degrees 
     * @returns Number in radians
     */
    toRadians(degrees) {
        return degrees * (Math.PI / 180);
    }

}