@fin.cx/opendata 3.5.1 → 3.6.0
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.
- package/{npmextra.json → .smartconfig.json} +12 -6
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.businessrecord.js +86 -43
- package/dist_ts/classes.handelsregister.d.ts +2 -0
- package/dist_ts/classes.handelsregister.js +126 -107
- package/dist_ts/classes.jsonldata.js +9 -6
- package/dist_ts/classes.main.opendata.d.ts +2 -1
- package/dist_ts/classes.main.opendata.js +59 -26
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +2 -1
- package/dist_ts/laws/classes.lawrecord.d.ts +24 -0
- package/dist_ts/laws/classes.lawrecord.js +181 -0
- package/dist_ts/laws/classes.lawservice.d.ts +52 -0
- package/dist_ts/laws/classes.lawservice.js +740 -0
- package/dist_ts/laws/index.d.ts +3 -0
- package/dist_ts/laws/index.js +4 -0
- package/dist_ts/laws/interfaces.law.d.ts +35 -0
- package/dist_ts/laws/interfaces.law.js +2 -0
- package/dist_ts/plugins.d.ts +8 -2
- package/dist_ts/plugins.js +14 -3
- package/dist_ts/stocks/classes.baseproviderservice.js +8 -8
- package/dist_ts/stocks/classes.fundamentalsservice.js +9 -9
- package/dist_ts/stocks/classes.stockdataservice.js +23 -23
- package/dist_ts/stocks/classes.stockservice.js +9 -9
- package/dist_ts/stocks/providers/provider.coingecko.d.ts +1 -1
- package/dist_ts/stocks/providers/provider.coingecko.js +45 -39
- package/dist_ts/stocks/providers/provider.marketstack.d.ts +3 -3
- package/dist_ts/stocks/providers/provider.marketstack.js +20 -16
- package/dist_ts/stocks/providers/provider.secedgar.d.ts +1 -1
- package/dist_ts/stocks/providers/provider.secedgar.js +22 -22
- package/dist_ts/stocks/providers/provider.yahoo.d.ts +2 -2
- package/dist_ts/stocks/providers/provider.yahoo.js +14 -13
- package/package.json +22 -19
- package/readme.md +280 -662
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.businessrecord.ts +1 -1
- package/ts/classes.handelsregister.ts +45 -20
- package/ts/classes.jsonldata.ts +7 -6
- package/ts/classes.main.opendata.ts +63 -30
- package/ts/index.ts +1 -0
- package/ts/laws/classes.lawrecord.ts +81 -0
- package/ts/laws/classes.lawservice.ts +1010 -0
- package/ts/laws/index.ts +3 -0
- package/ts/laws/interfaces.law.ts +48 -0
- package/ts/plugins.ts +19 -0
- package/ts/stocks/classes.fundamentalsservice.ts +1 -1
- package/ts/stocks/classes.stockdataservice.ts +17 -7
- package/ts/stocks/classes.stockservice.ts +2 -2
- package/ts/stocks/providers/provider.marketstack.ts +9 -7
- package/ts/stocks/providers/provider.secedgar.ts +2 -2
- package/ts/stocks/providers/provider.yahoo.ts +3 -3
- package/dist_ts/classes.germanbusinessdata.d.ts +0 -9
- package/dist_ts/classes.germanbusinessdata.js +0 -78
- package/readme.plan.md +0 -193
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@fin.cx/opendata',
|
|
6
|
-
version: '3.
|
|
6
|
+
version: '3.6.0',
|
|
7
7
|
description: 'A comprehensive TypeScript library for accessing business data and real-time financial information. Features include German company data management with MongoDB integration, JSONL bulk processing, automated Handelsregister interactions, and real-time stock market data from multiple providers.'
|
|
8
8
|
}
|
|
@@ -20,15 +20,44 @@ export class HandelsRegister {
|
|
|
20
20
|
this.uniqueDowloadFolder = plugins.path.join(this.downloadDir, plugins.smartunique.uniSimple());
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
private async resetDownloadFolder() {
|
|
24
|
+
await plugins.smartfs.directory(this.uniqueDowloadFolder).recursive().delete().catch(() => {});
|
|
25
|
+
await plugins.smartfs.directory(this.uniqueDowloadFolder).create();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private async waitForDownloadedFile() {
|
|
29
|
+
for (let attempt = 0; attempt < 120; attempt++) {
|
|
30
|
+
const directoryEntries = await plugins.fs.readdir(this.uniqueDowloadFolder);
|
|
31
|
+
const fileName = directoryEntries.find(
|
|
32
|
+
(entry) => !entry.endsWith('.crdownload') && !entry.endsWith('.tmp')
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
if (fileName) {
|
|
36
|
+
const filePath = plugins.path.join(this.uniqueDowloadFolder, fileName);
|
|
37
|
+
const firstStat = await plugins.fs.stat(filePath);
|
|
38
|
+
await plugins.smartdelay.delayFor(500);
|
|
39
|
+
const secondStat = await plugins.fs.stat(filePath);
|
|
40
|
+
|
|
41
|
+
if (firstStat.size === secondStat.size) {
|
|
42
|
+
return filePath;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
await plugins.smartdelay.delayFor(500);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
throw new Error('Timed out while waiting for the download to finish.');
|
|
50
|
+
}
|
|
51
|
+
|
|
23
52
|
public async start() {
|
|
24
53
|
// Start the browser
|
|
25
|
-
await
|
|
54
|
+
await this.resetDownloadFolder();
|
|
26
55
|
await this.smartbrowserInstance.start();
|
|
27
56
|
}
|
|
28
57
|
|
|
29
58
|
public async stop() {
|
|
30
59
|
// Stop the browser
|
|
31
|
-
await plugins.
|
|
60
|
+
await plugins.smartfs.directory(this.uniqueDowloadFolder).recursive().delete();
|
|
32
61
|
await this.smartbrowserInstance.stop();
|
|
33
62
|
}
|
|
34
63
|
|
|
@@ -184,24 +213,16 @@ export class HandelsRegister {
|
|
|
184
213
|
throw new Error('Invalid file type');
|
|
185
214
|
}
|
|
186
215
|
}, typeArg);
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
case 'AD':
|
|
198
|
-
await file.rename(`ad.pdf`);
|
|
199
|
-
break;
|
|
200
|
-
case 'SI':
|
|
201
|
-
await file.rename(`si.xml`);
|
|
202
|
-
break;
|
|
203
|
-
break;
|
|
204
|
-
}
|
|
216
|
+
const downloadedFilePath = await this.waitForDownloadedFile();
|
|
217
|
+
const renamedFilePath = plugins.path.join(
|
|
218
|
+
this.uniqueDowloadFolder,
|
|
219
|
+
typeArg === 'AD' ? 'ad.pdf' : 'si.xml'
|
|
220
|
+
);
|
|
221
|
+
await plugins.fs.rename(downloadedFilePath, renamedFilePath);
|
|
222
|
+
const file = await plugins.smartfileFactory.fromFilePath(renamedFilePath);
|
|
223
|
+
|
|
224
|
+
// Keep the download folder empty between requests.
|
|
225
|
+
await this.resetDownloadFolder();
|
|
205
226
|
return file;
|
|
206
227
|
}
|
|
207
228
|
|
|
@@ -297,6 +318,10 @@ export class HandelsRegister {
|
|
|
297
318
|
*/
|
|
298
319
|
public async getSpecificCompany(companyArg: BusinessRecord['data']['germanParsedRegistration']) {
|
|
299
320
|
return this.asyncExecutionStack.getExclusiveExecutionSlot(async () => {
|
|
321
|
+
if (!companyArg?.type || !companyArg.number || !companyArg.court) {
|
|
322
|
+
throw new Error('A complete parsed German registration is required.');
|
|
323
|
+
}
|
|
324
|
+
|
|
300
325
|
const page = await this.getNewPage();
|
|
301
326
|
await this.navigateToPage(page, 'Normal search');
|
|
302
327
|
await page.waitForSelector('#form\\:schlagwoerter', { timeout: 5000 });
|
package/ts/classes.jsonldata.ts
CHANGED
|
@@ -53,14 +53,15 @@ export class JsonlDataProcessor<T> {
|
|
|
53
53
|
dataUrlArg = 'https://daten.offeneregister.de/de_companies_ocdata.jsonl.bz2'
|
|
54
54
|
) {
|
|
55
55
|
const done = plugins.smartpromise.defer();
|
|
56
|
-
const dataExists = await plugins.
|
|
56
|
+
const dataExists = await plugins.smartfs.directory(this.germanBusinessDataDir).exists();
|
|
57
57
|
if (!dataExists) {
|
|
58
|
-
await plugins.
|
|
58
|
+
await plugins.smartfs.directory(this.germanBusinessDataDir).create();
|
|
59
59
|
} else {
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
const
|
|
63
|
-
|
|
62
|
+
const jsonlDataStream = await plugins.smartarchive.SmartArchive.create()
|
|
63
|
+
.url(dataUrlArg)
|
|
64
|
+
.toStreamFiles();
|
|
64
65
|
let totalRecordsCounter = 0;
|
|
65
66
|
let nextRest: string = '';
|
|
66
67
|
jsonlDataStream.pipe(
|
|
@@ -74,11 +75,11 @@ export class JsonlDataProcessor<T> {
|
|
|
74
75
|
writeFunction: async (chunkArg: Buffer, streamToolsArg) => {
|
|
75
76
|
const currentString = nextRest + chunkArg.toString();
|
|
76
77
|
const lines = currentString.split('\n');
|
|
77
|
-
nextRest = lines.pop();
|
|
78
|
+
nextRest = lines.pop() ?? '';
|
|
78
79
|
console.log(`Got another ${lines.length} records.`);
|
|
79
80
|
const concurrentProcessor = new plugins.smartarray.ConcurrentProcessor<string>(
|
|
80
81
|
async (line) => {
|
|
81
|
-
let entry: T;
|
|
82
|
+
let entry: T | undefined;
|
|
82
83
|
if (!line) return;
|
|
83
84
|
try {
|
|
84
85
|
entry = JSON.parse(line);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { BusinessRecord } from './classes.businessrecord.js';
|
|
2
2
|
import { HandelsRegister } from './classes.handelsregister.js';
|
|
3
3
|
import { JsonlDataProcessor, type SeedEntryType } from './classes.jsonldata.js';
|
|
4
|
-
import * as paths from './paths.js';
|
|
5
4
|
import * as plugins from './plugins.js';
|
|
6
5
|
|
|
7
6
|
export interface IOpenDataConfig {
|
|
@@ -11,12 +10,13 @@ export interface IOpenDataConfig {
|
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
export class OpenData {
|
|
14
|
-
public db
|
|
15
|
-
private
|
|
13
|
+
public db!: plugins.smartdata.SmartdataDb;
|
|
14
|
+
private localSmartDb?: plugins.smartdb.LocalSmartDb;
|
|
16
15
|
private config: IOpenDataConfig;
|
|
16
|
+
private started = false;
|
|
17
17
|
|
|
18
|
-
public jsonLDataProcessor
|
|
19
|
-
public handelsregister
|
|
18
|
+
public jsonLDataProcessor!: JsonlDataProcessor<SeedEntryType>;
|
|
19
|
+
public handelsregister!: HandelsRegister;
|
|
20
20
|
|
|
21
21
|
public CBusinessRecord = plugins.smartdata.setDefaultManagerForDoc(this, BusinessRecord);
|
|
22
22
|
|
|
@@ -28,38 +28,62 @@ export class OpenData {
|
|
|
28
28
|
throw new Error('@fin.cx/opendata: All directory paths are required (downloadDir, germanBusinessDataDir, nogitDir).');
|
|
29
29
|
}
|
|
30
30
|
this.config = configArg;
|
|
31
|
-
this.serviceQenv = new plugins.qenv.Qenv(paths.packageDir, this.config.nogitDir);
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
public async start() {
|
|
34
|
+
if (this.started) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
35
38
|
// Ensure configured directories exist
|
|
36
|
-
await plugins.
|
|
37
|
-
await plugins.
|
|
38
|
-
await plugins.
|
|
39
|
+
await plugins.smartfs.directory(this.config.nogitDir).create();
|
|
40
|
+
await plugins.smartfs.directory(this.config.downloadDir).create();
|
|
41
|
+
await plugins.smartfs.directory(this.config.germanBusinessDataDir).create();
|
|
39
42
|
|
|
43
|
+
this.localSmartDb = new plugins.smartdb.LocalSmartDb({
|
|
44
|
+
folderPath: plugins.path.join(this.config.nogitDir, 'opendata-smartdb'),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const connectionInfo = await this.localSmartDb.start();
|
|
40
48
|
this.db = new plugins.smartdata.SmartdataDb({
|
|
41
|
-
mongoDbUrl:
|
|
42
|
-
mongoDbName:
|
|
43
|
-
mongoDbUser: await this.serviceQenv.getEnvVarOnDemand('MONGODB_USER'),
|
|
44
|
-
mongoDbPass: await this.serviceQenv.getEnvVarOnDemand('MONGODB_PASS'),
|
|
49
|
+
mongoDbUrl: connectionInfo.connectionUri,
|
|
50
|
+
mongoDbName: 'opendata',
|
|
45
51
|
});
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
this.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
await this.db.init();
|
|
55
|
+
await this.db.mongoDb.collection('_opendata_bootstrap').insertOne({
|
|
56
|
+
createdAt: new Date(),
|
|
57
|
+
});
|
|
58
|
+
await this.db.mongoDb.collection('_opendata_bootstrap').deleteMany({});
|
|
59
|
+
|
|
60
|
+
this.jsonLDataProcessor = new JsonlDataProcessor(
|
|
61
|
+
this.config.germanBusinessDataDir,
|
|
62
|
+
async (entryArg) => {
|
|
63
|
+
const businessRecord = new this.CBusinessRecord();
|
|
64
|
+
businessRecord.id = await this.CBusinessRecord.getNewId();
|
|
65
|
+
businessRecord.data.name = entryArg.name;
|
|
66
|
+
businessRecord.data.germanParsedRegistration = {
|
|
67
|
+
court: entryArg.all_attributes.registered_office,
|
|
68
|
+
number: entryArg.all_attributes._registerNummer,
|
|
69
|
+
type: entryArg.all_attributes._registerArt as 'HRA' | 'HRB',
|
|
70
|
+
};
|
|
71
|
+
await businessRecord.save();
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
this.handelsregister = new HandelsRegister(this, this.config.downloadDir);
|
|
76
|
+
await this.handelsregister.start();
|
|
77
|
+
this.started = true;
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (this.handelsregister) {
|
|
80
|
+
await this.handelsregister.stop().catch(() => {});
|
|
59
81
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
82
|
+
await this.db.close().catch(() => {});
|
|
83
|
+
await this.localSmartDb.stop().catch(() => {});
|
|
84
|
+
this.localSmartDb = undefined;
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
63
87
|
}
|
|
64
88
|
|
|
65
89
|
public async buildInitialDb() {
|
|
@@ -81,7 +105,16 @@ export class OpenData {
|
|
|
81
105
|
|
|
82
106
|
|
|
83
107
|
public async stop() {
|
|
108
|
+
if (!this.started) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (this.handelsregister) {
|
|
113
|
+
await this.handelsregister.stop();
|
|
114
|
+
}
|
|
84
115
|
await this.db.close();
|
|
85
|
-
await this.
|
|
116
|
+
await this.localSmartDb?.stop();
|
|
117
|
+
this.localSmartDb = undefined;
|
|
118
|
+
this.started = false;
|
|
86
119
|
}
|
|
87
120
|
}
|
package/ts/index.ts
CHANGED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import type { TJurisdiction, TLawSource, TRawLawFormat } from './interfaces.law.js';
|
|
3
|
+
|
|
4
|
+
@plugins.smartdata.Manager()
|
|
5
|
+
export class LawRecord extends plugins.smartdata.SmartDataDbDoc<LawRecord, LawRecord> {
|
|
6
|
+
public static getByLookupKey = async (lookupKeyArg: string) => {
|
|
7
|
+
const lawRecords = await LawRecord.getInstances({
|
|
8
|
+
lookupKey: lookupKeyArg,
|
|
9
|
+
});
|
|
10
|
+
return lawRecords[0];
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
@plugins.smartdata.unI()
|
|
14
|
+
id!: string;
|
|
15
|
+
|
|
16
|
+
@plugins.smartdata.unI()
|
|
17
|
+
@plugins.smartdata.svDb()
|
|
18
|
+
lookupKey!: string;
|
|
19
|
+
|
|
20
|
+
@plugins.smartdata.index()
|
|
21
|
+
@plugins.smartdata.svDb()
|
|
22
|
+
jurisdiction!: TJurisdiction;
|
|
23
|
+
|
|
24
|
+
@plugins.smartdata.index()
|
|
25
|
+
@plugins.smartdata.svDb()
|
|
26
|
+
source!: TLawSource;
|
|
27
|
+
|
|
28
|
+
@plugins.smartdata.searchable()
|
|
29
|
+
@plugins.smartdata.svDb()
|
|
30
|
+
identifier!: string;
|
|
31
|
+
|
|
32
|
+
@plugins.smartdata.searchable()
|
|
33
|
+
@plugins.smartdata.svDb()
|
|
34
|
+
title!: string;
|
|
35
|
+
|
|
36
|
+
@plugins.smartdata.searchable()
|
|
37
|
+
@plugins.smartdata.svDb()
|
|
38
|
+
shortTitle: string = '';
|
|
39
|
+
|
|
40
|
+
@plugins.smartdata.searchable()
|
|
41
|
+
@plugins.smartdata.svDb()
|
|
42
|
+
citation: string = '';
|
|
43
|
+
|
|
44
|
+
@plugins.smartdata.index()
|
|
45
|
+
@plugins.smartdata.svDb()
|
|
46
|
+
type: string = '';
|
|
47
|
+
|
|
48
|
+
@plugins.smartdata.index()
|
|
49
|
+
@plugins.smartdata.svDb()
|
|
50
|
+
language: string = '';
|
|
51
|
+
|
|
52
|
+
@plugins.smartdata.svDb()
|
|
53
|
+
sourceUrl!: string;
|
|
54
|
+
|
|
55
|
+
@plugins.smartdata.svDb()
|
|
56
|
+
rawFormat!: TRawLawFormat;
|
|
57
|
+
|
|
58
|
+
@plugins.smartdata.svDb()
|
|
59
|
+
rawBody!: string;
|
|
60
|
+
|
|
61
|
+
@plugins.smartdata.searchable()
|
|
62
|
+
@plugins.smartdata.svDb()
|
|
63
|
+
text!: string;
|
|
64
|
+
|
|
65
|
+
@plugins.smartdata.index()
|
|
66
|
+
@plugins.smartdata.svDb()
|
|
67
|
+
dateIssued: string = '';
|
|
68
|
+
|
|
69
|
+
@plugins.smartdata.index()
|
|
70
|
+
@plugins.smartdata.svDb()
|
|
71
|
+
lastModified: string = '';
|
|
72
|
+
|
|
73
|
+
@plugins.smartdata.svDb()
|
|
74
|
+
sourceMeta: Record<string, string> = {};
|
|
75
|
+
|
|
76
|
+
@plugins.smartdata.svDb()
|
|
77
|
+
fetchedAt: Date = new Date();
|
|
78
|
+
|
|
79
|
+
@plugins.smartdata.svDb()
|
|
80
|
+
syncedAt: Date = new Date();
|
|
81
|
+
}
|