Commit 148069f4 authored by Nitin Jadhav's avatar Nitin Jadhav
Browse files

Merge branch 'master' into react-migration

parents 587dedfd 6a7972dd
Pipeline #155673 passed with stages
in 7 minutes and 37 seconds
node_modules
.DS_Store
Thumbs.db
db.json
*.log
public
functions
src/site/_includes/css
src/site/_includes/js
.env
yarn-error.log
dist
build
temp
package.variables.scss
src/site/_data/emblJobs.json
\ No newline at end of file
{
"indent_size": 2,
"indent_char": " ",
"css": {
"indent_size": 2
},
"wrap_line_length": 10000,
"wrap_attributes": false
}
# embl-jobs-pages
Creates pages for `embl.org/jobs/position/{name}`
- Made with `vf-eleventy`
- Pulls jobs from contentHub via `https://www.embl.org/api/v1/jobs?_format=json&source=contenthub`
- Redirects from email alerts using format of https://www.embl.org/jobs/alerts/alert-email/{nPostingTargetID}(3656)
- Redirects from EMBL.de are mostly handled by the script at https://sourcecode.embl.de/webdev/website/nps-legacy/-/blob/master/js/ext/src/embl_jobs_redirect.js (as of Feb 2021)
## Developing
1. You'll need to [install npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
1. If you don't have `yarn`, install it
- https://yarnpkg.com/lang/en/docs/install/
1. Install all the things
- `yarn install`
1. Generate the site in `/build`
- `gulp dev` renders and serves
- `gulp build` build static assets
### Local setup using docker
1. Run `docker-compose up` this will generate image & build the application locally
2. To access in browser simple access `http://localhost:8000/jobs/`
### Deployment
- Latest commit to `master` goes to [wwwdev.embl.org/jobs](https://wwwdev.embl.org/jobs/)
- Latest tag goes to [www.embl.org/jobs](https://www.embl.org/people/)
\ No newline at end of file
gzip on;
gzip_http_version 1.1;
gzip_vary on;
gzip_comp_level 6;
gzip_proxied any;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript text/x-js application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon;
gzip_buffers 16 32k;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
server {
listen 8080;
server_name localhost;
absolute_redirect off;
root /usr/share/nginx/html;
index index.html;
location / {
expires 5m;
add_header Cache-Control "public";
try_files $uri /index.html;
}
error_page 404 /404/index.html;
location = /404/index.html {
root /usr/share/nginx/html/jobs/info/error-pages;
internal;
}
error_page 403 /403/index.html;
location = /403/index.html {
root /usr/share/nginx/html/jobs/info/error-pages;
internal;
}
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /500/index.html;
location = /500/index.html {
root /usr/share/nginx/html/jobs/info/error-pages;
}
}
const { DateTime } = require('luxon');
const _ = require('lodash');
const moment = require('moment');
const Path = require('path');
module.exports = function(config) {
// Add in tags, filters useful for Visual Framework installs
// (fractal's render tag, codeblock, markdown, etc)
// and common configuration
const vfEleventyExtension = require("@visual-framework/vf-extensions\/11ty");
config.addPlugin(vfEleventyExtension);
// BroswerSync options
config.setBrowserSyncConfig({ open: true });
// Filters
// https://www.11ty.io/docs/filters/
// -----
// {{ "myContent" | sampleFilter }}
// config.addFilter("sampleFilter", function(value) {
// return 'ddd' + value;
// });
// Add any utiliuty filters
config.addFilter("dateDisplay", (dateObj, format = "LLL d, y") => {
return DateTime.fromJSDate(dateObj, {
zone: "utc"
}).toFormat(format);
});
// clean up the dates received from EDR
// Thu, 02/13/2020 - 12:00
config.addFilter("parseDate", function(value) {
value = value || '';
value = value.split(', ')[1]; // remove text day of week
value = value.split(' - ')[0]; // remove hour stamp
// stringToDate("17/9/2014","dd/MM/yyyy","/");
// stringToDate("9/17/2014","mm/dd/yyyy","/")
// stringToDate("9-17-2014","mm-dd-yyyy","-")
function stringToDate(_date,_format,_delimiter) {
var formatLowerCase=_format.toLowerCase();
var formatItems=formatLowerCase.split(_delimiter);
var dateItems=_date.split(_delimiter);
var monthIndex=formatItems.indexOf("mm");
var dayIndex=formatItems.indexOf("dd");
var yearIndex=formatItems.indexOf("yyyy");
var month=parseInt(dateItems[monthIndex]);
month-=1;
var formatedDate = new Date(dateItems[yearIndex],month,dateItems[dayIndex]);
return formatedDate;
}
return stringToDate(value,'mm/dd/yyyy','/');
});
// convert to a nice date
// expects format of:
// Tue Feb 25 2020 00:00:00 GMT+0100 (Central European Standard Time)
// returns:
// 26 February 2020
config.addFilter("prettyDate", function(value) {
value = value || '';
var month = new Array();
month[0] = "January";
month[1] = "February";
month[2] = "March";
month[3] = "April";
month[4] = "May";
month[5] = "June";
month[6] = "July";
month[7] = "August";
month[8] = "September";
month[9] = "October";
month[10] = "November";
month[11] = "December";
var d = new Date(Date.parse(value)),
month = '' + (month[d.getMonth()]),
day = '' + d.getDate(),
year = d.getFullYear();
return day + " " + month + " " + year;
});
config.addFilter("makeLowercase", function(value) {
value = value || '';
return value.toLowerCase();
});
config.addFilter("makeUppercase", function(value) {
value = value || '';
return value.toUpperCase();
});
config.addFilter("spaceToDashes", function(value) {
value = value || '';
return value.replace(/\s+/g, '-');
});
// push a value into an array
// {% set emblEbiJobs = emblEbiJobs | push(job) %}
config.addFilter("push", function(object, value) {
value = value || '';
let length = object.length + 1;
object.push(value);
return object;
});
// receive format of `2015-10-08T15:30:00` and make it pretty with Moment.js
config.addFilter("dateMoment", (time, format = "D MMMM YYYY, HH:mm") => {
// console.log('time',time)
time = time.toString().replace('T',' '); // no need for T in timestamp
time = time || new Date();
if (format == 'unix') {
return moment(time).format('X'); // time in seconds
} else {
return moment(time).format(format);
}
});
// do some common cleanup on job title
config.addFilter("jobTitleClean", (input) => {
input = input.replace('&','&');
return input;
});
// Shortcodes
// https://www.11ty.io/docs/shortcodes/
// -----
// nunjucks
// {% sampleShortcode "firstName", "lastName" %}
// handlebars
// {{ sampleShortcode "firstName" "lastName" }}
// config.addShortcode("sampleShortcode", function(firstName, lastName) {
// return 'hi ' + firstName + lastName;
// });
// If you want to minify html output
// config.addTransform("htmlmin", require("./node_modules/\@visual-framework/vf-eleventy--extensions/utils/minify-html.js"));
// Add any custom tags
// config.addNunjucksTag("uppercase", function(nunjucksEngine) {
// return new function() {
// this.tags = ["uppercase"];
//
// this.parse = function(parser, nodes, lexer) {
// var tok = parser.nextToken();
//
// var args = parser.parseSignature(null, true);
// parser.advanceAfterBlockEnd(tok.value);
//
// return new nodes.CallExtensionAsync(this, "run", args);
// };
//
// this.run = function(context, myStringArg, callback) {
// let ret = new nunjucksEngine.runtime.SafeString(
// myStringArg.toUpperCase()
// );
// callback(null, ret);
// };
// }();
// });
// pass some assets right through
config.addPassthroughCopy("./src/site/images");
config.addPassthroughCopy("./src/site/apply/assets");
return {
dir: {
input: "src/site",
output: "build",
data: "_data"
},
templateFormats : [
"njk", "md" // note that .md files will also be parsed with njk processor
],
htmlTemplateEngine : ["njk", "md"],
markdownTemplateEngine : "njk",
passthroughFileCopy: true,
pathPrefix: "/jobs/" // if your site is deployed to a sub-url, otherwise comment out
};
};
const path = require('path');
const gulp = require('gulp');
// Pull in optional configuration from the package.json file, a la:
const {componentPath, componentDirectories, buildDestionation} = require('@visual-framework/vf-config');
// Tasks to build/run vf-core component system
require('@visual-framework/vf-core/gulp-tasks/_gulp_rollup.js')(gulp, path, componentPath, componentDirectories, buildDestionation);
require('@visual-framework/vf-extensions/gulp-tasks/_gulp_rollup.js')(gulp, path, componentPath, componentDirectories, buildDestionation);
// require('./node_modules/\@visual-framework/vf-extensions/gulp-tasks/gulp-build-search-index.js')(gulp, path, buildDestionation);
// Watch folders for changes
gulp.task('watch', function() {
// left for convenience for local watch additions
gulp.watch(['./build/css/styles.css'], gulp.series('eleventy:reload'));
// gulp.watch(['./build/search/index.html'], gulp.parallel('vf-build-search-index'));
});
// fetch embl jobs data and write
// note: we don't commit this json to the repository and instead do this at build/run
// https://www.embl.org/api/v1/jobs?_format=json&source=contenthub
const _ = require('lodash');
var usedNames = [];
// filter and pre-process EDR data
function filterFetchedData(data) {
data = JSON.parse(data.toString());
console.log('Person fetching script needs to be updated for jobs data');
console.log('Jobs fetched from EDR: ' + data.length)
// remove persons without an EMBL ID
// _.remove(data, function(n) {
// return typeof n.emblId == 'undefined';
// });
// remove alumni
// _.remove(data, function(n) {
// return n.personEEGroup == 'Unemployed';
// });
// _.forEach(data, function(n) {
// drop hmtl from start and end dates
// _.forEach(data, function(n) {
// // <time datetime="2020-01-15T12:00:00Z" class="datetime">Wed, 01/15/2020 - 12:00</time>
// n.field_jobs_publish_date = n.field_jobs_publish_date.split('datetime="')[1];
// n.field_jobs_publish_date = n.field_jobs_publish_date.split('" class="datetime"')[0];
// n.field_jobs_expiration = n.field_jobs_expiration.split('datetime="')[1];
// n.field_jobs_expiration = n.field_jobs_expiration.split('" class="datetime"')[0];
// });
// Sort so most recent jobs come first
data = _.sortBy(data, function(n) {
return n.field_jobs_publish_date;
});
data = data.reverse(); // reverse so newest jobs come first
// track duplicate names by adding a `urlSuffix` attribute
// _.forEach(data, function(n) {
// if (n.displayName) {
// var tempName = n.displayName.replace(/\s+/g, '-').toLowerCase();
// var isNameUsed = usedNames.includes(tempName);
// if (isNameUsed === false) {
// usedNames.push(tempName);
// } else {
// // this method only handles the first duplicate, if we have two persons we'll need more logic
// n.urlSuffix = '-1';
// console.log('Duplicate name found for ' + tempName + '; suffix will be added.');
// }
// }
// });
console.log('After filtering, jobs to have record made: ' + data.length)
return data;
}
const request = require('request');
const ora = require('ora');
const fs = require('fs');
gulp.task('fetch-jobs', function(done) {
// console.log('Fetching EMBL people data from EMBL EDR.');
// console.log('People data will not fetch if request is made outside of EMBL or EMBL-EBI network.');
const spinner = ora('Downloading jobs data from contentHub\n').start();
let totalDataReceived = 0;
request({
url: 'https://www.embl.org/api/v1/jobs?_format=json&source=contenthub',
headers: {
'User-Agent': 'request'
}
},
function optionalCallback(err, httpResponse, body) {
if (err) {
console.error(err);
}
spinner.stop();
console.log('Downloaded '+ totalDataReceived + ' KB of jobs data from the contentHub.');
console.warn('It may take a 2-3 minutes to prepare the data before Eleventy renders the pages.');
let data = filterFetchedData(httpResponse.body);
let buffer = new Uint8Array(Buffer.from(JSON.stringify(data)));
fs.writeFile("src/site/_data/emblJobs.json", buffer, function(err) {
if(err) {return console.log(err);}
// console.log("The file was saved!");
done();
});
}
)
.on('data', function(data) {
// decompressed data as it is received
})
.on('response', function(response) {
// unmodified http.IncomingMessage object
response.on('data', function(data) {
// compressed data as it is received
totalDataReceived = totalDataReceived + data.length;
spinner.color = 'yellow';
spinner.text = 'Downloading jobs data; ' + totalDataReceived/1000 + ' KB received\n';
})
});
// .pipe(filterFetchedData())
// .pipe(source('emblJobs.json'))
// .pipe(gulp.dest('./src/site/_data'));
});
// Let's build this sucker.
gulp.task('build', gulp.series(
'vf-clean',
gulp.parallel('vf-css','vf-scripts','vf-component-assets'),
'vf-css:production', //optimise, prefix css
'fetch-jobs',
'fractal:build',
'fractal',
'eleventy:init',
'eleventy:build',
// 'vf-build-search-index',
'manual-exit'
));
// Build and watch things during dev
gulp.task('dev', gulp.series(
'vf-clean',
gulp.parallel('vf-css','vf-scripts','vf-component-assets'),
'fetch-jobs',
'fractal:development',
'fractal',
'eleventy:init',
'eleventy:develop',
gulp.parallel('watch','vf-watch')
));
apiVersion: v1
kind: Service
metadata:
name: embl-jobs-pages-service
spec:
type: NodePort
ports:
- protocol: TCP
port: 80
targetPort: 8080
selector:
app: embl-jobs-pages
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: embl-jobs-pages
spec:
selector:
matchLabels:
app: embl-jobs-pages
replicas: 1
template:
metadata:
labels:
app: embl-jobs-pages
spec:
containers:
- name: embl-jobs-pages
image: embl-jobs-pages:latest
ports:
- containerPort: 8080
commonLabels:
app: Embl-Jobs-Pages
resources:
- deployment.yaml
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: embl-jobs-pages
spec:
template:
spec:
containers:
- name: embl-jobs-pages
image: $IMAGE_NAME
commonLabels:
varient: caas
commonAnnotations:
note: CaaS varients
bases:
- ../../base
patches:
- ci_image.yaml
- replicas.yaml
- probes.yaml
- registry_credentials.yaml
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: embl-jobs-pages
spec:
template:
spec:
containers:
- name: embl-jobs-pages
readinessProbe:
httpGet:
path: /jobs
port: 8080
initialDelaySeconds: 5
timeoutSeconds: 1
periodSeconds: 15
livenessProbe:
httpGet:
path: /jobs
port: 8080
initialDelaySeconds: 15
timeoutSeconds: 1
periodSeconds: 15
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: embl-jobs-pages
spec:
template:
spec:
imagePullSecrets:
- name: gitlab
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: embl-jobs-pages
spec:
replicas: 2
{
"name": "@visual-framework/vf-boilerplate-eleventy",
"version": "2.0.0-alpha.14",
"description": "Build a site with Visual Framework components using the the Eleventy templating engine.",
"repository": {
"type": "git",
"url": "git+https://github.com/visual-framework/vf-eleventy.git"
},
"license": "Apache-2.0",
"author": "Ken Hawkins <ken.hawkins@embl.de> (https://www.embl.de/aboutus/communication_outreach/)",
"main": "gulpfile.js",
"vfConfig": {
"vfName": "EMBL Jobs directory",
"vfNamespace": "embljobs-",
"vfComponentPath": "./src/components",
"vfBuildDestination": "./build",
"vfBuildFractalMode": "dataObject",
"vfThemePath": "@frctl/mandelbrot"
},
"scripts": {
"build": "gulp build && echo \"🏋 Build completed\" && exit 0",
"dev": "gulp dev",
"start": "npm run dev",
"test": "echo \"Error: no test specified\" && exit 0",
"update-components": "yarn upgrade-interactive --latest"
},
"dependencies": {
"@11ty/eleventy": "0.12.1",
"@node-sass/node-module-importer": "1.2.3",
"@visual-framework/embl-breadcrumbs-lookup": "2.0.1",
"@visual-framework/embl-conditional-edit": "1.0.3",
"@visual-framework/embl-content-hub-loader": "1.0.8",
"@visual-framework/embl-content-meta-properties": "1.0.1",
"@visual-framework/embl-favicon": "1.0.2",
"@visual-framework/embl-grid": "2.1.2",
"@visual-framework/embl-logo": "1.1.0",
"@visual-framework/embl-notifications": "1.0.1",
"@visual-framework/vf-analytics-google": "1.0.3",
"@visual-framework/vf-banner": "1.7.1",
"@visual-framework/vf-body": "1.1.0",
"@visual-framework/vf-box": "2.3.1",
"@visual-framework/vf-breadcrumbs": "2.0.3",
"@visual-framework/vf-card": "2.5.4",
"@visual-framework/vf-card-container": "3.1.2",
"@visual-framework/vf-cluster": "1.0.2",
"@visual-framework/vf-code-example": "1.2.1",
"@visual-framework/vf-config": "1.0.1-alpha.0",