Skip to content

Commit

Permalink
Update:
Browse files Browse the repository at this point in the history
a) Add WebWorker for history downloading;
b) Add Windows e2e Github actions
  • Loading branch information
ccxzhang committed Jul 26, 2024
1 parent 503a16c commit 8bba34d
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 100 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,30 @@ jobs:
TEST_DHIS2_URL: ${{ secrets.TEST_DHIS2_URL }}
TEST_USERNAME: ${{ secrets.TEST_USERNAME }}
TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}

build_on_win:
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18.x'

- name: Install dependencies
run: npm install

- name: Build project
run: npm run build:win

- name: Run Playwright tests
run: npm run test:playwright
env:
ELECTRON_NO_ATTACH_CONSOLE: true
ELECTRON_DISABLE_SANDBOX: true
DEBUG: pw:api
TEST_DHIS2_URL: ${{ secrets.TEST_DHIS2_URL }}
TEST_USERNAME: ${{ secrets.TEST_USERNAME }}
TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}
4 changes: 4 additions & 0 deletions electron.vite.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export default defineConfig({
'@renderer': resolve('src/renderer/src')
}
},
worker: {
format: 'es',
plugins: []
},
plugins: [react()]
}
})
80 changes: 71 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"dexie-react-hooks": "^1.1.7",
"dotenv": "^16.4.5",
"electron-updater": "^6.1.7",
"jszip": "^3.10.1",
"playwright": "^1.45.1",
"react-redux": "^9.1.2",
"react-router-dom": "^6.23.1",
Expand Down
2 changes: 1 addition & 1 deletion src/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function createWindow() {
return { action: 'deny' }
})

mainWindow.webContents.openDevTools()
// mainWindow.webContents.openDevTools()

// HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local html file for production.
Expand Down
87 changes: 55 additions & 32 deletions src/renderer/src/pages/DownloadHistory/index.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useState } from 'react'
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import JSZip from 'jszip'
import { useLiveQuery } from 'dexie-react-hooks'
import { objectToCsv, jsonToCsv } from '../../utils/downloadUtils'
import { fetchCsvData, fetchData } from '../../service/useApi'
Expand All @@ -11,12 +12,12 @@ const HistoryPage = ({ dictionaryDb }) => {
const [selectedRows, setSelectedRows] = useState([])
const dispatch = useDispatch()
const { dhis2Url, username, password } = useSelector((state) => state.auth)
const worker = new Worker(new URL('../../service/worker.js', import.meta.url))

const handleCheckboxChange = (id) => {
// setSelectedRows((prev) =>
// prev.includes(id) ? prev.filter((rowId) => rowId !== id) : [...prev, id]
// )
setSelectedRows([id])
setSelectedRows((prev) =>
prev.includes(id) ? prev.filter((rowId) => rowId !== id) : [...prev, id]
)
}

const handleSelectAllChange = () => {
Expand All @@ -26,33 +27,55 @@ const HistoryPage = ({ dictionaryDb }) => {
}

const handleQuickRedownload = async () => {
const redownloadingUrls = downloadQueries
.filter(({ id }) => selectedRows.includes(id))
.map((el) => el.url)
if (!worker || selectedRows.length === 0) {
console.log('No worker or no URLs selected for redownload.')
return
}

try {
dispatch(setLoading(true))
for (let i = 0; i < redownloadingUrls.length; i++) {
const url = redownloadingUrls[i]
let csvBlob
if (url.includes('csv')) {
csvBlob = await fetchCsvData(url, username, password)
} else {
const data = await fetchData(url, username, password)
const { csvData } = jsonToCsv(data)
csvBlob = new Blob([csvData], { type: 'text/csv;charset=utf-8' })
const zip = new JSZip()
dispatch(setLoading(true))

const fetchFile = async (url, index) => {
return new Promise((resolve, reject) => {
const handleMessage = (e) => {
if (e.data.index === index) {
worker.removeEventListener('message', handleMessage)

if (e.data.type === 'success') {
const csvBlob = new Blob([e.data.data], { type: 'text/csv;charset=utf-8' })
zip.file(`history_${index}.csv`, csvBlob)
dispatch(
setNotification({ message: `Finished downloading from ${url}`, type: 'info' })
)
resolve()
} else if (e.data.type === 'error') {
dispatch(setError(e.data.message))
reject(new Error(e.data.message))
}
}
}
dispatch(setNotification({ message: `Finished ${i}`, type: 'info' }))
const link = document.createElement('a')
link.href = URL.createObjectURL(csvBlob)
link.setAttribute('download', `file_${i}.csv`)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
worker.addEventListener('message', handleMessage)
worker.postMessage({ url, username, password, index })
})
}

try {
const promises = downloadQueries
.filter(({ id }) => selectedRows.includes(id))
.map((query, index) => fetchFile(query.url, index))

await Promise.all(promises)

zip.generateAsync({ type: 'blob' }).then((content) => {
const element = document.createElement('a')
element.href = URL.createObjectURL(content)
element.download = 'selected_histories.zip'
document.body.appendChild(element)
element.click()
document.body.removeChild(element)
})
} catch (error) {
const errorMessage = error.message ? error.message : error
dispatch(setError(errorMessage))
console.error('Error processing downloads:', error)
} finally {
dispatch(setLoading(false))
}
Expand Down Expand Up @@ -80,12 +103,12 @@ const HistoryPage = ({ dictionaryDb }) => {
<thead>
<tr className="bg-gray-200 text-gray-600 uppercase text-sm leading-normal">
<th className="py-3 px-4 text-left">
{/* <input
<input
type="checkbox"
className="form-checkbox h-4 w-4 text-indigo-600 transition duration-150 ease-in-out"
checked={selectedRows.length === downloadQueries.length}
onChange={handleSelectAllChange}
/> */}
/>
</th>
<th className="py-3 px-4 text-left">Organization Level</th>
<th className="py-3 px-4 text-left">Period</th>
Expand Down Expand Up @@ -137,7 +160,7 @@ const HistoryPage = ({ dictionaryDb }) => {
className="bg-indigo-500 text-white px-4 py-2 rounded-md shadow-md hover:bg-indigo-600 transition duration-150 ease-in-out"
disabled={selectedRows.length === 0}
>
Downloading Selected Record
Download Selected Record
</button>
</div>
</div>
Expand Down
7 changes: 1 addition & 6 deletions src/renderer/src/pages/MainPage/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,7 @@ import { fetchData, fetchCsvData } from '../../service/useApi'
import DataElementsMenu from './DataElements'
import CategoryDropdownMenu from './CategoryCombo'
import { generatePeriods } from '../../utils/dateUtils'
import {
generateDownloadingUrl,
createDataChunks,
csvGeneratorToBlob,
jsonToCsvGenerator
} from '../../utils/downloadUtils'
import { generateDownloadingUrl, createDataChunks } from '../../utils/downloadUtils'
import DownloadButton from './DownloadButton'
import { useSelector, useDispatch } from 'react-redux'
import { setLoading, setError, setNotification } from '../../reducers/statusReducer'
Expand Down
Loading

0 comments on commit 8bba34d

Please sign in to comment.