This Readme file documents the basics of site structure and functioning.
You will need the following things properly installed on your computer.
With this repository cloned onto your machine, you can browse a local version of the site. In your local development environment, start the server by typing hugo serve --environment development into the terminal.
hugo servestarts the server - you can then browse the site at http://localhost:1313/EH-dataportal--environment developmentspecifies that it will serve the site for the development environment, using content from/config/development/config.toml
You can browse the site at http://localhost:1313/EH-dataportal.
To build the source code, simply enter the command hugo. This assembles the site’s files, building to /docs (this build-to destination can be specified in config.toml).
More help is available by typing hugo --help in the terminal. You can also view help online at Hugo's CLI docs page.
Our internal workflows are to begin work by:
- Branching off of production
- Giving the branch a unique name. We name branches: hotfix-[FIXNAME], content-[CONTENTNAME], or feature-[PROJECTNAME].
- Keeping branch work focused on discrete, unique tasks
After committing, working branches can be merged into development for testing then merged into production for deployment.
A run-down of main branches, actions, and purposes are:
| Deploy to | EH-dataportal branch: |
EHDP-data branch: |
Action on merge: | Used for: |
|---|---|---|---|---|
| Production servers | production |
production |
Builds to builds/prod-prod |
Live site |
| 307 (internal) | build-to-dev-stage |
staging |
Builds to builds/dev-stage |
Demoing data & content |
On merge, these branches are automatically built and served to other branches using Github Actions (triggerd by a merged pull request). (Note that this requires a workflow YAML file in both production and the build branch, e.g. build-to-dev-stage.)
When changes are merged into production, a Github Action bulids and commits the site files to builds/prod-prod. This build is configured using a GitHub actions workflow file located in .github/workflows/. We deploy this branch to our server to serve up the production site.
Current workflows:
| Workflow: | EH-dataportal branch: |
EHDP-data branch: |
Action on merge: | In use? |
|---|---|---|---|---|
hugo-build-to-prod-prod.yml |
production |
production |
Builds to builds/prod-prod |
Yes |
hugo-build-to-dev-stage.yml |
build-to-dev-stage |
staging |
Builds to builds/dev-stage |
Yes |
hugo-build-to-dev-prod.yml |
development |
production |
Builds to builds/dev-prod |
No |
hugo-build-any-branch.yml |
Any (need to specify) | Any (need to specify) | By default, builds to builds/[specified-branch] |
Yes |
Note:
GitHub Actions and the deployment pipeline are set up to convert all end-of-line characters to Unix style (LF). This is configured in the workflow YAML files (step set git EOL). This is nice for consistency and for avoiding git flagging hundreds of inconsequential changes, but it's actually important for subresource integrity calculations.
In addition to automated builds, these actions are triggered: The site runs a CodeQL analysis on merges/builds, and is set up to use Github's Depandabot to review dependencies for vulnerabilities.
The /config folder includes subfolders with environment-specific configuration. Specifically, there are different configuration files for different combinations of development or production site code, and staging or production data. You serve or build the site by specifying the environment (e.g., hugo serve --environment production or hugo serve --environment dev_stage). This merges the contents of that environment's config file (in /config/ENVIRONMENT/config.toml') with /config/_default/config.toml. You may find it useful to create aliases for these functions (in Powershell, or Bash).
Some key uses of environment-specific variables in the config are:
- Setting the
baseURL - Setting the variable
data_branch, which tells the site to read data fromstagingorproductionbranches of EHDP-data.
To deploy to a new environment, update the baseURL in config.toml. Update the path, if necessary, in the environment-specific config.toml file.
Current environments:
| Environment: | Build type: | Data branch: | Purpose | Notes |
|---|---|---|---|---|
development |
Development | production |
Preview site changes | Identical to dev_prod |
dev_prod |
Development | production |
Preview site changes | |
dev_stage |
Development | staging |
Preview combined site & data changes | |
production |
Production | production |
Deploy to production servers | Identical to prod_prod |
prod_prod |
Production | production |
Deploy to production servers | |
prod_stage |
Production | staging |
Preview data changes | |
local_prod |
Development | production |
Preview site changes | Uses locally hosted data repo |
local_stage |
Development | staging |
Preview combined site & data changes | Uses locally hosted data repo |
Most of the data used by the site is stored in the separate EHDP-data repository. This setup allows us to update the site's data without needing to re-build the entire site. Look there for descriptions of the data files, and for the code used to generate the them.
Note that any file required to build the site should remain with the source code, but anything required only for display can be stored in the remote data repo, EHDP-data.
Generally, Hugo works by combining content (in markdown, located in /content) with templates (located in the /themes) - you'll notice that these two directories have identical structures, because Hugo combines content in /content/data-stories, for example, with templates in /themes/layouts/data-stories.
- A file named
_index.mdwill getsection.htmllayout - A file named
index.mdwill, by default, receive thesingle.htmllayout - And, a file with another name,
name.md, will receivesingle.htmllayout - A file with
layout: customin the frontmatter will get a layout calledcustom.html(all in the corresponding layouts folder).
Generally, a page constructed with index.md will be the final item in that directory structure that Hugo builds; _index.md is required for a content item to have child pages.
Templates can include Hugo code (which you can identify by {{ curly brackets }}. When Hugo serves or builds the site, it runs code, inserts content into the HTML, and produces static HTML pages. Any template is actually an assembly of other templates, including partials, which are re-usable template blocks.
- First create the markdown file with the terminal command
hugo new data-stories/TITLE/index.md. - Add a banner image to the same folder.
- Copy, paste, and edit the frontmatter from pre-existing data stories. You will need these fields (as well as others):
title,date, anddraftseo_titleandseo_descriptioncategories: this determines what Key Topics this data story is associated withkeywordsto support search functionsimageto associate with the image filenamemenu.main.identifierto highlight the correct button in the nav menu
- Write the data story in markdown. You can use Datawrapper and Vega shortcodes (see additional information on shortcodes, below)
- To publish, set
draft: false. The data story will be a part of the site when you serve or build it, and it will appear on the related pages if it's been tagged properly viacategories.
To create a new Key Topic:
- Create a markdown file with
hugo new key-topics/TITLE/index.md - Copy, paste, and edit the frontmatter from pre-existing Key Topic files. In particular, you will need the following frontmatter fields:
keyTopic(for example,keyTopic: airquality). This associates this Key Topic with any other content that hasairqualityas one of itscategories.layout: singleto give it the correct template
To create a child page, create a subfolder within the keytopic - for example, see the folder structure under /content/key-topics/airquality.
The data explorer includes markdown files for each topic (previously called subtopics). The associated indicators are specified in an array (with headers) in the frontmatter. Extensive Javascript powers the rest of the functions, with the javascript for each display (summary, map, trend, and links) in discrete files.
To publish a new neighborhood report, you'd need:
- JSON files for each neighborhood stored in
EHDP-data/neighborhood-reports/reports - YML stored in
/data/globals - Preview chart images stored in
EHDP-data/neighborhood-reports/images - Indicator data files stored in
EHDP-data/neighborhood-reports/data
Related content and related data are managed through frontmatter fields; partials that ingest related content or related data are set to default to things that match on Key Topic / categories, if those frontmatter fieldsdo not exist.
Templates are stored in themes/dohmh/layouts, in the folder for their corresponding content area. A template includes:
- The base template, in
layouts/_default/baseof.html, and components referenced in that file (likeheader.html,footer.html, etc) - The page template itself (e.g.,
layouts/data-stories/single.html) - Partials: re-usable template blocks are are stored in
/partials. These can be called from any other templates.
Shortcodes can be called from content files (markdown). Essentially, the shortcode is called and arguments are passed into it and inserted into the corresponding HTML code in layouts/shortcodes. There are shortcodes for a few different visualization embeds for Data Stories, and more can be written as needed.
Data accessible throughout the site can be stored in the data folder. This can be referenced by site templates. For example, featured_data.yml is referenced by partials/featured-data.html and displayed on the Home Page and the Data Explorer landing page. You can update "featured datasets" by updating this file.
Other content in data are SEO variables and Neighborhood Reports specifications.
We use Hugo's integrity function; this calculates a "message digest" value for a resource, allowing us to include it in the integrity property of <script> and <link> tags, which usually load JavaScript and CSS files, respectively. Hugo also adds a hash value to the resource's built filename, and tells the pages to fetch the files with the hashed names. (We use a partial template to modify the way this filename hash is calculated, because Hugo's default is absurdly long.) This is a way of improving security by ensuring the integrity of the JS and CSS files.
If all of these resources break on the production site, it may be because the server's certificate is expired. If some - but not all - of these break on the production site, Unix vs. Windows end-of-line characters may be to blame. See Automated actions above for more info.
Dependencies (The JS libraries the site uses: D3, Arquero, Vega-Lite, Accessible Autocomplete, etc) are served by the site rather than linked from CDNs. When you run npm install to configure your local repo, they are stored in /node_modules. When you run a build (hugo or via merging to development or production, per Github Actions), Hugo grabs these dependencies, applies the Integrity hash (see above), and references these 'local' versions.
We use Hugo to automatically resize images. Where you put the source path of an image, there's additional code - Hugo resizes the image, generates a different size (puts it in the /resources/_gen/images), and automatically points to the resized image. Missing images are frequent causes of build failures.
We currently use a variety of environment-specific code to produce:
- Different analytics for staging and production
data-index.html, on site build, assembles a json file of topics and indicators. It ranges over DE topic frontmatter and produces a cross-reference of topics and indicators (file). This is used on data-index.html as well as on the Neighborhood Reports: when an indicator is clicked, it runs getURL() to find the parent topic for the indicator, generates a URL, and produces the Get The Dataset button.
The repo includes some files to integrate with CloudCannon, an online CMS provider. Specifically:
cloudcannon.config.yamlsets up how the site appears in the CloudCannon CMS, what the editor reveals, what shortcodes are easily accessible, etc..cloudcannon/prebuildis code that runs immediately before CloudCannon builds a site branch..cloudcannon/postbuildis code that runs after CloudCannon builds a site branch..cloudcannon/schemasinclude frontmatter templates for when CloudCannon works with frontmatter.
Resources used in a build (like a Neighborhood Report json spec, for example) may be cached by whatever machine is running the build. Updates to resources might not be reflected in a build if Hugo is using cached versions. In config.toml, setting the cache to have a maxAge = 0 effectively turns it off, ensuring that Hugo will use the original, non-cached resources. Caching in Hugo is explained more here.