@@ -4,6 +4,7 @@ const npm = require("npm-stat-api");
44const Enum = require ( "enum" ) ;
55const csv = require ( "csv-parser" ) ;
66const createCsvWriter = require ( "csv-writer" ) . createArrayCsvWriter ;
7+ const fetch = require ( "node-fetch" ) ;
78
89const StatDate = require ( "./statdate.js" ) ;
910
@@ -119,6 +120,16 @@ class WriteNpmStat {
119120 } ) ;
120121 }
121122
123+ /**
124+ * Returns last week npm statistics for a package
125+ * @returns {Promise } Promise object represents the last week npm statistics for a package
126+ */
127+ getLastWeekNpmStat ( ) {
128+ return new Promise ( ( resolve ) => {
129+ return resolve ( this . #getLastWeekStat( 100 ) ) ;
130+ } ) ;
131+ }
132+
122133 static getDays ( startDate , endDate ) {
123134 const arr = [ ] ;
124135 const dt = new Date ( startDate ) ;
@@ -151,6 +162,45 @@ class WriteNpmStat {
151162 } ) ;
152163 }
153164
165+ #getLastWeekStat( retryLimit , retryCount ) {
166+ retryLimit = retryLimit || Number . MAX_VALUE ;
167+ retryCount = Math . max ( retryCount || 0 , 0 ) ;
168+ return fetch (
169+ "https://api.npmjs.org/versions/" + this . #packageName + "/last-week"
170+ )
171+ . then ( ( response ) => {
172+ if ( response . status !== 200 ) {
173+ throw new Error ( "response.status: " + response . status ) ;
174+ }
175+ return new Promise ( ( resolve ) => {
176+ response . json ( ) . then ( ( response ) => {
177+ const responseObject = { } ;
178+ const day = StatDate . formatStart ( new Date ( ) ) ;
179+ for ( const [ key , value ] of Object . entries (
180+ response . downloads
181+ ) ) {
182+ const statKey = day + "_" + key ;
183+ const statValues = [ ] ;
184+ statValues . push ( day ) ;
185+ if ( this . writePackageName ) {
186+ statValues . push ( this . #packageName) ;
187+ }
188+ statValues . push ( key ) ;
189+ statValues . push ( value ) ;
190+ responseObject [ statKey ] = statValues ;
191+ }
192+ return resolve ( responseObject ) ;
193+ } ) ;
194+ } ) ;
195+ } )
196+ . catch ( ( err ) => {
197+ if ( retryCount < retryLimit ) {
198+ return this . #getLastWeekStat( retryLimit , retryCount + 1 ) ;
199+ }
200+ throw err ;
201+ } ) ;
202+ }
203+
154204 /**
155205 * Writes npm statistics for a package
156206 * @param {string|null } [startDate] - start date of the statistics
@@ -173,28 +223,56 @@ class WriteNpmStat {
173223 * <br> - "%Y-%m-%d", for example "2022-12-31", which means to be collected until "2022-12-31"
174224 *
175225 * <br> - undefined, which means to be collected until the actual day
176- * @param {string|null } [endDate =npmstat] - postfix of the csv file
226+ * @param {string|null } [postfix =npmstat] - postfix of the csv file
177227 * @returns {Promise } Promise object represents the npm statistics for a package
178228 */
179229 writeNpmStat ( startDate , endDate , postfix = "npmstat" ) {
180230 return new Promise ( ( resolve ) => {
231+ const lastWeek = false ;
181232 const stats = this . getNpmStat ( startDate , endDate ) ;
182233 stats . then ( ( stats ) => {
183234 const grouped = this . #groupStats(
235+ lastWeek ,
184236 stats ,
185237 startDate ,
186238 endDate ,
187239 postfix
188240 ) ;
189- this . #mergeStats( grouped ) . then ( ( merged ) => {
190- this . #writeStats( merged ) ;
241+ this . #mergeStats( lastWeek , grouped ) . then ( ( merged ) => {
242+ this . #writeStats( lastWeek , merged ) ;
191243 return resolve ( merged ) ;
192244 } ) ;
193245 } ) ;
194246 } ) ;
195247 }
196248
197- #groupStats( stats , startDate , endDate , postfix ) {
249+ /**
250+ * Writes last week npm statistics for a package
251+ * @param {string|null } [postfix=lastweek_npmstat] - postfix of the csv file
252+ * @returns {Promise } Promise object represents the last week npm statistics for a package
253+ */
254+ writeLastWeekNpmStat ( postfix = "lastweek_npmstat" ) {
255+ return new Promise ( ( resolve ) => {
256+ const lastWeek = true ;
257+ const stats = this . getLastWeekNpmStat ( ) ;
258+ stats . then ( ( stats ) => {
259+ const day = StatDate . formatStart ( new Date ( ) ) ;
260+ const grouped = this . #groupStats(
261+ lastWeek ,
262+ stats ,
263+ day ,
264+ day ,
265+ postfix
266+ ) ;
267+ this . #mergeStats( lastWeek , grouped ) . then ( ( merged ) => {
268+ this . #writeStats( lastWeek , merged ) ;
269+ return resolve ( merged ) ;
270+ } ) ;
271+ } ) ;
272+ } ) ;
273+ }
274+
275+ #groupStats( lastWeek , stats , startDate , endDate , postfix ) {
198276 const statDate = new StatDate ( startDate , endDate ) ;
199277 const days = WriteNpmStat . getDays ( statDate . start , statDate . end ) ;
200278 const grouped = { } ;
@@ -212,9 +290,17 @@ class WriteNpmStat {
212290 const prefix = day . substring ( 0 , substring ) ;
213291 if ( ! initialized [ prefix ] ) {
214292 initialized [ prefix ] = true ;
215- grouped [ prefix + "_" + postfix + ".csv" ] = [
216- [ day , stats [ day ] ] ,
217- ] ;
293+ grouped [ prefix + "_" + postfix + ".csv" ] = [ ] ;
294+ }
295+ if ( lastWeek ) {
296+ for ( const [ key , value ] of Object . entries ( stats ) ) {
297+ if ( key . startsWith ( day ) ) {
298+ grouped [ prefix + "_" + postfix + ".csv" ] . push ( [
299+ key ,
300+ value ,
301+ ] ) ;
302+ }
303+ }
218304 } else {
219305 grouped [ prefix + "_" + postfix + ".csv" ] . push ( [
220306 day ,
@@ -225,21 +311,29 @@ class WriteNpmStat {
225311 } else {
226312 grouped [ postfix + ".csv" ] = [ ] ;
227313 days . forEach ( ( day ) => {
228- grouped [ postfix + ".csv" ] . push ( [ day , stats [ day ] ] ) ;
314+ if ( lastWeek ) {
315+ for ( const [ key , value ] of Object . entries ( stats ) ) {
316+ if ( key . startsWith ( day ) ) {
317+ grouped [ postfix + ".csv" ] . push ( [ key , value ] ) ;
318+ }
319+ }
320+ } else {
321+ grouped [ postfix + ".csv" ] . push ( [ day , stats [ day ] ] ) ;
322+ }
229323 } ) ;
230324 }
231325 return grouped ;
232326 }
233327
234- #mergeStats( stats ) {
328+ #mergeStats( lastWeek , stats ) {
235329 return new Promise ( ( resolve ) => {
236330 if ( ! this . mergeStoredData ) {
237331 return resolve ( stats ) ;
238332 }
239333 const csvFiles = { } ;
240334 const csvFilesReady = [ ] ;
241335 for ( const [ key , value ] of Object . entries ( stats ) ) {
242- const csvFileReady = this . #readCsv( key , value [ 0 ] ) ;
336+ const csvFileReady = this . #readCsv( lastWeek , key , value [ 0 ] ) ;
243337 csvFilesReady . push ( csvFileReady ) ;
244338 csvFileReady . then ( ( csvData ) => {
245339 Object . assign ( csvFiles , csvData ) ;
@@ -256,7 +350,7 @@ class WriteNpmStat {
256350 } ) ;
257351 }
258352
259- #readCsv( csvFile , firstNewLine ) {
353+ #readCsv( lastWeek , csvFile , firstNewLine ) {
260354 return new Promise ( ( resolve ) => {
261355 const csvData = { } ;
262356 csvData [ csvFile ] = [ ] ;
@@ -273,13 +367,16 @@ class WriteNpmStat {
273367 . pipe ( csv ( ) )
274368 . on ( "data" , ( row ) => {
275369 if ( firstNewLine ) {
276- if ( row . date < firstNewLine [ 0 ] ) {
370+ if ( row . date < firstNewLine [ 0 ] . substring ( 0 , 10 ) ) {
277371 const statKey = row . date ;
278372 const statValues = [ ] ;
279373 statValues . push ( row . date ) ;
280374 if ( writePackageName ) {
281375 statValues . push ( row . package ) ;
282376 }
377+ if ( lastWeek ) {
378+ statValues . push ( row . version ) ;
379+ }
283380 statValues . push ( row . downloads ) ;
284381 csvData [ csvFile ] . push ( [ statKey , statValues ] ) ;
285382 }
@@ -292,19 +389,25 @@ class WriteNpmStat {
292389 } ) ;
293390 }
294391
295- #writeStats( stats ) {
392+ #writeStats( lastWeek , stats ) {
296393 if ( this . outDir ) {
297394 fs . mkdir ( this . outDir , { recursive : true } , ( err ) => {
298395 if ( err ) {
299396 throw err ;
300397 }
301398 for ( const [ key , value ] of Object . entries ( stats ) ) {
302399 const csvFilePath = this . outDir + "/" + key ;
400+ const header = [ "date" ] ;
401+ if ( this . writePackageName ) {
402+ header . push ( "package" ) ;
403+ }
404+ if ( lastWeek ) {
405+ header . push ( "version" ) ;
406+ }
407+ header . push ( "downloads" ) ;
303408 const csvWriter = createCsvWriter ( {
304409 path : csvFilePath ,
305- header : this . writePackageName
306- ? [ "date" , "package" , "downloads" ]
307- : [ "date" , "downloads" ] ,
410+ header,
308411 } ) ;
309412 const postProcessedStats = [ ] ;
310413 value . forEach ( ( stat ) => {
0 commit comments