Skip to content

Commit

Permalink
feat: show forecast data in stock report (#22)
Browse files Browse the repository at this point in the history
* docs: adds another project

* feat: stock report shows forecast data when available

* refactor: renaming createdAt in forecast data to date

* refactor: different format for restock date
  • Loading branch information
Ephigenia authored Sep 7, 2020
1 parent 95856e8 commit 4ba9412
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 18 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Features
================================================================================

- list of 156 IKEA stores worldwide
- get product stock amount for a product from a whole country or single store in JSON, CSV and CLI-Table format
- get product stock amount for a product from a whole country or single store in JSON, CSV and CLI-Table format including forecast
- support for many countries: ae, at, au, ca, ch, cn, cz, de, dk, es, fi, fr, gb
hk, hr, hu, ie, it, jo, jp, kr, kw, lt, my, nl, no, pl, pt, qa, ro, ru, sa, se, sg, sk, th, tw, us
- integrate the library into your node project
Expand Down Expand Up @@ -223,4 +223,4 @@ Other Projects & Articles

* npm package [ikea-stock-checker](https://www.npmjs.com/package/ikea-stock-checker)
* [API of the Day: Checking IKEA Availability and Warehouse Locations](https://medium.com/@JoshuaAJung/api-of-the-day-ikea-availability-checks-8678794a9b52) by Joshua Jung

* https://github.com/lovegandhi/ikea-stock-checker
48 changes: 42 additions & 6 deletions source/lib/iows2.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ const stores = require('./stores');
* @typedef {('LOW'|'MEDIUM'|'HIGH')} ProductAvailabilityProbability
*/

/**
* @typedef {object} ProductAvailabilityForecastItem
* @property {createdAt} date
* instance of a date for which the estimation is made
* @property {number} stock
* estimated number of items in stock on the forecasted date
* @property {ProductAvailabilityProbability} probability
* probability of the product beeing in store ("LOW", "MEDIUM" or "HIGH")
*/

/**
* @typedef {Object} ProductAvailability
* @property {Date} createdAt instance of a javascript date of the moment when
Expand All @@ -23,6 +33,11 @@ const stores = require('./stores');
* ikea store identification number
* @property {Number} stock
* number of items currently in stock
* @property {ProductAvailabilityForecastItem[]} [forecast]
* when available a list of items indicating the estimated stock amount in the
* next days
* @property {Date} [restockDate]
* Estimated date when the item gets restocked. Can be empty
*/

/**
Expand Down Expand Up @@ -77,34 +92,55 @@ class IOWS2 {
}

/**
* @param {object<string, any>} data
* @param {object<string, any>} data plain iows endpoint response data object
* @returns {ProductAvailability} transformed stock information
*/
static parseAvailabilityFromResponseData(data) {
const stock = data.StockAvailability.RetailItemAvailability.AvailableStock.$;
const probability = data.StockAvailability.RetailItemAvailability.InStockProbabilityCode.$;
const availability = data.StockAvailability;

// AvailableStockForecastList can contain estimated stock amounts in the
// next 4 days.
const forecastData = availability.AvailableStockForecastList.AvailableStockForecast || [];
const forecast = forecastData.map(item => ({
stock: parseInt(item.AvailableStock.$, 10),
date: new Date(item.ValidDateTime.$),
probability: item.InStockProbabilityCode.$,
}));

// RestockDateTime may contain an estimated date when the product gets
// restocked. It also can be missing in the response
let restockDate = null;
if (availability.RetailItemAvailability.RestockDateTime) {
restockDate = new Date(availability.RetailItemAvailability.RestockDateTime.$);
}

const stock = availability.RetailItemAvailability.AvailableStock.$;
const probability = availability.RetailItemAvailability.InStockProbabilityCode.$;
return {
createdAt: new Date(),
forecast,
probability,
restockDate,
stock,
};
}

buildUrl(baseUrl, countryCode, languageCode, buCode, productId) {
// build url for single store and product Id
// ireland requires a different URL
let code = 'ART';
let itemType = 'ART';
// TODO move this to somewhere else
if (buCode === '038') {
code = 'SPR';
itemType = 'SPR';
}
return [
this.baseUrl,
encodeURIComponent(this.countryCode),
encodeURIComponent(this.languageCode),
'stores',
buCode,
'availability/' + code,
'availability',
itemType,
encodeURIComponent(productId)
].join('/');
}
Expand Down
47 changes: 37 additions & 10 deletions source/lib/reporter/stock-table.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ function probabilityColor(val) {
}
}

function diffDays(date1, date2) {
return (date1.getTime() - date2.getTime()) / 60 / 60 / 24 / 1000;
}

module.exports = {
createReport: function(data) {
let table = new Table({
Expand All @@ -39,6 +43,8 @@ module.exports = {
'store',
'stock',
'probability',
'restockDate',
'forecast',
],
colAligns: [
null,
Expand All @@ -49,20 +55,41 @@ module.exports = {
null,
'right',
'right',
null,
],
});

data
.map(({ productId, store, availability }) => [
availability.createdAt.toISOString(),
store.countryCode,
countries.getName(store.countryCode, 'en'),
productId,
store.buCode,
store.name,
availabilityColor(availability.stock)(availability.stock),
probabilityColor(availability.probability)(availability.probability),
])
.map(({ productId, store, availability }) => {
const { restockDate, createdAt, stock, probability } = availability;

let restockColumn = '';
if (availability.restockDate) {
const daysUntilRestock = Math.floor(diffDays(availability.restockDate, new Date()));
if (daysUntilRestock > 0) {
restockColumn = `in ${daysUntilRestock}d (${restockDate.toISOString().substr(0, 10)})`;
}
}

const forecast = availability.forecast.map(item => {
const shortDate = item.date.toISOString().substr(5, 5);
const coloredStock = availabilityColor(item.stock)(item.stock);
return `${shortDate}: ${coloredStock}`;
}).join(', ');

return [
createdAt.toISOString(),
store.countryCode,
countries.getName(store.countryCode, 'en'),
productId,
store.buCode,
store.name,
availabilityColor(stock)(stock),
probabilityColor(probability)(probability),
restockColumn,
forecast,
]
})
.forEach(row => table.push(row));

return table.toString();
Expand Down

0 comments on commit 4ba9412

Please sign in to comment.