Skip to content


Switch branches/tags

Latest commit


Git stats


Failed to load latest commit information.

Build Status

This repo hosts the code for which also provides an embeddable map to other projects

New volunteer?

Join the slack!

  • new dev? please look at issues and comment on something to grab it!
  • new data moderator? Join the slack and come to #data!
  • not either? Say hello on Slack and check out open roles for our team here:

Current setup

  • The website reads from a Google sheet, generates a json blob, which is rendered by a Node server.

Reading our data to build your own frontend

  • Our curated data file updates every five minutes and can be read from [US]. Each country that we serve has its own country code, i.e. data-ca.json for Canada, data-fr.json for France, etc.
  • Similarly, we also offer CSV output at , with similar country-code modifications for each country.
  • If reading in data and producing web output sounds like a lot to do, please read on:

How can I embed your map on my site?

We'd love for anyone to embed our map to help fight the battle against coronavirus. Please go here for detailed instructions:

Adding Countries and Locales

We use virtual paths via routing rules on the server to view country-specific datasets.

For example, /us/give.html will filter the map to the United States and /fr/give.html will filter to France.

To view translated version of a country you can pass in a locale parameter. /us/give.html?locale=fr-FR will show the map of the United States in French and /fr/give.html?locale=en-US will show the map of France in English.

To add a new country, you need to set a few variables.

  1. Get the country code from
  2. Add the country code and a link to the donation form to viewHelpers/getDonationFormUrl.js. The form should include translations for all official languages in that country.
  3. Add the translated strings for all official languages in that country in i18n.js. As a starting point, it is OK to launch a new language using an international variant. e.g. you can launch Canada with en translations and fr translations, they do not need to be localized to en-CA and fr-CA.
  4. Update the list of languages and countries in countries.js and locales.js and ensure that they propagate correctly to the language and country dropdowns. In countries.js you should also add the name of the string for that country's administrative region. e.g. US = "State", CA "Province", FR = "Department", copy for who they should direct large donations to, and copy for who they should contact if there are no donation sites near them.

Data inflow, storage & moderation


Currently information about PPE needs is contributed by members of the public through a Google Form. We have at least one form per country; for CA and CH we have one per language. See the International Forms and Data section of the Wiki for details.


Currently the data about PPE needs is stored in Google Sheets spreadsheets (one per country). Data from the forms (described above) automatically feeds into these sheets. See International Forms and Data section of the Wiki for details.


Moderation is done by volunteers in accordance with the guidance laid out in the findthemasks wiki.

Directory structure

  • /public - The client-side code for the website.
  • /functions - The cloud function used to generate data.json. Not needed for frontend work.


Basic architecture

Firebase is used to pull data from our moderated datastore and then generate a data.json. There is a production environment findthemasks and a dev environment findthemasks-dev.

The setup uses cloud functions to provide http endpoints, cloud-storage to keep the generated results, and the firebase realtime database (NOT firestore) to cache oauth tokens.

Adding an oauth token requires hitting the /authgoogleapi?sheetid=longstring on the cloud-function endpoint and granting an OAuth token for a user that has access to the sheet.

How to deploy

  • Install the firebase cli for your platform.
  • Do once
    • firebase login
    • firebase use --add findthemasks-dev
    • firebase use --add findthemasks
    • cd functions; npm install # Note you need node v8 or higher. Look a nvm
  • Switch deployment envrionments firebase use [findthemasks or findthemasks-dev]
  • Deploy the cloud function. cd functions; npm run deploy

How to set config variables.

Secrets and configs not checked into github are specified via cloud function configs.

To set a config:

firebase functions:config:set findthemasks.geocode_key="some_client_id"

In code, this can be retrieved via:


In get all configs:

firebase functions:config:get

The namespace can be anything. Add new configs to the findthemasks namespace.

How to locally develop

Firebase comes with a local emulation environment that lets you live develop against localhost. Since we are using firebase configs, first we have to snag the configs from the environment. Do that with:

firebase functions:config:get > .runtimeconfig.json

Next generate a new Firebase Admin SDK private key here:

And save it to service_key.json

Then start up the emulator. Note this will talk to the production firebase database (likely okay as the firebase database is just storing oauth tokens).

export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service_key.json
firebase emulators:start --only functions

This will create localhost versions of everything. Cloud functions should be on http://localhost:5001 and console.log() messages will stream to the terminal.

There is also a "shell"

firebase functions:shell

that can be used, but running the emulator and hitting with a web browser is often easier in our simple case.

Scripts that edit the spreadsheet (Apps Script)

There are currently 2 scripts that automatically update the spreadsheet, and one that backs it up:

  • fillInGeocodes: fills in the lat/lng column based on the address in the "address" column. (Note that the "address" column is defined as the column that has "address" in row 2.) Uses Google Maps geocoding API. Currently runs once/minute.
  • createStandardAddress: fills in the "address" column based on the data in the "orig_address", "city", and "state" columns. Uses Google Maps geocoding API. Currently runs once/minute.
  • backupSheet: makes a timestamped copy of the sheet. Currently runs once every 2 hours.

There are a few important things to know about these scripts:

  • They are visible by navigating to tools > script editor from the Google Sheet.
  • They can be run by anyone who has edit permission to the Google Sheet.
  • Triggers (automation) can be set up by navigating to Edit > Edit current project's triggers.
  • The dev-owner of each sheet should set up a trigger for each of the 3 scripts. (US spreadsheet dev owner is @susanashlock's gmail).
  • The scripts are run using quota of the user the user that runs them.
  • Each Gmail user has a fixed amount of geocoding quota per day. This quota is somewhere around 250 calls per day. @susanashlock's account has 'special' quota. We're not sure exactly what it is, but is sufficient to support about 1000 calls per day.

Frontend development

To work on the frontend, you'll need to run a development server.

Install npm

On mac, brew install npm

Install dependencies

npm install

You'll need to do this once when first setting up, and periodically as dependencies change. The dev server script (see next section) will crap out with an error message when a new dependency is added - this is your cue to run 'npm install' again.

Run the dev server

npm run dev

This should start a server running on http://localhost:3000



All the code for







No releases published


No packages published