Skip to content

Commit

Permalink
Merge branch 'XDgov:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
curt-mitch-census authored Mar 1, 2024
2 parents 3656712 + d7063cb commit 5f7decb
Show file tree
Hide file tree
Showing 40 changed files with 914 additions and 222 deletions.
30 changes: 29 additions & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
# Pull Request

## Description

## Testing
Please provide a brief description of the changes introduced by this pull request.

## Related Issue

- Link to the related issue (if applicable).

## Changes Made

- List the changes made in bullet points.

## Screenshots (if applicable)

Include screenshots or GIFs that demonstrate the changes, if relevant.

## Checklist

- [ ] I have read the CONTRIBUTING.md document.
- [ ] My code follows the project's coding standards.
- [ ] I have added tests that prove my fix is effective or that my feature works.
- [ ] All new and existing tests passed.
- [ ] I have updated the documentation accordingly.
- [ ] The title of my pull request is a short description of the changes.
- [ ] My pull request is assigned to the appropriate milestone.

## Additional Notes

Provide any additional information or context that might be helpful for reviewers.
6 changes: 4 additions & 2 deletions .github/workflows/airtable.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
name: Airtable Import
on: [push]
on:
schedule:
- cron: "0 1 * * *" # every night at 1am
jobs:
airtable:
runs-on: ubuntu-latest
Expand All @@ -17,7 +19,7 @@ jobs:
npm install
- name: create .env file
run: |
echo "AIRTABLE_API_KEY=${{secrets.AIRTABLE_API_KEY}}" >> .env
echo "AIRTABLE_ACCESS_TOKEN=${{secrets.AIRTABLE_ACCESS_TOKEN}}" >> .env
echo "AIRTABLE_BASE_ID=${{secrets.AIRTABLE_BASE_ID}}" >> .env
- name: run Airtable Import
run: |
Expand Down
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ gem "kramdown-parser-gfm"
# This is the default theme for new Jekyll sites. You may change this to anything you like.
gem "minima", "~> 2.0"

# Fix for build issue on Pages
gem "ffi"

# If you want to use GitHub Pages, remove the "gem "jekyll"" above and
# uncomment the line below. To upgrade, run `bundle update github-pages`.
# gem "github-pages", group: :jekyll_plugins
Expand Down
1 change: 1 addition & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ PLATFORMS
x86_64-linux

DEPENDENCIES
ffi
jekyll (~> 4.2.1)
jekyll-feed (~> 0.6)
jekyll-seo-tag
Expand Down
8 changes: 7 additions & 1 deletion _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ analytics:
agency: Commerce # Change this to your agency
subagency: Census # Change this to your agency

collections_dir: collections
collections:
news:
output: true
Expand All @@ -34,7 +35,8 @@ collections:
permalink: /blog/:permalink/
import:
output: true
collections_dir: collections
team_members:
output: true

defaults:
- scope:
Expand All @@ -57,6 +59,10 @@ defaults:
path: "_blog"
values:
layout: blog
- scope:
path: "_team_members"
values:
layout: team-member

# Build and syntax highlighting settings
markdown: kramdown
Expand Down
4 changes: 4 additions & 0 deletions _data/navigation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ primary:
href: /about/
- text: Projects
href: /projects/
- text: Team
href: /team/
- text: Fellowship
href: /apply/

Expand All @@ -12,6 +14,8 @@ secondary:
href: /about/
- text: Projects
href: /projects/
- text: Team
href: /team/
- text: Apply
href: /apply/

Expand Down
13 changes: 13 additions & 0 deletions _includes/components/team-member-card.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<div class="col-12 tablet:grid-col-4 project-cards">
<div class="team-member-card">
<img id="{{ member.image_id }}" alt="Image of {{member.name}}" src="{{ site.baseurl }}{{ member.image_path }}" />
<h4 class="team-member-heading">{{ member.name }}</h4>
<h4 class="team-member-heading">{{ member.job_title }}</h4>
{% if member.portfolio %}
<h4>Project Status: {{member.portfolio}}</h4>
{% endif %}
<div class="member-card-content">
{{ member.blurb }}
</div>
</div>
</div>
5 changes: 2 additions & 3 deletions _layouts/bios.html → _layouts/team-member.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@
<div class="news-content-container">
<header>
<div class="breadcrumb">Team Bios</div>
<h1>{{ page.title }}</h1>
<p class="news-date">{{ page.publish_date | date: '%B %d, %Y' }}</p>
<h1>{{ member.name }}</h1>
</header>
<div class="bios-content">
{{ content }}
{{ member.content }}
</div>
</div>
</div>
Expand Down
331 changes: 235 additions & 96 deletions airtable-cache.json

Large diffs are not rendered by default.

146 changes: 108 additions & 38 deletions airtable.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
const fs = require('fs');
const Airtable = require('airtable');
const marked = require('marked');
const { deepCompare, downloadAndSaveImage } = require('./helpers/utilities');
const { deepCompare, downloadAndSaveImage, writeMarkdownFile, dashCaseString } = require('./helpers/utilities');


// Load environment variables
require('dotenv').config();
marked.use({
breaks: true,
gfm: true,
})

const base = new Airtable({apiKey: process.env.AIRTABLE_API_KEY}).base(process.env.AIRTABLE_BASE_ID);
const base = new Airtable({apiKey: process.env.AIRTABLE_ACCESS_TOKEN}).base(process.env.AIRTABLE_BASE_ID);

const xdContent = {};
const cacheFilePath = './airtable-cache.json';
const newsFilePath = './collections/_import/news.md';
const biosFilePath = './collections/_import/bios.md';


// Image ingestion to check for new images and save them to our repo
const checkAndCleanImages = (newData, cacheData) => {
const promisesArray = Array.from(Object.entries(newData)).map(async (contentArray, index) => {
const contentName = contentArray[0];
const contentData = contentArray[1];
let count = 0;

for (const item of contentData) {
const contentImages = item['Images'];
const contentImages = item['Images'];

if (!contentImages) continue;

Expand All @@ -30,8 +35,8 @@ const checkAndCleanImages = (newData, cacheData) => {

// Construct our new image path from the content type and item name
const name = item["Name"].toLowerCase().replaceAll(' ', '-');
const directory = `assets/img/import/${contentName.toLowerCase().replaceAll(' ', '_')}`;
const directory = `assets/img/import/${contentName.toLowerCase().replaceAll(' ', '_')}`;

// Lookup the same image from our cache
const cachedImage = cacheEquivalent['Images'].find(image => {
return image.imageId === item.ID || image.id === contentImages[0].id;
Expand All @@ -45,9 +50,9 @@ const checkAndCleanImages = (newData, cacheData) => {
// If new, copy image file to our repo then replace with new local path
await downloadAndSaveImage(directory, name, contentImages[0].url)
.then((newLocalImagePath) => {

if (typeof newLocalImagePath !== 'string') return;

// Replace the image url with the local one, so our new/cache comparison lines up
contentImages[0].url = contentImages[0].newLocalPath = newLocalImagePath;
})
Expand All @@ -62,7 +67,7 @@ const checkAndCleanImages = (newData, cacheData) => {

// Fetch our airtable content and generate some markup with it
// Optionally (if newer), write to our cache file with new data
const fetchAirtablePromise = (path) => new Promise((resolve, reject) => {
const fetchAirtablePromise = () => new Promise((resolve, reject) => {

base('xd.gov Content').select({
// Selecting the first 3 records in Grid view:
Expand Down Expand Up @@ -93,31 +98,92 @@ const fetchAirtablePromise = (path) => new Promise((resolve, reject) => {

});

const generateXdMarkup = (content) => {
let newsMarkDown = '---\n' + 'layout: news-landing\n' + 'title: News\n' + '---';
const generateXdMarkdown = (content) => {
const fullMarkdownObj = {};

// Create News page elements
content['News'].forEach(({ Name: name, Blurb: blurb }) => {
newsMarkDown += `\n<div>\n<h3>${name}</h3>\n<p>${blurb}</p>\n</div>`;
})
for (const contentType in content) {
const contentMarkdownArray = [];
let markdown = '';

let biosMarkdown = '---\n' + 'layout: bios\n' + 'title: Bios\n' + '---';

// Create Bios page elements
content['Bio'].forEach(({ Name: name, Blurb: blurb, Images: images }) => {
if ([name, blurb, images].every(item => item !== undefined)) {
biosMarkdown += `\n<div>\n<img id="${images[0].id}" alt="Image of ${name}" src="${images[0].newLocalPath}" /><h3>${name}</h3>\n${marked.parse(blurb)}</div>`;
// Create a landing page for News and Bios only
if (contentType === 'News') {
markdown += `---\n layout: news-landing\n title: News\n---`
} else if (contentType === 'Bio') {
markdown += `---\n layout: bios-landing\n title: Bios\n---`
}
})

// Loop through content types and generate unique markdown for each
content[contentType].map(async (obj) => {
const { Name, Title, Images, Attachments, Blurb, Portfolio } = obj;
let itemMarkdown = ``

switch (contentType) {
// case 'News':
// if ([Name, Blurb].some(item => item === undefined)) return;

// itemMarkdown += `
// \n<div>\n
// <h3>${Name}</h3>\n
// ${marked.parse(Blurb)}
// </div>
// `;
// break;

case 'Bio':
if ([Name, Title, Images, Blurb].some(item => item === undefined)) return;
const directory = '/collections/_team_members';
const bioMarkdownAttrs = `---
name: ${Name}
title: ${Name}
permalink: /team/${dashCaseString(Name)}/
image_id: ${Images[0].id}
image_path: ${Images[0].newLocalPath}
job_title: ${Title}
blurb: ${marked.parse(Blurb)}
---`;

await writeMarkdownFile(directory, Name, bioMarkdownAttrs);
break;

// case 'Project':
// if ([Name, Title, Images, Blurb, Portfolio, Attachments].some(item => item === undefined)) return;

// itemMarkdown += `---\n layout: project\n title: ${Title} Project\n---`

// // TODO: Create unique project file path from title and store it

// itemMarkdown += `
// \n<div>\n
// <img id="${Images[0].id}" alt="Image of ${Name}" src="${Images[0].newLocalPath}" />\n
// <h1>${Title}</h1>\n
// <h4>Author(s): ${Name}</h4>\n
// <h4>Project Status: ${Portfolio}</h4>\n
// <div class="breadcrumb"></div>\n
// ${marked.parse(Blurb)}\n
// <p>Materials: ${Attachments}</p>
// </div>\n
// --End--
// `;
// break;
}

markdown += itemMarkdown;
})

contentMarkdownArray.push(markdown)

fullMarkdownObj[contentType] = contentMarkdownArray;

}

// Keep log for Action debugging
console.log(newsMarkDown, biosMarkdown);
console.log(fullMarkdownObj);

return [newsMarkDown, biosMarkdown];
return fullMarkdownObj;
}

(async () => {
const newAirtableData = await fetchAirtablePromise(cacheFilePath, newsFilePath, biosFilePath);
const newAirtableData = await fetchAirtablePromise();
const cacheData = JSON.parse(await fs.promises.readFile(cacheFilePath));

// Before we compare this data to cache, we need to sanitize the image paths
Expand All @@ -135,7 +201,7 @@ const generateXdMarkup = (content) => {
return;
}

const markup = generateXdMarkup(newAirtableData);
const markdown = generateXdMarkdown(newAirtableData);

// Write to json airtable-cache file
try {
Expand All @@ -148,19 +214,23 @@ const generateXdMarkup = (content) => {

// Write to news file
try {
await fs.promises.writeFile(newsFilePath, markup[0]);
console.log('News markup written successfully to disk');
await fs.promises.writeFile(newsFilePath, markdown['News']);
console.log('News markdown written successfully to disk');
} catch (error) {
console.error('An error has occurred ', error);
return;
}

// Write to bios file
try {
await fs.promises.writeFile(biosFilePath, markup[1]);
console.log('Bios markup written successfully to disk');
} catch (error) {
console.error('An error has occurred ', error);
return;
}

// TODO: Create Bio pages individually

// TODO: Create Project pages individually
// First separate project markdown by separator...
markdown['Project'] = markdown['Project'][0].split('--End--');

// Then Write project pages to disk per entry
markdown['Project'].forEach((project) => {
// Must reference stored file path (see: TODO in generateXdMarkdown() )
// [Code goes here]
})

})();
3 changes: 2 additions & 1 deletion assets/css/_core/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ $color-brand-blue: #112e51;
$color-brand-teal: #13E1BF;

$color-black: #000000;
$color-white: #ffffff;
$color-white: #FFFFFF;
$color-banner: #3E375A;
$color-border: #EEEEEE;

$color-gray-cool-5: #edeff0;

Expand Down
3 changes: 2 additions & 1 deletion assets/css/_includes/_all.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
@import "header";
@import "position";
@import "praise";
@import "project-card";
@import "project-card";
@import "team-member-card";
2 changes: 1 addition & 1 deletion assets/css/_includes/_footer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
}

.usa-footer_nav {

a {
color: $color-white;
font-weight: bold;
Expand Down
Loading

0 comments on commit 5f7decb

Please sign in to comment.