Geovation/photos

View on GitHub
scripts/populateDemoData.js

Summary

Maintainability
A
3 hrs
Test Coverage
#!/usr/bin/env node

/**
 * This script just populate the DB with demo data. The ID's are prefixed with "test_" for make it easy to find them and
 * eventually delete them.
 * Run it from this folder.
 */
const admin = require("firebase-admin");
const randomLocation = require("random-location");
const Jimp = require("jimp");
const serviceAccount = require("./serviceAccountKey.json");
const storageBucket = require("../public/config.json").FIREBASE.config.storageBucket;
const path = require('path');
const _ = require('lodash');
const turf = require('@turf/turf');
const Queue = require('queue-promise');

const fs = require('fs');
const os = require('os');
const fsPromises = fs.promises;

const LOCATIONS = {
  "Global": {},
  "London": {
    center: {
      latitude: 51.509865,
      longitude: -0.118092,
    },
    radius: 50 * 1000
  },
};

const argv = require("yargs")
  .usage("Usage: $0 [options]")
  .describe("n", "Number of photos")
  .alias("n", "number")
  .demandOption(["n"])
  .number("n")
  .describe("l", "Location")
  .alias("l", "location")
  .default("l", "Global")
  .choices("l", _.keys(LOCATIONS))
  .describe("s", "Storage")
  .alias("s", "storage")
  .default("s", storageBucket)
  .example("$0 -n 100", `Upload 100 photos to ${storageBucket}`)
  .help("h")
  .alias("h", "help").argv;

const publishedProbability = 1;

const fileNameGeovation = "geovation.jpg";
let bucket;
let db;

async function addMetaDataSync(id, locationName) {
  const published = Math.random() <= publishedProbability ? true : null;

  let location;
  if (LOCATIONS[locationName].center) {
    const rndLocation = randomLocation.randomCirclePoint(LOCATIONS[locationName].center, LOCATIONS[locationName].radius);
    location = new admin.firestore.GeoPoint(
      rndLocation.latitude,
      rndLocation.longitude
    );
  } else {
    const longLat = turf.randomPosition();
    location = new admin.firestore.GeoPoint(longLat[1], longLat[0]);
  }

  const lastYear = new Date();
  lastYear.setMonth(lastYear.getMonth() - 12);
  const now = new Date();
  const rndDate = new Date(lastYear.getTime() + Math.random() * (now.getTime() - lastYear.getTime()));

  const data = {
    updated: admin.firestore.FieldValue.serverTimestamp(),
    location,
    description: `${id} some text here ${locationName}`,
    moderated: published ? rndDate : null,
    published,
    test: true,
    number: Math.round(Math.random()*10)
  };

  const rtn = await db.collection("photos").doc(id).set(data);
  console.log(`Added ${id} with data:`, data);
  return rtn;
}

async function addPhotoSync(id, filePath) {
  // upload it as "original"
  const rtn = await bucket.upload(filePath, { destination: `photos/${id}/original.jpg`, });
  console.log(`Uploaded ${filePath}`);
  return rtn;
}

async function run(num, storage, location) {
  console.log(`Will upload ${num} photos to ${storage} in ${location}`);

  admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
    storageBucket: storage,
  });
  bucket = admin.storage().bucket();
  db = admin.firestore();

  const image = await Jimp.read(path.join( path.dirname(__filename), fileNameGeovation));
  const fontBlack = await Jimp.loadFont(Jimp.FONT_SANS_64_BLACK);
  const fontWhite = await Jimp.loadFont(Jimp.FONT_SANS_64_WHITE);

  const maxHeight = image.getHeight();
  const maxWidth = image.getWidth();
  const digits = 12;

  const queue = new Queue({
    concurrent: 10,
    interval: 10
  });
  
  queue.on("end", () => console.log("The end"));
  queue.on("resolve", data => console.log(`Resolved ${data}`));
  queue.on("reject", error => console.error(error));
  const tempDir = os.tmpdir();

  for (let i = 0; i < num; i++) {
    console.log(`Enqueuing photo ${i + 1}/${num}`);

    const id = `test_${(Math.floor(Math.random() * 10 ** digits) + "").padStart(
      digits,
      "0"
    )}`;
    const tmpFilePath = path.join(tempDir, `${id}.jpg`);

    queue.enqueue(() => uploadPhoto(image, tmpFilePath, id, location, i, num, fontWhite, fontBlack, maxWidth, maxHeight)); 
  }
  // await Promise.all(queue);
  while (queue.shouldRun) {
    const data = await queue.dequeue();
  }
}

async function uploadPhoto(image, tmpFilePath, id, location, i, num, fontWhite, fontBlack, maxWidth, maxHeight) {
        // watermark it with id
    const text = {
      text: id,
      alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER,
      alignmentY: Jimp.VERTICAL_ALIGN_BOTTOM,
    };
    const newImage = image.clone();
    newImage.print(fontWhite, 5, 0, text, maxWidth, maxHeight);
    newImage.print(fontBlack, 0, 0, text, maxWidth, maxHeight);
  
    await newImage.writeAsync(tmpFilePath);
    await addMetaDataSync(id, location);
    await addPhotoSync(id, tmpFilePath);
    await fsPromises.unlink(tmpFilePath);
    console.log(`Completed photo ${i + 1}/${num}`);
};

run(argv.number, argv.storage, argv.location)
  .then(() => {
    console.log("The End ;)");
    process.exit(0);
  })
  .catch((e) => {
    console.error(e);
    process.exit(1);
  });