@justair/justair-library 4.8.27 → 4.8.29
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/package.json +4 -3
- package/src/models/sampleSites.js +17 -0
- package/src/models/samples.js +165 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@justair/justair-library",
|
|
3
|
-
"version": "4.8.
|
|
3
|
+
"version": "4.8.29",
|
|
4
4
|
"description": "JustAir Internal Library",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -17,11 +17,12 @@
|
|
|
17
17
|
"license": "ISC",
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"mongoose": "^7.8.3",
|
|
20
|
+
"nanoid": "^5.1.6",
|
|
20
21
|
"winston": "^3.10.0"
|
|
21
22
|
},
|
|
22
23
|
"devDependencies": {
|
|
23
|
-
"typescript": "^5.3.2",
|
|
24
24
|
"@types/node": "^16.0.0",
|
|
25
|
-
"ts-node": "^10.0.0"
|
|
25
|
+
"ts-node": "^10.0.0",
|
|
26
|
+
"typescript": "^5.3.2"
|
|
26
27
|
}
|
|
27
28
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import mongoose from "mongoose";
|
|
2
|
+
import { customAlphabet } from 'nanoid';
|
|
3
|
+
const generateSiteCode = customAlphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 8);
|
|
4
|
+
|
|
2
5
|
const sampleParametersEnum = [
|
|
3
6
|
"C6H6", // Benzene
|
|
4
7
|
"PB", // Lead
|
|
@@ -39,6 +42,7 @@ const noteSchema = mongoose.Schema({
|
|
|
39
42
|
const sampleSiteAuditSchema = mongoose.Schema(
|
|
40
43
|
{
|
|
41
44
|
sampleSiteId: { type: mongoose.Types.ObjectId, ref: "SampleSites" },
|
|
45
|
+
sampleSiteCode: { type: String }, // reference to SampleSites.code
|
|
42
46
|
orgId: { type: mongoose.Types.ObjectId, ref: "Organizations" },
|
|
43
47
|
timeUpdated: Date,
|
|
44
48
|
deletedAt: { type: Date, default: Date.now }, // Only populated on delete
|
|
@@ -60,6 +64,7 @@ const SampleSiteAudit = mongoose.model("SampleSiteAudit", sampleSiteAuditSchema)
|
|
|
60
64
|
const sampleSitesSchema = mongoose.Schema(
|
|
61
65
|
{
|
|
62
66
|
name: { type: String },
|
|
67
|
+
code: { type: String, unique: true },
|
|
63
68
|
managedBy: { type: String },
|
|
64
69
|
sponsoredBy: { type: String },
|
|
65
70
|
lastSamplePeriodStartDate: { type: Date },
|
|
@@ -108,6 +113,14 @@ const sampleSitesSchema = mongoose.Schema(
|
|
|
108
113
|
}
|
|
109
114
|
);
|
|
110
115
|
|
|
116
|
+
// Update pre-save hook
|
|
117
|
+
sampleSitesSchema.pre('save', function (next) {
|
|
118
|
+
if (!this.code) {
|
|
119
|
+
this.code = generateSiteCode();
|
|
120
|
+
}
|
|
121
|
+
next();
|
|
122
|
+
});
|
|
123
|
+
|
|
111
124
|
// Geographic queries - already exists
|
|
112
125
|
sampleSitesSchema.index({ "location.gps": "2dsphere" }); // use location.gps for geospatial queries
|
|
113
126
|
|
|
@@ -137,6 +150,7 @@ sampleSitesSchema.pre("findOneAndDelete", async function () {
|
|
|
137
150
|
if (coordinates) {
|
|
138
151
|
const auditLog = new SampleSiteAudit({
|
|
139
152
|
sampleSiteId: docToDelete._id,
|
|
153
|
+
sampleSiteCode: docToDelete.code,
|
|
140
154
|
orgId: docToDelete.sponsor,
|
|
141
155
|
timeUpdated: docToDelete.updatedAt,
|
|
142
156
|
sampleSiteLocation: {
|
|
@@ -167,6 +181,7 @@ sampleSitesSchema.pre("deleteMany", async function () {
|
|
|
167
181
|
if (coordinates) {
|
|
168
182
|
return {
|
|
169
183
|
sampleSiteId: doc._id,
|
|
184
|
+
sampleSiteCode: doc.code,
|
|
170
185
|
orgId: doc.sponsor,
|
|
171
186
|
timeUpdated: doc.updatedAt,
|
|
172
187
|
sampleSiteLocation: {
|
|
@@ -201,6 +216,7 @@ sampleSitesSchema.pre("deleteOne", async function () {
|
|
|
201
216
|
if (coordinates) {
|
|
202
217
|
const auditLog = new SampleSiteAudit({
|
|
203
218
|
sampleSiteId: docToDelete._id,
|
|
219
|
+
sampleSiteCode: docToDelete.code,
|
|
204
220
|
orgId: docToDelete.sponsor,
|
|
205
221
|
timeUpdated: docToDelete.updatedAt,
|
|
206
222
|
sampleSiteLocation: {
|
|
@@ -229,6 +245,7 @@ sampleSitesSchema.pre("updateMany", async function () {
|
|
|
229
245
|
if (coordinates) {
|
|
230
246
|
return {
|
|
231
247
|
sampleSiteId: doc._id,
|
|
248
|
+
sampleSiteCode: doc.code,
|
|
232
249
|
orgId: doc.sponsor,
|
|
233
250
|
timeUpdated: doc.updatedAt,
|
|
234
251
|
sampleSiteLocation: {
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
|
|
3
|
+
// Samples Audit Schema
|
|
4
|
+
const samplesAuditSchema = mongoose.Schema(
|
|
5
|
+
{
|
|
6
|
+
siteId: { type: mongoose.Types.ObjectId, ref: "SampleSites" },
|
|
7
|
+
orgId: { type: mongoose.Types.ObjectId, ref: "Organizations" },
|
|
8
|
+
siteCode: { type: String }, // reference to SampleSites.code
|
|
9
|
+
periodStartDate: { type: Date },
|
|
10
|
+
periodEndDate: { type: Date },
|
|
11
|
+
parameterName: { type: String },
|
|
12
|
+
parameterValue: { type: Number },
|
|
13
|
+
parameterUnit: { type: String },
|
|
14
|
+
upperDetectionLimit: { type: Number },
|
|
15
|
+
lowerDetectionLimit: { type: Number },
|
|
16
|
+
deletedAt: { type: Date, default: Date.now }, // Only populated on delete
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
timestamps: true,
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
// Create the SamplesAudit model
|
|
25
|
+
const SamplesAudit = mongoose.model("SamplesAudit", samplesAuditSchema);
|
|
26
|
+
|
|
27
|
+
// Samples Schema
|
|
28
|
+
const samplesSchema = mongoose.Schema(
|
|
29
|
+
{
|
|
30
|
+
siteId: { type: mongoose.Types.ObjectId, ref: "SampleSites" },
|
|
31
|
+
orgId: { type: mongoose.Types.ObjectId, ref: "Organizations" },
|
|
32
|
+
siteCode: { type: String }, // reference to SampleSites.code
|
|
33
|
+
periodStartDate: { type: Date },
|
|
34
|
+
periodEndDate: { type: Date },
|
|
35
|
+
parameterName: { type: String },
|
|
36
|
+
parameterValue: { type: Number },
|
|
37
|
+
parameterUnit: { type: String },
|
|
38
|
+
upperDetectionLimit: { type: Number },
|
|
39
|
+
lowerDetectionLimit: { type: Number },
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
timestamps: true,
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
// Primary query pattern index (descending periodStartDate for latest data first)
|
|
47
|
+
samplesSchema.index({ siteId: 1, periodStartDate: -1 });
|
|
48
|
+
// Pollutant-based queries
|
|
49
|
+
samplesSchema.index({ siteId: 1, parameterName: 1 });
|
|
50
|
+
|
|
51
|
+
// Pre-hook to log single document deletions
|
|
52
|
+
samplesSchema.pre("findOneAndDelete", async function () {
|
|
53
|
+
const docToDelete = await this.model.findOne(this.getFilter()).lean();
|
|
54
|
+
if (docToDelete) {
|
|
55
|
+
console.log("Logging findOneAndDelete to audit", docToDelete);
|
|
56
|
+
const auditLog = new SamplesAudit({
|
|
57
|
+
siteId: docToDelete.siteId,
|
|
58
|
+
orgId: docToDelete.orgId,
|
|
59
|
+
siteCode: docToDelete.siteCode,
|
|
60
|
+
periodStartDate: docToDelete.periodStartDate,
|
|
61
|
+
periodEndDate: docToDelete.periodEndDate,
|
|
62
|
+
parameterName: docToDelete.parameterName,
|
|
63
|
+
parameterValue: docToDelete.parameterValue,
|
|
64
|
+
parameterUnit: docToDelete.parameterUnit,
|
|
65
|
+
upperDetectionLimit: docToDelete.upperDetectionLimit,
|
|
66
|
+
lowerDetectionLimit: docToDelete.lowerDetectionLimit,
|
|
67
|
+
deletedAt: new Date(),
|
|
68
|
+
});
|
|
69
|
+
await auditLog.save();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Pre-hook to log multiple document deletions
|
|
74
|
+
samplesSchema.pre("deleteMany", async function () {
|
|
75
|
+
console.log("deleteMany pre-hook triggered");
|
|
76
|
+
const docsToDelete = await this.model.find(this.getFilter()).lean();
|
|
77
|
+
if (docsToDelete.length) {
|
|
78
|
+
console.log(`Logging ${docsToDelete.length} documents to audit`);
|
|
79
|
+
const auditLogs = docsToDelete.map((doc) => ({
|
|
80
|
+
siteId: doc.siteId,
|
|
81
|
+
orgId: doc.orgId,
|
|
82
|
+
siteCode: doc.siteCode,
|
|
83
|
+
periodStartDate: doc.periodStartDate,
|
|
84
|
+
periodEndDate: doc.periodEndDate,
|
|
85
|
+
parameterName: doc.parameterName,
|
|
86
|
+
parameterValue: doc.parameterValue,
|
|
87
|
+
parameterUnit: doc.parameterUnit,
|
|
88
|
+
upperDetectionLimit: doc.upperDetectionLimit,
|
|
89
|
+
lowerDetectionLimit: doc.lowerDetectionLimit,
|
|
90
|
+
deletedAt: new Date(),
|
|
91
|
+
}));
|
|
92
|
+
await SamplesAudit.insertMany(auditLogs);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Pre-hook to log a single document deletion (for deleteOne)
|
|
97
|
+
samplesSchema.pre("deleteOne", async function () {
|
|
98
|
+
console.log("deleteOne pre-hook triggered");
|
|
99
|
+
const docToDelete = await this.model.findOne(this.getFilter()).lean();
|
|
100
|
+
if (docToDelete) {
|
|
101
|
+
console.log("Logging deleteOne to audit", docToDelete);
|
|
102
|
+
const auditLog = new SamplesAudit({
|
|
103
|
+
siteId: docToDelete.siteId,
|
|
104
|
+
orgId: docToDelete.orgId,
|
|
105
|
+
siteCode: docToDelete.siteCode,
|
|
106
|
+
periodStartDate: docToDelete.periodStartDate,
|
|
107
|
+
periodEndDate: docToDelete.periodEndDate,
|
|
108
|
+
parameterName: docToDelete.parameterName,
|
|
109
|
+
parameterValue: docToDelete.parameterValue,
|
|
110
|
+
parameterUnit: docToDelete.parameterUnit,
|
|
111
|
+
upperDetectionLimit: docToDelete.upperDetectionLimit,
|
|
112
|
+
lowerDetectionLimit: docToDelete.lowerDetectionLimit,
|
|
113
|
+
deletedAt: new Date(),
|
|
114
|
+
});
|
|
115
|
+
await auditLog.save();
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Pre-hook to log single document updates
|
|
120
|
+
samplesSchema.pre("findOneAndUpdate", async function () {
|
|
121
|
+
const docToUpdate = await this.model.findOne(this.getFilter()).lean();
|
|
122
|
+
if (docToUpdate) {
|
|
123
|
+
console.log("Logging findOneAndUpdate to audit", docToUpdate);
|
|
124
|
+
const auditLog = new SamplesAudit({
|
|
125
|
+
siteId: docToUpdate.siteId,
|
|
126
|
+
orgId: docToUpdate.orgId,
|
|
127
|
+
siteCode: docToUpdate.siteCode,
|
|
128
|
+
periodStartDate: docToUpdate.periodStartDate,
|
|
129
|
+
periodEndDate: docToUpdate.periodEndDate,
|
|
130
|
+
parameterName: docToUpdate.parameterName,
|
|
131
|
+
parameterValue: docToUpdate.parameterValue,
|
|
132
|
+
parameterUnit: docToUpdate.parameterUnit,
|
|
133
|
+
upperDetectionLimit: docToUpdate.upperDetectionLimit,
|
|
134
|
+
lowerDetectionLimit: docToUpdate.lowerDetectionLimit,
|
|
135
|
+
deletedAt: null,
|
|
136
|
+
});
|
|
137
|
+
await auditLog.save();
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
// Pre-hook to log multiple document updates
|
|
142
|
+
samplesSchema.pre("updateMany", async function () {
|
|
143
|
+
const docsToUpdate = await this.model.find(this.getFilter()).lean();
|
|
144
|
+
if (docsToUpdate.length) {
|
|
145
|
+
console.log(`Logging ${docsToUpdate.length} documents to audit`);
|
|
146
|
+
const auditLogs = docsToUpdate.map((doc) => ({
|
|
147
|
+
siteId: doc.siteId,
|
|
148
|
+
orgId: doc.orgId,
|
|
149
|
+
siteCode: doc.siteCode,
|
|
150
|
+
periodStartDate: doc.periodStartDate,
|
|
151
|
+
periodEndDate: doc.periodEndDate,
|
|
152
|
+
parameterName: doc.parameterName,
|
|
153
|
+
parameterValue: doc.parameterValue,
|
|
154
|
+
parameterUnit: doc.parameterUnit,
|
|
155
|
+
upperDetectionLimit: doc.upperDetectionLimit,
|
|
156
|
+
lowerDetectionLimit: doc.lowerDetectionLimit,
|
|
157
|
+
deletedAt: null,
|
|
158
|
+
}));
|
|
159
|
+
await SamplesAudit.insertMany(auditLogs);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Create the SampleSites model
|
|
164
|
+
const Samples = mongoose.model("Samples", samplesSchema);
|
|
165
|
+
export { samplesSchema, Samples, SamplesAudit, samplesAuditSchema };
|