Получение данных из конечной точки, получение координат, затем объединение в вывод

Я никоим образом не являюсь опытным разработчиком (или даже разработчиком!), Но я действительно хотел попробовать написать сценарий, который извлекал данные одной конечной точки, извлекал некоторые данные, отправлял их для геокодирования, а затем сохранял оба исходные (с геокодами) и геокодированные данные в файл. При последующих запросах он будет искать геокодированные данные в файле, чтобы проверить, совпадают ли какие-либо данные, и извлекать данные оттуда, в противном случае снова запускать запрос на выборку.

Я думаю, что это работает – по крайней мере, это работает, насколько я могу судить, но я хотел бы получить обзор (или улучшение, если это ваше дело) 🙂

#!/usr/bin/env node

'use strict';

//
// MARK: - modules
//
const fs = require('fs');
const fetch = require('node-fetch');


//
// MARK: create the variables / constants
//

// get the command line arguments
const args = (function(argv) {

    // remove `node` and `script` name
    argv = argv.slice(2);

    // returned object
    var args = {};
    var argName, argValue;

    // loop through each argument
    argv.forEach(function(arg, index) {

        // seperate argument, for a key/value return
        arg = arg.split('=');

        // retrieve the argument name
        argName = arg[0];

        // remove "--" or "-"
        if (argName.indexOf('-') === 0) {
            argName = argName.slice(
                argName.slice(0, 2).lastIndexOf('-') + 1
            );
        }

        // associate defined value or initialize it to "true" state
        argValue = (arg.length === 2) ?

            // check if argument is valid number
            parseFloat(arg[1]).toString() === arg[1]

        ?
        +arg[1]: arg[1]

        :
        true;

        // finally add the argument to the args set
        args[argName] = argValue;
    });

    return args;
})(process.argv);



// api key checks
const apiKey = function() {

    // api not declared
    if (!args.api) {
        return 1;
    }

    // declared but empty
    if (args.api === true) {
        return 2;
    }

    // all good
    if (args.api && args.api !== true) {
        return args.api;
    }

}(args);



//
// MARK: - check the files and directories we need
//
const req = (() => {

    // where we store things
    const directory = './docs';
    const datavic = `${ directory }/datavic.json`;
    const database = `./database.json`;

    // checks and creation
    if (!fs.existsSync(directory)) {
        fs.mkdirSync(directory);
    }

    // array of items
    return {
        dir: directory,
        api: datavic,
        db: database
    }
})();



//
// MARK: - the script initialiser
//
function init() {
    console.time();
    fetchExposures();
    console.timeEnd();
}



//
// MARK: - 1. fetch the data from datavic
//
function fetchExposures() {

    // api check
    if (apiKey === 1) {
        return console.error(
            '[x] Exiting: API argument not declared!'
        );
    }
    if (apiKey === 2) {
        return console.error(
            '[x] Exiting: API key not provided!'
        );
    }

    // url: Data Victoria API
    const url = new URL(
        'https://discover.data.vic.gov.au/api/3/action/datastore_search'
    );

    // url: parameters
    url.searchParams.append(
        'resource_id', 'afb52611-6061-4a2b-9110-74c920bede77'
    );

    url.searchParams.append(
        'limit', '10000'
    );

    // fetch the data
    fetch(url)
        .then(res => res.json())

    .then(async data => {

        // send off data to be checked against database
        const valid = await validateData(data);

        // send off data to be checked against database
        const database = await checkDatabase(valid);

        // fetch the coordinates
        const coordinates = await fetchCoordinates(database);

        // add the coordinates to api file
        const api = await addCoordinates(data, coordinates);

        // write items to file
        await writeFiles(api, coordinates);
    })

    .catch(err => {
        console.error(`[x] Error: ${ err }`);
    });
}



//
// MARK: - 2. remove the bad characters
//
function validateData(input) {

    const sanitised = JSON.stringify(input, null, 2)
        .replace(/\t/g, '')
        .replace(/\r/g, '')
        .replace(/\n/g, '')
        .replace(/\v/g, '')
        .replace(/\h/g, '');

    return JSON.parse(sanitised);
}



//
// MARK: - 3. check if any of the results are in the database
//
function checkDatabase(input) {

    // 3a. exlude any that have null address or postcode
    const inputNoNull = input.result.records.filter(item => {
        if (!(item.Site_streetaddress === null ||
                item.Site_postcode === null)) {
            return item
        }
    })

    // -- select only certain fields
    .map(item => ({
        Suburb: item.Suburb,
        Site_streetaddress: item.Site_streetaddress,
        Site_state: item.Site_state,
        Site_postcode: item.Site_postcode
    }))

    // -- sort by ID
    .sort((a, b) => {
        return a._id - b._id;
    });

    // -- get the unique data
    const data = [...new Set(
        inputNoNull.map(item1 =>
            item1.Suburb +
            item1.Site_streetaddress +
            item1.Site_state +
            item1.Site_postcode
        )
    )].map(
        item1 => inputNoNull.find(
            item2 =>
            item2.Suburb +
            item2.Site_streetaddress +
            item2.Site_state +
            item2.Site_postcode ==
            item1
        )
    );

    // -- read the database
    const db = fs.readFileSync(req.db);
    const database = db ? JSON.parse(db) : {};

    // append new items
    const mergeJSON = ((file1, file2) =>
        Object.values([...file1, ...file2]
            .reduce((left, right) => {
                const key = `${right.Suburb} ${right.Site_streetaddress} ${right.Site_state} ${right.Site_postcode}`;
                left[key] = left[key] || right;

                return left;
            }, {})
        )
    );

    // return it
    return mergeJSON(database, data);
}



//
// MARK: - 4. loop all items in database, fetch the coordinates
//
async function fetchCoordinates(input) {

    let geocodedSites = [];

    // loop over the locations
    for (let x = 0; x < input.length; x++) {
        try {
            const result = await getGeocode(input[x]);
            geocodedSites.push(result);
        } catch (err) {
            console.log(`[x] Error: ${ err }`);
        }
    }

    // return it
    return geocodedSites;
}



//
// MARK: - 5. re-check the results are in the database
//
function addCoordinates(data, coordinates) {

    const dataapi = data.result.records;

    let arr = [];

    // loop over the locations
    for (let x = 0; x < dataapi.length; x++) {
        coordinates.filter(item2 => {

            if (dataapi[x].Suburb === item2.Suburb &&
                dataapi[x].Site_streetaddress === item2.Site_streetaddress &&
                dataapi[x].Site_state === item2.Site_state &&
                dataapi[x].Site_postcode === item2.Site_postcode
            ) {
                // -- append the coordinates to the items
                const fulldata = {
                    ...dataapi[x],
                    latitude: item2.latitude,
                    longitude: item2.longitude
                }
                arr.push(fulldata);
            }
        });
    }

    // return it
    return arr;
}



//
// MARK: - 6. write files to system
//
function writeFiles(file1, file2) {

    // stringify the inputs
    const stringFile1 = JSON.stringify(file1, undefined, 2);
    const stringFile2 = JSON.stringify(file2, undefined, 2);

    // write them to disk
    fs.writeFileSync(req.api, stringFile1);
    fs.writeFileSync(req.db, stringFile2);
}



//
// MARK: - function: get the coordinates
//
async function getGeocode(site) {

    // build the address
    const query = `${site.Site_streetaddress} ${site.Suburb } ${site.Site_postcode } ${site.Site_state} Australia`;


    // 1. skip any with existing coordinates
    if (site.latitude && site.longitude || site.skip) {
        console.error(`[✔] Skipping: ${ query }`);
        return Promise.resolve(site);
    }

    // 2. fetch only missing items
    console.log(`[⦿] Geocoding: ${ query }`);

    // url: Data Victoria API
    const url = new URL(
        'http://api.positionstack.com/v1/forward'
    );

    // url: parameters
    url.searchParams.append(
        'access_key', apiKey
    );

    url.searchParams.append(
        'region', 'Victoria'
    );

    url.searchParams.append(
        'country', 'AU'
    );

    url.searchParams.append(
        'limit', '1'
    );

    // build the search query
    url.searchParams.append(
        'query', query
    )

    // fetch the data
    return fetch(url)
        .then(res => res.json())
        .then(data => {

            const latitude = data.data[0].latitude;
            const longitude = data.data[0].longitude;
            const label = data.data[0].label;
            const confidence = data.data[0].confidence;

            return {
                ...site,
                confidence,
                latitude,
                longitude,
                label
            }
        })

    .catch(err => {
        console.log(`    [x] Failed: ${ query }`);
        return {...site,
            skip: true
        }
    });
}

//
// MARK: - success: if we got to here
//
init();

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *