feat(route): add layoffs.fyi (#11621)

* feat(route): add layoffs.fyi

Add RSS source for layoffs.fyi (routes and docs).

* Update lib/v2/layoffs/index.js

Remove the unnecessary header.
This commit is contained in:
BrandNewLifeJackie26 2023-01-15 10:26:14 -08:00 committed by GitHub
parent 3d1b54f1b4
commit e4053665d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 139 additions and 0 deletions

View File

@ -136,6 +136,14 @@ Official Website: <https://news.yahoo.co.jp/pages/article/20200207>
</RouteEn>
## Layoffs.fyi
### Layoff Data Tracker
<RouteEn author="BrandNewLifeJackie26" example="/layoffs" path="/layoffs" radar="1"/>
RSS source in the original site is outdated.
## Lever
### Recruitment

View File

@ -155,6 +155,14 @@ pageClass: routes
<Route author="proletarius101" example="/iyouport/osint" path="/iyouport/:category?"/>
## Layoffs.fyi
### 裁员数据跟踪
<Route author="BrandNewLifeJackie26" example="/layoffs" path="/layoffs" radar="1"/>
该网站原始 RSS 数据源无人维护,故重新抓取数据并生成数据源。
## MiniFlux
因需设置 API Key故请自行架设 RSSHub。API 密钥则请于 MiniFlux 实例中的 `设置` -> `API密钥` -> `创建一个新的API密钥` 处获取。

104
lib/v2/layoffs/index.js Normal file
View File

@ -0,0 +1,104 @@
const got = require('@/utils/got');
const { parseDate } = require('@/utils/parse-date');
const ROW_COUNT = 100;
const WEBSITE_URL = 'https://layoffs.fyi';
const SOURCE_URL = `https://airtable.com/v0.3/view/viwA14Z1pM69YIsaW/readSharedViewData?
stringifiedObjectParams=%7B%22shouldUseNestedResponseFormat%22%3Atrue%7D&
requestId=reqQcci3rVyhkcAyX&accessPolicy=%7B%22allowedActions%22%3A%5B%7B%22
modelClassName%22%3A%22view%22%2C%22modelIdSelector%22%3A%22viwA14Z1pM69YIsaW%22%2C%22
action%22%3A%22readSharedViewData%22%7D%2C%7B%22modelClassName%22%3A%22view%22%2C%22
modelIdSelector%22%3A%22viwA14Z1pM69YIsaW%22%2C%22action%22%3A%22getMetadataForPrinting
%22%7D%2C%7B%22modelClassName%22%3A%22view%22%2C%22modelIdSelector
%22%3A%22viwA14Z1pM69YIsaW%22%2C%22action%22%3A%22readSignedAttachmentUrls
%22%7D%2C%7B%22modelClassName%22%3A%22row%22%2C%22modelIdSelector%22%3A%22
rows%20*%5BdisplayedInView%3DviwA14Z1pM69YIsaW%5D%22%2C%22action%22%3A%22
createBoxDocumentSession%22%7D%2C%7B%22modelClassName%22%3A%22row%22%2C%22
modelIdSelector%22%3A%22rows%20*%5BdisplayedInView%3DviwA14Z1pM69YIsaW%5D%22%2C%22
action%22%3A%22createDocumentPreviewSession%22%7D%5D%2C%22shareId%22%3A%22shrqYt5kSqMzHV9R5%22%2C%22
applicationId%22%3A%22app1PaujS9zxVGUZ4%22%2C%22generationNumber%22%3A0%2C%22
expires%22%3A%222023-02-02T00%3A00%3A00.000Z%22%2C%22
signature%22%3A%22096478e17705ffdac261da07c5b4c025aff6d8c6d86be027533fd248913798de%22%7D`;
/**
* getMapping can convert either an array or an object
* of a specific structure into a Map, where the key is
* the name and the value is the Id. It will only parse
* entries with 'name' and 'id' in it
*
* Inputs:
* obj = [{name: 'Apple', id: 1}, {name: 'Peach', id: 2}]
* Returns:
* [{1: 'Apple', 2: 'Peach'}, {Apple: 1, Peach: 2}]
*
* Inputs:
* obj = {Apple: {name: 'Apple', id: 1}, Peach: {name: 'Peach', id: 2}}
* Returns:
* [{1: 'Apple', 2: 'Peach'}, {Apple: 1, Peach: 2}]
*/
const getMappings = function (obj) {
const mapping = new Map();
const reverseMapping = new Map();
for (const key in obj) {
if ('name' in obj[key] && 'id' in obj[key]) {
reverseMapping.set(obj[key].name, obj[key].id);
mapping.set(obj[key].id, obj[key].name);
}
}
return [mapping, reverseMapping];
};
module.exports = async (ctx) => {
const headers = {
'x-airtable-application-id': 'app1PaujS9zxVGUZ4',
'x-airtable-inter-service-client': 'webClient',
'x-requested-with': 'XMLHttpRequest',
'x-time-zone': 'America/Los_Angeles',
};
// Get data from data source
const response = await got({
method: 'get',
url: SOURCE_URL,
headers,
});
const data = response.data.data;
const table = data.table;
// Columns are represented by special IDs
const columnReverseMapping = getMappings(table.columns)[1];
const companyColumnId = columnReverseMapping.get('Company');
const dateAddedColumnId = columnReverseMapping.get('Date Added');
const numOfLaidOffColumnId = columnReverseMapping.get('# Laid Off');
const sourceColumnId = columnReverseMapping.get('Source');
const countryColumnId = columnReverseMapping.get('Country');
const countryMapping = getMappings(table.columns.filter((col) => col.name === 'Country')[0].typeOptions.choices)[0];
const rows = table.rows.slice(0, ROW_COUNT);
ctx.state.data = {
title: 'Tech layoff data feed from layoffs.fyi',
link: WEBSITE_URL,
description: 'This feed gets tech layoff data from layoffs.fyi and display them in a user friendly way',
item: rows.map((row) => {
const rowContent = row.cellValuesByColumnId;
const company = rowContent[companyColumnId];
const dateAdded = parseDate(rowContent[dateAddedColumnId]);
const source = rowContent[sourceColumnId];
const numOfLaidOff = rowContent[numOfLaidOffColumnId] === undefined ? 'some' : rowContent[numOfLaidOffColumnId];
const country = countryMapping.get(rowContent[countryColumnId]);
const title = `${company} Layoffs Happening!`;
const description = `${company} lays off ${numOfLaidOff} employees in ${country}. For more details, please visit ${source}.`;
return {
title,
description, // the article content
pubDate: dateAdded, // Data publish date
link: source, // Laid off source link
};
}),
};
};

View File

@ -0,0 +1,3 @@
module.exports = {
'/': ['BrandNewLifeJackie26'],
};

13
lib/v2/layoffs/radar.js Normal file
View File

@ -0,0 +1,13 @@
module.exports = {
'layoffs.fyi': {
_name: 'Layoffs',
'.': [
{
title: 'Data Tracker Feed',
docs: 'https://docs.rsshub.app/other.html#layoffs',
source: '/',
target: '/layoffs',
},
],
},
};

3
lib/v2/layoffs/router.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = function (router) {
router.get('/', require('./index.js'));
};