d);
};
logger.write = function (d) {
    console.log(d);
};

//MAIN SCRIPT
async function execute() {
    while (true) {
        perfy.start('skip_reqs');
        try {
            await reloadSkip();
            var resultTime = perfy.end('skip_reqs');
            await misc.sleep(30000 - resultTime.fullMilliseconds);
        } catch (e) {
            console.log("ERROR: " + JSON.stringify(e));
        }
    }
}
execute();

async function reloadSkip() {
    var reqs = [];
    reqs.push(misc.performRequest(requestOptions.getBikes()));

    console.log("SKIP SCOOTERS || LOADING SCOOTERS");
    responses = await Promise.all(reqs);

    localSkips = [];
    responses.forEach(function (currentResponse) {
        localSkips = localSkips.concat(currentResponse.bikes);
    });
    console.log("SKIP SCOOTERS || RECEIVED " + localSkips.length + " SCOOTERS");

    var dbSkips = await sql.runRaw(`SELECT * FROM \`bike_locs\` WHERE \`COMPANY\` = 'SKIP'`, true);
    var results = compareSkip(localSkips, dbSkips[0]);

    var toRemoveQueries = "";
    results.idsToRemove.forEach(function (current) {
        toRemoveQueries += `DELETE FROM \`bike_locs\` WHERE \`id\` = '${current.id}'; `;
    });
    var removePromise = sql.runRaw(toRemoveQueries, false);

    formattedObjects = [];
    results.idsToAdd.forEach(function (current) {
        formattedObjects.push(['Skip', 'US', current.bike_id, 1, "scooter", current.lat, current.lon]);
    });
    var addPromise = sql.addObjects('bike_locs', ['company','region', 'id', 'bikes_available', 'type', 'lat', 'lng'], formattedObjects);

    toUpdateQueries = "";
    results.idsToUpdate.forEach(function (current) {
        toUpdateQueries += `UPDATE \`bike_locs\` SET \`lat\`='${current.lat}', \`lng\`='${current.lon}' WHERE \`id\`='${current.id}'; `;
    });

    var updatePromise = sql.runRaw(toUpdateQueries, false);

    await Promise.all([removePromise, addPromise, updatePromise]);
    console.log(`SKIP SCOOTERS || SUCCESS: ADDED: ${results.idsToAdd.length}, UPDATED: ${results.idsToUpdate.length}, REMOVED: ${results.idsToRemove.length}`);
}

function compareSkip(localSkip, dbSkip) {
    idsToUpdate = [];

    localSkip.forEach(function (currentSkip) {
        if (dbSkip.some(item => item.id == currentSkip.id)) {
            var similarSkip = dbSkip.filter(skip => skip.id == currentSkip.id);
            if (similarSkip[0].lat!= currentSkip.lat || similarSkip[0].lng!= currentSkip.lon) {
                const currentSkipLoc = {
                    'latitude': currentSkip.lat,
                    'longitude': currentSkip.lon
                };

                const similarSkipLoc = {
                    'latitude': similarSkip[0].lat,
                    'longitude': similarSkip[0].lng
                };

                if (!haversine(currentSkipLoc, similarSkipLoc, {
                        threshold: locUpdateThresholdMeters,
                        unit:'meter'
                    })) {
                    idsToUpdate.push(currentSkip);
                }
            }
        }
    });

    var idsToAdd = localSkip.filter(function (currentSkip) {
        return!dbSkip.some(skip => skip.id == currentSkip.bike_id);
    });
    var idsToRemove = dbSkip.filter(function (currentSkip) {
        return!localSkip.some(skip => skip.bike_id == currentSkip.id);
    });

    return {
        idsToUpdate,
        idsToAdd,
        idsToRemove
    };
}It wears you out...trust me.
Recycle it! The oceans are already full of platsic crap.
Just discovered that you can replace /edit at the end of a Google Sheets URL with /pdf and it will auto create a PDF of the document. This is inexplicably satisfying.<reponame>undef-thesis/BT.Web<filename>src/app/shared/map/GoogleAddressParser.ts
interface AddressComponent {
  long_name: string;
  short_name: string;
  types: Array<string>;
}

interface Address {
  latitude?: number;
  longitude?: number;
  country?: string;
  countryCode?: string;
  province?: string;
  city?: string;
  street?: string;
  postalCode?: string;
}

interface Coordinates {
  latitude: number;
  longitude: number;
}

export class GoogleAddressParser {
  private address: Address = {};

  constructor(
    private addressComponents: Array<AddressComponent>,
    private coordinates: Coordinates
  ) {
    this.parseAddress();
  }

  public result(): Address {
    return this.address;
  }

  private parseAddress() {
    if (!Array.isArray(this.addressComponents)) {
      throw Error('Address Components is not an array');
    }

    if (!this.addressComponents.length) {
      throw Error('Address Components is empty');
    }

    let streetNumber: string = '';

    for (let i = 0; i < this.addressComponents.length; i++) {
      const component: AddressComponent = this.addressComponents[i];

      this.address.latitude = this.coordinates.latitude;
      this.address.longitude = this.coordinates.longitude;

      if (this.isStreetNumber(component) && component.long_name!== undefined) {
        streetNumber = component.long_name;
      }

      if (
        this.isStreetName(component) &&
        component.long_name!== 'Unnamed Road'
      ) {
        this.address.street = `${component.long_name} ${streetNumber}`;
      }

      if (this.isCity(component)) {
        this.address.city = component.long_name;
      }

      if (this.isCountry(component)) {
        this.address.country = component.long_name;
      }

      if (this.isProvince(component)) {
        this.address.province = component.long_name;
      }

      if (this.isPostalCode(component)) {
        this.address.postalCode = component.long_name;
      }
    }
  }

  public parseAddressShort() {
    if (!Array.isArray(this.addressComponents)) {
      throw Error('Address Components is not an array');
    }

    if (!this.addressComponents.length) {
      throw Error('Address Components is empty');
    }

    for (let i = 0; i < this.addressComponents.length; i++) {
      const component: AddressComponent = this.addressComponents[i];

      this.address.latitude = this.coordinates.latitude;
      this.address.longitude = this.coordinates.longitude;

      if (this.isCity(component)) {
        this.address.city = component.long_name;
      }

      if (this.isCountry(component)) {
        this.address.country = component.long_name;
        this.address.countryCode = component.short_name;
      }
    }

    return this.address;
  }

  private isStreetNumber(component: AddressComponent): boolean {
    return component.types.includes('street_number');
  }

  private isStreetName(component: AddressComponent): boolean {
    return component.types.includes('route');
  }

  private isCity(component): boolean {
    return component.types.includes('locality');
  }

  private isProvince(component): boolean {
    return component.types.includes('administrative_area_level_1');
  }

  private isCountry(component): boolean {
    return component.types.includes('country');
  }

  private isPost