@transportme/vline-nsp-reader 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ export default {
2
+ VLINE_HOST: 'https://vline.com.au',
3
+ HEAT_PAGE: '/Timetables/heat'
4
+ }
@@ -0,0 +1,93 @@
1
+ import fetch from 'node-fetch';
2
+ import constants from './constants.mjs'
3
+ import { load as parseHTML } from 'cheerio'
4
+ import async from 'async'
5
+ import fs from 'fs/promises'
6
+ import path from 'path'
7
+ import { pipeline } from 'stream/promises'
8
+ import { createWriteStream } from 'fs'
9
+ import PassPDFReader from '../pass/pass-pdf-reader.mjs'
10
+
11
+ export class HeatTimetable {
12
+
13
+ files = []
14
+
15
+ constructor() {
16
+ }
17
+
18
+ addFile(file) {
19
+ this.files.push(file)
20
+ }
21
+
22
+ async saveFiles(outputDir) {
23
+ try {
24
+ await fs.mkdir(outputDir)
25
+ } catch (e) {}
26
+ await async.forEach(this.files, async file => {
27
+ await file.download(outputDir)
28
+ })
29
+ }
30
+
31
+ }
32
+
33
+ export class HeatTimetableFile {
34
+
35
+ line
36
+ type
37
+ href
38
+
39
+ #filePath
40
+
41
+ constructor(line, type, href) {
42
+ this.line = line
43
+ this.type = type
44
+ this.href = href
45
+ }
46
+
47
+ async download(outputDir) {
48
+ this.#filePath = path.join(outputDir, `${this.line} - ${this.type}.pdf`)
49
+
50
+ let response = await fetch(constants.VLINE_HOST + this.href)
51
+ let outputStream = createWriteStream(this.#filePath)
52
+
53
+ await pipeline(response.body, outputStream)
54
+ }
55
+
56
+ setFilePath(filePath) {
57
+ this.#filePath = filePath
58
+ }
59
+
60
+ async extractRuns() {
61
+ let reader = new PassPDFReader(this.#filePath)
62
+ return await reader.readRuns()
63
+ }
64
+
65
+ static fromFile(pathname) {
66
+ const filename = path.basename(pathname).replace('.pdf', '')
67
+ const [line, type] = filename.split(' - ')
68
+ const file = new HeatTimetableFile(line, type, '')
69
+ file.setFilePath(pathname)
70
+
71
+ return file
72
+ }
73
+
74
+ }
75
+
76
+ export async function getHeatTimetables() {
77
+ let body = await (await fetch(constants.VLINE_HOST + constants.HEAT_PAGE)).text()
78
+ let $ = parseHTML(body)
79
+
80
+ let buttons = Array.from($('div.TimeTableHeaderMainContainer > a.button-file-link-caption'))
81
+ const timetable = new HeatTimetable()
82
+
83
+ buttons.forEach(button => {
84
+ let text = $(button).text().replace(/PDF.+/, '').replace(' extreme heat timetable', '').replace(/via .+\(/, '(').replace(/ \/ \w+/, '')
85
+ let data = text.match(/([\w ]+) \((.+)\)/)
86
+ if (!data) return null
87
+
88
+ let [_, line, type] = data
89
+ timetable.addFile(new HeatTimetableFile(line, type, $(button).attr('href')))
90
+ })
91
+
92
+ return timetable.files.length ? timetable : null
93
+ }
@@ -28,13 +28,15 @@ export default class PassPDFReader {
28
28
  const body = this.getBody(table)
29
29
 
30
30
  for (let columnIndex = 0; columnIndex < body[0].length; columnIndex++) {
31
+ let operationDay = body[0][columnIndex]
31
32
  let currentRun = {
32
33
  type: '',
33
- stops: []
34
+ stops: [],
35
+ operationDay
34
36
  }
35
37
 
36
38
  let lastStation
37
- for (let stationIndex = 0; stationIndex < stations.length; stationIndex++) {
39
+ for (let stationIndex = 1; stationIndex < stations.length; stationIndex++) {
38
40
  const stationName = stations[stationIndex]
39
41
  const stopData = (body[stationIndex][columnIndex] || '').replace('.', ':')
40
42
  if (stationName === 'Service') {
@@ -43,13 +45,15 @@ export default class PassPDFReader {
43
45
  }
44
46
  if (stationName === 'Service Information') continue
45
47
  if (!stopData || stopData.length === 1) continue
48
+ if (stationName === 'Change Service' && !currentRun.stops.length) continue
46
49
  if (stationName === 'Change Service' && stopData.length) {
47
50
  currentRun.stops.push(lastStation)
48
51
  runs.push(currentRun)
49
52
  lastStation = null
50
53
  currentRun = {
51
54
  type: stopData[0] + stopData.slice(1).toLowerCase(),
52
- stops: []
55
+ stops: [],
56
+ operationDay
53
57
  }
54
58
 
55
59
  continue
@@ -67,8 +71,8 @@ export default class PassPDFReader {
67
71
  }
68
72
  }
69
73
 
70
- currentRun.stops.push(lastStation)
71
- runs.push(currentRun)
74
+ if (lastStation) currentRun.stops.push(lastStation)
75
+ if (currentRun.stops.length) runs.push(currentRun)
72
76
  }
73
77
  }
74
78
 
@@ -56,11 +56,12 @@ export default class PassTableReader {
56
56
  const tableData = []
57
57
 
58
58
  page.Texts.forEach(text => {
59
+ if (text.y < tableStart - commonHeight * 2 || text.y > tableEnd + commonHeight * 1.5) return
60
+
59
61
  let textContent = decodeURIComponent(text.R[0].T)
60
62
 
61
- let firstYGreater = rowStarts.find(r => r > text.y + 0.3)
62
- let currentRow = rowStarts.indexOf(firstYGreater) - 1
63
- if (currentRow < 0) return
63
+ let currentRow = text.y < tableStart - commonHeight ? 0 : rowStarts.findIndex(r => r > text.y + 0.3)
64
+ if (currentRow < 0) currentRow = rowStarts.length
64
65
 
65
66
  let currentCol = colStarts.findLastIndex(c => c < text.x + 0.4)
66
67
 
@@ -77,29 +78,22 @@ export default class PassTableReader {
77
78
  }
78
79
  }
79
80
 
80
- return tableData
81
- })
82
-
83
- console.log(tables)
84
-
85
- console.log(heightFrequency)
86
- console.log(VLines)
87
- console.log(rowStarts)
81
+ const colCount = tableData[1].length
82
+ for (let x = colCount - 1; x > 0; x--) {
83
+ let text = tableData[0][x] || ''
84
+ if (text.startsWith(' ') || text.startsWith('to')) {
85
+ tableData[0][x - 1] = (tableData[0][x - 1] + ' ' + text.trim()).replace('continued', '').trim()
86
+ tableData[0][x] = ''
87
+ }
88
+ }
88
89
 
89
- let pageData = []
90
- let smallTable = rowStarts.length === 2
90
+ for (let x = 1; x < colCount; x++) {
91
+ if (!tableData[0][x]) tableData[0][x] = tableData[0][x - 1]
92
+ }
91
93
 
92
- return pageData
94
+ return tableData
95
+ })
93
96
  })
94
-
95
- return pages[0]
96
-
97
- // return pages.map(page => {
98
- // let maxSize = Math.max(...page.map(row => row.length))
99
- // let blankCells = Array(maxSize).fill('')
100
-
101
- // return page.map(row => row.map(g => g.replace(/ +/g, ' ').trim()).concat(blankCells).slice(0, maxSize))
102
- // })
103
97
  }
104
98
 
105
99
  read() {
package/lib.mjs CHANGED
@@ -1,9 +1,13 @@
1
+ import { getHeatTimetables, HeatTimetable, HeatTimetableFile } from './lib/heat/vline-heat.mjs'
1
2
  import { getNSPVersion, NSPFile, NSPVersion } from './lib/nsp/vline-nsp.mjs'
2
3
  import PassPDFReader from './lib/pass/pass-pdf-reader.mjs'
3
4
 
4
5
  export {
5
6
  getNSPVersion,
7
+ getHeatTimetables,
6
8
  NSPFile,
7
9
  NSPVersion,
8
- PassPDFReader
10
+ PassPDFReader,
11
+ HeatTimetable,
12
+ HeatTimetableFile
9
13
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@transportme/vline-nsp-reader",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "main": "lib.mjs",
5
5
  "scripts": {
6
6
  "test": "mocha './{,!(node_modules)/**}/*.test.mjs'"