Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: ENOTDIR, not a directory #172

Merged
merged 9 commits into from
Dec 28, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/backend/eventEmitters/EventEmitter.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// eslint-disable-next-line max-classes-per-file
import Emittery from 'emittery';
import { EnrichedTransaction, OutputVendorName } from '@/backend/commonTypes';
import Emittery from 'emittery';
import { CompanyTypes } from 'israeli-bank-scrapers-core';

export enum EventNames {
IMPORT_PROCESS_START = 'IMPORT_PROCESS_START',
DOWNLOAD_CHROME = 'DOWNLOAD_CHROME',
IMPORTER_START = 'IMPORTER_START',
IMPORTER_PROGRESS = 'IMPORTER_PROGRESS',
IMPORTER_ERROR = 'IMPORTER_ERROR',
Expand Down Expand Up @@ -88,8 +89,15 @@ export class ExporterEvent extends BudgetTrackingEvent {
}
}

export class DownalodChromeEvent extends BudgetTrackingEvent {
constructor(percent: number) {
super({ message: `Download Chrome: ${percent}%` });
}
}

export type EventDataMap = {
[EventNames.IMPORT_PROCESS_START]: BudgetTrackingEvent
[EventNames.DOWNLOAD_CHROME]: DownalodChromeEvent
[EventNames.IMPORTER_START]: ImporterEvent
[EventNames.IMPORTER_PROGRESS]: ImporterEvent
[EventNames.IMPORTER_ERROR]: ImporterEvent
Expand Down
5 changes: 1 addition & 4 deletions src/backend/import/bankScraper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { createScraper, SCRAPERS } from 'israeli-bank-scrapers-core';
import getChrome from './downloadChromium';
import { AccountToScrapeConfig } from '../configManager/configManager';

export { ScaperScrapingResult } from 'israeli-bank-scrapers-core/lib/scrapers/base-scraper';
Expand All @@ -22,13 +21,11 @@ type EmitProgressEventFunction = (eventCompanyId: string, message: string) => Pr

export async function scrape({
companyId, credentials, startDate, showBrowser = false
}: ScrapeParameters, emitProgressEvent: EmitProgressEventFunction) {
}: ScrapeParameters, emitProgressEvent: EmitProgressEventFunction, chromePath: string) {
if (!credentials || (!credentials.username && !credentials.num && !credentials.id) || !credentials.password) {
throw new Error(`Missing credentials for scraper. CompanyId: ${companyId}`);
}

const chromePath = await getChrome(undefined);

const options = {
companyId, // mandatory; one of 'hapoalim', 'discount', 'otsarHahayal', 'leumiCard', 'isracard', 'amex'
startDate, // the date to fetch transactions from (can't be before the minimum allowed time difference for the scraper)
Expand Down
12 changes: 0 additions & 12 deletions src/backend/import/downloadChromium.js

This file was deleted.

31 changes: 31 additions & 0 deletions src/backend/import/downloadChromium.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import download, { OnProgress } from 'download-chromium';
import { getPuppeteerConfig } from 'israeli-bank-scrapers-core';

const getIntegerPercent = (callback: OnProgress): OnProgress => {
let prevPercent = -1;

return ({ percent, ...rest }) => {
const p = Math.floor(percent * 10);
if (p > prevPercent) {
prevPercent = p;
callback({ percent: p, ...rest });
}
};
};

const revision = getPuppeteerConfig().chromiumRevision;

let downloadProm: ReturnType<typeof download>;

export default async function downloadChromium(installPath?: string, onProgress?: OnProgress) {
if (downloadProm) return downloadProm;

downloadProm = download({
revision,
installPath,
onProgress: onProgress && getIntegerPercent(onProgress),
log: true
});

return downloadProm;
}
21 changes: 16 additions & 5 deletions src/backend/import/importTransactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,30 @@ import * as categoryCalculation from '@/backend/import/categoryCalculationScript
import _ from 'lodash';
import moment from 'moment';
import {
AccountStatus, BudgetTrackingEventEmitter, EventNames, EventPublisher, ImporterEvent
AccountStatus, BudgetTrackingEventEmitter, DownalodChromeEvent, EventNames, EventPublisher, ImporterEvent
} from '../eventEmitters/EventEmitter';
import { calculateTransactionHash } from '../transactions/transactions';
import getChrome from './downloadChromium';

type AccountToScrapeConfig = configManager.AccountToScrapeConfig;
type Config = configManager.Config;
type ScrapingConfig = Config['scraping'];

const TRANSACTION_STATUS_COMPLETED = 'completed';

export async function scrapeFinancialAccountsAndFetchTransactions(scrapingConfig: ScrapingConfig, startDate: Date, eventPublisher: EventPublisher) {
export async function scrapeFinancialAccountsAndFetchTransactions(
scrapingConfig: ScrapingConfig, startDate: Date, eventPublisher: EventPublisher, chromePath?: string
) {
const companyIdToTransactions: Record<string, EnrichedTransaction[]> = {};

const dowloadedChrome = await getChrome(chromePath, ({ percent }) => emitChromeDownload(eventPublisher, percent));

const accountsToScrape = scrapingConfig.accountsToScrape.filter((accountToScrape) => accountToScrape.active !== false);
const scrapingPromises = accountsToScrape.map(async (accountToScrape) => {
const companyId = accountToScrape.key;
try {
await eventPublisher.emit(EventNames.IMPORTER_START, buildImporterEvent(accountToScrape, { message: 'Importer start' }));
const scrapeResult = await fetchTransactions(accountToScrape, startDate, scrapingConfig, eventPublisher);
const scrapeResult = await fetchTransactions(accountToScrape, startDate, scrapingConfig, eventPublisher, dowloadedChrome);
const transactions = await postProcessTransactions(accountToScrape, scrapeResult);
companyIdToTransactions[companyId] = transactions;
await eventPublisher.emit(EventNames.IMPORTER_END, buildImporterEvent(accountToScrape, { message: 'Importer end', status: AccountStatus.DONE }));
Expand All @@ -47,6 +53,10 @@ function buildImporterEvent(accountConfig: AccountToScrapeConfig, additionalPara
});
}

function emitChromeDownload(eventPublisher: EventPublisher, percent: number) {
eventPublisher.emit(EventNames.DOWNLOAD_CHROME, new DownalodChromeEvent(percent));
}

export async function getFinancialAccountNumbers() {
const eventEmitter = new BudgetTrackingEventEmitter();
const config = await configManager.getConfig();
Expand All @@ -69,7 +79,8 @@ export async function getFinancialAccountNumbers() {
async function fetchTransactions(
accountToScrapeConfig: AccountToScrapeConfig,
startDate: Date, scrapingConfig: Config['scraping'],
eventPublisher: EventPublisher
eventPublisher: EventPublisher,
chromePath: string
) {
const emitImporterProgressEvent = async (eventCompanyId: string, message: string) => {
await eventPublisher.emit(EventNames.IMPORTER_PROGRESS, buildImporterEvent(accountToScrapeConfig, { message }));
Expand All @@ -80,7 +91,7 @@ async function fetchTransactions(
credentials: accountToScrapeConfig.loginFields,
startDate,
showBrowser: scrapingConfig.showBrowser,
}, emitImporterProgressEvent);
}, emitImporterProgressEvent, chromePath);
if (!scrapeResult.success) {
throw new Error(scrapeResult.errorMessage || scrapeResult.errorType);
}
Expand Down
4 changes: 2 additions & 2 deletions src/backend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export { BudgetTrackingEvent };

export const { inputVendors } = bankScraper;

export async function scrapeAndUpdateOutputVendors(optionalEventPublisher?: EventPublisher) {
export async function scrapeAndUpdateOutputVendors(optionalEventPublisher?: EventPublisher, chromePath?: string) {
const eventPublisher = createEventPublisher(optionalEventPublisher);
const config = await configManager.getConfig();

Expand All @@ -32,7 +32,7 @@ export async function scrapeAndUpdateOutputVendors(optionalEventPublisher?: Even

await eventPublisher.emit(EventNames.IMPORT_PROCESS_START, { message: `Starting to scrape from ${startDate} to today` });

const companyIdToTransactions = await scrapeFinancialAccountsAndFetchTransactions(config.scraping, startDate, eventPublisher);
const companyIdToTransactions = await scrapeFinancialAccountsAndFetchTransactions(config.scraping, startDate, eventPublisher, chromePath);
try {
const executionResult = await createTransactionsInExternalVendors(config.outputVendors, companyIdToTransactions, startDate, eventPublisher);

Expand Down
17 changes: 17 additions & 0 deletions src/types/download-chromium.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
declare module 'download-chromium' {
type Progress = {
percent: number,
transferred: number,
total: number
}
export type OnProgress = (progress: Progress) => void;
type DownloadOptions = {
platform: string,
revision: string,
log: boolean,
onProgress: OnProgress
installPath: string
}
function download(options: Partial<DownloadOptions>): Promise<string>
export default download
}
5 changes: 4 additions & 1 deletion src/ui/components/app/MainContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,11 @@ import { EventEmitter, scrapeAndUpdateOutputVendors } from '@/backend';
import { AccountsState, handleEvent } from '@/ui/components/app/accountsState';
import store from '@/ui/store';
import ConfigEditor from '@/ui/components/app/ConfigEditor.vue';
import electron from 'electron';
import { Levels } from '../shared/log/types';

const userDataPath = electron.remote.app.getPath('userData');

const statusToColor = {
NOT_STARTED: null,
IN_PROGRESS: null,
Expand Down Expand Up @@ -94,7 +97,7 @@ export default defineComponent({
accountsState.value = new AccountsState(config.getActiveImporters, config.getActiveExporters);
accountsState.value.setPendingStatus();
scrapingStatus.value = 'IN_PROGRESS';
scrapeAndUpdateOutputVendors(eventEmitter)
scrapeAndUpdateOutputVendors(eventEmitter, userDataPath)
.then(() => scrapingStatus.value = 'SUCCESS')
.catch((e) => {
accountsState.value.clear();
Expand Down