Skip to content
This repository was archived by the owner on Jan 9, 2023. It is now read-only.

Commit 24c4a53

Browse files
committed
Added DB to CSV utility
1 parent 9a69f7a commit 24c4a53

File tree

2 files changed

+246
-0
lines changed

2 files changed

+246
-0
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,13 @@ The csv fields that are supported are as follows:
8181
- **vendorItemNo** (optional) - Vendor item number
8282
8383
The first row of the csv file needs to have the columnName as specified above so that the import tool knows which value is in which column.
84+
85+
## DB to CSV
86+
There is a utility located under utils/db-to-csv.js that will allow you to dump the database to CSV. To use it, run the following command
87+
`node utils/db-dump.js <type> <filename> <dbname>`, eg `node utils/db-dump.js inventory`
88+
89+
The following parameters are supported:
90+
91+
- **type** (required) - the type of object to dump from the database (eg inventory). For a list of available types, run `node utils/db-to-csv.js --types`
92+
- **filename** (optional) - the name of the file to write to. If not specifed, this will default to type.csv (eg inventory.csv for inventory type).
93+
- **dbname** (optional) - the name of the database to dump from. If not specifed, this will default to the main database for HospitalRun.

utils/db-to-csv.js

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
const config = require('../config.js');
2+
const fs = require('fs');
3+
const nano = require('nano')(config.couchAuthDbURL);
4+
const relPouch = require('relational-pouch');
5+
const stringify = require('csv-stringify');
6+
7+
const ALLOWED_TYPES = [
8+
'allergy',
9+
'appointment',
10+
'attachment',
11+
'billingLineItem',
12+
'customForm',
13+
'diagnosis',
14+
'imaging',
15+
'incCategory',
16+
'incident',
17+
'incidentNote',
18+
'invLocation',
19+
'invPurchase',
20+
'invRequest',
21+
'inventory',
22+
'invoice',
23+
'lab',
24+
'lineItemDetail',
25+
'lookup',
26+
'medication',
27+
'operationReport',
28+
'operativePlan',
29+
'option',
30+
'overridePrice',
31+
'patient',
32+
'patientNote',
33+
'payment',
34+
'photo',
35+
'priceProfile',
36+
'pricing',
37+
'procCharge',
38+
'procedure',
39+
'report',
40+
'sequence',
41+
'userRole',
42+
'visit',
43+
'vital'
44+
];
45+
46+
const RELATIONSHIPS = {
47+
allergy: {
48+
patient: 'patient',
49+
},
50+
appointment: {
51+
patient: 'patient',
52+
visits: 'visit'
53+
},
54+
billingLineItem: {
55+
details: 'lineItemDetail'
56+
},
57+
imaging: {
58+
charges: 'procCharge',
59+
imagingType: 'pricing',
60+
patient: 'patient',
61+
visit: 'visit'
62+
},
63+
incident: {
64+
incidentAttachments: 'attachment',
65+
notes: 'incidentNote',
66+
patient: 'patient'
67+
},
68+
invPurchase: {
69+
inventoryItem: 'inventory'
70+
},
71+
invRequest: {
72+
inventoryItem: 'inventory',
73+
patient: 'patient',
74+
visit: 'visit'
75+
},
76+
inventory: {
77+
locations: 'invLocation',
78+
purchases: 'invPurchase'
79+
},
80+
invoice: {
81+
lineItems: 'billingLineItem',
82+
patient: 'patient',
83+
paymentProfile: 'priceProfile',
84+
payments: 'payment',
85+
visit: 'visit'
86+
},
87+
lab: {
88+
charges: 'procCharge',
89+
labType: 'pricing',
90+
patient: 'patient',
91+
visit: 'visit'
92+
},
93+
lineItemDetail: {
94+
pricingItem: 'pricing'
95+
},
96+
medication: {
97+
inventoryItem: 'inventory',
98+
patient: 'patient',
99+
visit: 'visit',
100+
},
101+
operationReport: {
102+
diagnoses: 'diagnosis',
103+
operativePlan: 'operativePlan',
104+
patient: 'patient',
105+
preOpDiagnoses: 'diagnosis'
106+
},
107+
operativePlan: {
108+
diagnoses: 'diagnosis',
109+
patient: 'patient',
110+
profile: 'priceProfile'
111+
},
112+
patientNote: {
113+
patient: 'patient',
114+
visit: 'visit'
115+
},
116+
patient: {
117+
allergies: 'allergy',
118+
diagnoses: 'diagnosis',
119+
operationReports: 'operationReport',
120+
operativePlans: 'operativePlan',
121+
paymentProfile: 'priceProfile',
122+
payments: 'payment',
123+
invoice: 'invoice'
124+
},
125+
photo: {
126+
patient: 'patient'
127+
},
128+
pricing: {
129+
pricingOverrides: 'overridePrice'
130+
},
131+
procCharge: {
132+
medication: 'inventory',
133+
pricingItem: 'pricing'
134+
},
135+
procedure: {
136+
charges: 'procCharge',
137+
visit: 'visit'
138+
},
139+
report: {
140+
visit: 'visit'
141+
},
142+
visit: {
143+
charges: 'procCharge',
144+
diagnoses: 'diagnosis',
145+
imaging: 'imaging',
146+
labs: 'lab',
147+
medication: 'medication',
148+
patient: 'patient',
149+
patientNotes: 'patientNote',
150+
procedures: 'procedure',
151+
reports: 'report',
152+
vitals: 'vital'
153+
}
154+
};
155+
156+
relPouch.setSchema([]);
157+
function mapId(type, id) {
158+
return relPouch.rel.makeDocID({
159+
id,
160+
type
161+
});
162+
}
163+
164+
let filename;
165+
let dbname = 'main';
166+
let type;
167+
168+
if (process.argv.length < 3) {
169+
console.log('Usage: node db-dump.js <type> <filename> <dbname>');
170+
process.exit();
171+
} else {
172+
type = process.argv[2];
173+
if (ALLOWED_TYPES.indexOf(type) === -1) {
174+
if (type !== '--types') {
175+
console.log(`${type} is not a valid type.`);
176+
}
177+
console.log(`Valid types are: ${ALLOWED_TYPES.join(', ')}`);
178+
process.exit();
179+
}
180+
181+
if (process.argv.length > 3) {
182+
filename = process.argv[3];
183+
} else {
184+
filename = type;
185+
}
186+
if (filename.indexOf('.') === -1) {
187+
filename = `${filename}.csv`;
188+
}
189+
if (process.argv.length > 4) {
190+
dbname = process.argv[4];
191+
}
192+
}
193+
194+
let queryParams = {
195+
endkey: mapId(type, {}),
196+
include_docs: true,
197+
startkey: mapId(type)
198+
};
199+
var maindb = nano.use(dbname);
200+
maindb.list(queryParams, (err, body) => {
201+
let dumpRows = body.rows.map((row) => {
202+
let parsedId = relPouch.rel.parseDocID( row.doc._id);
203+
let relationshipMap = RELATIONSHIPS[parsedId.type];
204+
if (!relationshipMap) {
205+
relationshipMap = {};
206+
}
207+
let fieldNames = Object.keys(row.doc.data);
208+
let returnObject = {
209+
id: row.doc._id
210+
};
211+
fieldNames.forEach((fieldName) => {
212+
returnObject[fieldName] = row.doc.data[fieldName];
213+
if (relationshipMap[fieldName]) {
214+
if (Array.isArray(returnObject[fieldName])) {
215+
returnObject[fieldName] = returnObject[fieldName].map((id) => {
216+
return mapId(relationshipMap[fieldName], id);
217+
});
218+
} else {
219+
returnObject[fieldName] = mapId(relationshipMap[fieldName], returnObject[fieldName]);
220+
}
221+
}
222+
});
223+
return returnObject;
224+
});
225+
console.log(`Writing ${dumpRows.length} ${type} records to ${filename}`);
226+
let writer = fs.createWriteStream(filename);
227+
writer.on('close', () => {
228+
console.error(`The file ${filename} has been created. Bytes written: ${writer.bytesWritten} `);
229+
process.exit();
230+
});
231+
stringify(dumpRows, {
232+
header: true,
233+
quoted: true
234+
}).pipe(writer);
235+
236+
});

0 commit comments

Comments
 (0)