@justair/justair-library 4.8.23 → 4.8.24-alpha-demo
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 +27 -27
- package/src/index.js +133 -133
- package/src/models/measurements.js +2 -0
- package/src/models/sampleSites.js +254 -254
package/package.json
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@justair/justair-library",
|
|
3
|
-
"version": "4.8.
|
|
4
|
-
"description": "JustAir Internal Library",
|
|
5
|
-
"main": "src/index.js",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"scripts": {
|
|
9
|
-
"test": "echo \"Error: no test specified\" && exit 1",
|
|
10
|
-
"build": "tsc",
|
|
11
|
-
"prepublish": "npm run build"
|
|
12
|
-
},
|
|
13
|
-
"keywords": [
|
|
14
|
-
"JustAir"
|
|
15
|
-
],
|
|
16
|
-
"author": "Britney Epps",
|
|
17
|
-
"license": "ISC",
|
|
18
|
-
"dependencies": {
|
|
19
|
-
"mongoose": "^7.8.3",
|
|
20
|
-
"winston": "^3.10.0"
|
|
21
|
-
},
|
|
22
|
-
"devDependencies": {
|
|
23
|
-
"typescript": "^5.3.2",
|
|
24
|
-
"@types/node": "^16.0.0",
|
|
25
|
-
"ts-node": "^10.0.0"
|
|
26
|
-
}
|
|
27
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@justair/justair-library",
|
|
3
|
+
"version": "4.8.24-alpha-demo",
|
|
4
|
+
"description": "JustAir Internal Library",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"prepublish": "npm run build"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"JustAir"
|
|
15
|
+
],
|
|
16
|
+
"author": "Britney Epps",
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"mongoose": "^7.8.3",
|
|
20
|
+
"winston": "^3.10.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"typescript": "^5.3.2",
|
|
24
|
+
"@types/node": "^16.0.0",
|
|
25
|
+
"ts-node": "^10.0.0"
|
|
26
|
+
}
|
|
27
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,133 +1,133 @@
|
|
|
1
|
-
import { adminSchema, Admin } from "./models/admin.js";
|
|
2
|
-
import {
|
|
3
|
-
configurationsSchema,
|
|
4
|
-
Configurations,
|
|
5
|
-
} from "./models/configurations.js";
|
|
6
|
-
import {
|
|
7
|
-
measurementsSchema,
|
|
8
|
-
Measurements,
|
|
9
|
-
Audit,
|
|
10
|
-
auditSchema,
|
|
11
|
-
} from "./models/measurements.js";
|
|
12
|
-
import {
|
|
13
|
-
monitorRequestsSchema,
|
|
14
|
-
MonitorRequests,
|
|
15
|
-
} from "./models/monitorRequests.js";
|
|
16
|
-
import {
|
|
17
|
-
monitorsSchema,
|
|
18
|
-
Monitors,
|
|
19
|
-
monitorAuditSchema,
|
|
20
|
-
MonitorAudit,
|
|
21
|
-
parametersEnum,
|
|
22
|
-
} from "./models/monitors.js";
|
|
23
|
-
import { organizationsSchema, Organizations } from "./models/organizations.js";
|
|
24
|
-
import {
|
|
25
|
-
referenceMonitorInfoSchema,
|
|
26
|
-
ReferenceMonitorInfo,
|
|
27
|
-
} from "./models/referenceMonitorInfo.js";
|
|
28
|
-
import { lightMonitorSchema, LightMonitors } from "./models/lightmonitors.js";
|
|
29
|
-
import {
|
|
30
|
-
monitorSuppliersSchema,
|
|
31
|
-
MonitorSuppliers,
|
|
32
|
-
} from "./models/monitorSuppliers.js";
|
|
33
|
-
import { contextsSchema, Contexts } from "./models/contexts.js";
|
|
34
|
-
import { parametersSchema, Parameters } from "./models/parameters.js";
|
|
35
|
-
import { usersSchema, Users } from "./models/users.js";
|
|
36
|
-
import {
|
|
37
|
-
eventsSchema,
|
|
38
|
-
Events,
|
|
39
|
-
eventsAuditSchema,
|
|
40
|
-
EventsAudit,
|
|
41
|
-
} from "./models/events.js";
|
|
42
|
-
import { announcementSchema, Announcements } from "./models/announcements.js";
|
|
43
|
-
import { jobsSchema, Jobs } from "./models/jobs.js";
|
|
44
|
-
import { apiKeySchema, ApiKey } from "./models/apiKey.js";
|
|
45
|
-
import { UsageMetrics, usageMetricsSchema } from "./models/usageMetrics.js";
|
|
46
|
-
import {
|
|
47
|
-
AlertsAudit,
|
|
48
|
-
Alerts,
|
|
49
|
-
alertsSchema,
|
|
50
|
-
alertsAuditSchema,
|
|
51
|
-
} from "./models/alerts.js";
|
|
52
|
-
import { Features, featuresSchema } from "./models/features.js";
|
|
53
|
-
import {
|
|
54
|
-
DataCompleteness,
|
|
55
|
-
dataCompletenessSchema,
|
|
56
|
-
} from "./models/dataCompleteness.js";
|
|
57
|
-
import { NetworkMetrics, networkMetricsSchema } from "./models/networkMetrics.js";
|
|
58
|
-
import Database from "./config/db.js"; // Import the new Database class
|
|
59
|
-
import CustomLogger from "./config/logger.js";
|
|
60
|
-
|
|
61
|
-
// SampleSites related imports
|
|
62
|
-
import {
|
|
63
|
-
sampleSitesSchema,
|
|
64
|
-
SampleSites,
|
|
65
|
-
sampleSiteAuditSchema,
|
|
66
|
-
SampleSiteAudit,
|
|
67
|
-
sampleParametersEnum,
|
|
68
|
-
} from "./models/sampleSites.js";
|
|
69
|
-
|
|
70
|
-
export function createLoggerInstance({ DATADOG_API_KEY, APPLICATION_NAME }) {
|
|
71
|
-
return new CustomLogger({ DATADOG_API_KEY, APPLICATION_NAME });
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export {
|
|
75
|
-
Database, // Export the Database class
|
|
76
|
-
adminSchema,
|
|
77
|
-
Admin,
|
|
78
|
-
configurationsSchema,
|
|
79
|
-
Configurations,
|
|
80
|
-
measurementsSchema,
|
|
81
|
-
Measurements,
|
|
82
|
-
monitorRequestsSchema,
|
|
83
|
-
MonitorRequests,
|
|
84
|
-
monitorsSchema,
|
|
85
|
-
Monitors,
|
|
86
|
-
organizationsSchema,
|
|
87
|
-
Organizations,
|
|
88
|
-
referenceMonitorInfoSchema,
|
|
89
|
-
ReferenceMonitorInfo,
|
|
90
|
-
usersSchema,
|
|
91
|
-
Users,
|
|
92
|
-
eventsSchema,
|
|
93
|
-
Events,
|
|
94
|
-
lightMonitorSchema,
|
|
95
|
-
LightMonitors,
|
|
96
|
-
monitorSuppliersSchema,
|
|
97
|
-
MonitorSuppliers,
|
|
98
|
-
contextsSchema,
|
|
99
|
-
Contexts,
|
|
100
|
-
parametersSchema,
|
|
101
|
-
Parameters,
|
|
102
|
-
announcementSchema,
|
|
103
|
-
Announcements,
|
|
104
|
-
jobsSchema,
|
|
105
|
-
Jobs,
|
|
106
|
-
apiKeySchema,
|
|
107
|
-
ApiKey,
|
|
108
|
-
UsageMetrics,
|
|
109
|
-
usageMetricsSchema,
|
|
110
|
-
Audit,
|
|
111
|
-
auditSchema,
|
|
112
|
-
EventsAudit,
|
|
113
|
-
eventsAuditSchema,
|
|
114
|
-
MonitorAudit,
|
|
115
|
-
monitorAuditSchema,
|
|
116
|
-
parametersEnum,
|
|
117
|
-
AlertsAudit,
|
|
118
|
-
Alerts,
|
|
119
|
-
alertsSchema,
|
|
120
|
-
alertsAuditSchema,
|
|
121
|
-
Features,
|
|
122
|
-
featuresSchema,
|
|
123
|
-
dataCompletenessSchema,
|
|
124
|
-
DataCompleteness,
|
|
125
|
-
networkMetricsSchema,
|
|
126
|
-
NetworkMetrics,
|
|
127
|
-
// SampleSites related exports
|
|
128
|
-
sampleSitesSchema,
|
|
129
|
-
SampleSites,
|
|
130
|
-
sampleSiteAuditSchema,
|
|
131
|
-
SampleSiteAudit,
|
|
132
|
-
sampleParametersEnum,
|
|
133
|
-
};
|
|
1
|
+
import { adminSchema, Admin } from "./models/admin.js";
|
|
2
|
+
import {
|
|
3
|
+
configurationsSchema,
|
|
4
|
+
Configurations,
|
|
5
|
+
} from "./models/configurations.js";
|
|
6
|
+
import {
|
|
7
|
+
measurementsSchema,
|
|
8
|
+
Measurements,
|
|
9
|
+
Audit,
|
|
10
|
+
auditSchema,
|
|
11
|
+
} from "./models/measurements.js";
|
|
12
|
+
import {
|
|
13
|
+
monitorRequestsSchema,
|
|
14
|
+
MonitorRequests,
|
|
15
|
+
} from "./models/monitorRequests.js";
|
|
16
|
+
import {
|
|
17
|
+
monitorsSchema,
|
|
18
|
+
Monitors,
|
|
19
|
+
monitorAuditSchema,
|
|
20
|
+
MonitorAudit,
|
|
21
|
+
parametersEnum,
|
|
22
|
+
} from "./models/monitors.js";
|
|
23
|
+
import { organizationsSchema, Organizations } from "./models/organizations.js";
|
|
24
|
+
import {
|
|
25
|
+
referenceMonitorInfoSchema,
|
|
26
|
+
ReferenceMonitorInfo,
|
|
27
|
+
} from "./models/referenceMonitorInfo.js";
|
|
28
|
+
import { lightMonitorSchema, LightMonitors } from "./models/lightmonitors.js";
|
|
29
|
+
import {
|
|
30
|
+
monitorSuppliersSchema,
|
|
31
|
+
MonitorSuppliers,
|
|
32
|
+
} from "./models/monitorSuppliers.js";
|
|
33
|
+
import { contextsSchema, Contexts } from "./models/contexts.js";
|
|
34
|
+
import { parametersSchema, Parameters } from "./models/parameters.js";
|
|
35
|
+
import { usersSchema, Users } from "./models/users.js";
|
|
36
|
+
import {
|
|
37
|
+
eventsSchema,
|
|
38
|
+
Events,
|
|
39
|
+
eventsAuditSchema,
|
|
40
|
+
EventsAudit,
|
|
41
|
+
} from "./models/events.js";
|
|
42
|
+
import { announcementSchema, Announcements } from "./models/announcements.js";
|
|
43
|
+
import { jobsSchema, Jobs } from "./models/jobs.js";
|
|
44
|
+
import { apiKeySchema, ApiKey } from "./models/apiKey.js";
|
|
45
|
+
import { UsageMetrics, usageMetricsSchema } from "./models/usageMetrics.js";
|
|
46
|
+
import {
|
|
47
|
+
AlertsAudit,
|
|
48
|
+
Alerts,
|
|
49
|
+
alertsSchema,
|
|
50
|
+
alertsAuditSchema,
|
|
51
|
+
} from "./models/alerts.js";
|
|
52
|
+
import { Features, featuresSchema } from "./models/features.js";
|
|
53
|
+
import {
|
|
54
|
+
DataCompleteness,
|
|
55
|
+
dataCompletenessSchema,
|
|
56
|
+
} from "./models/dataCompleteness.js";
|
|
57
|
+
import { NetworkMetrics, networkMetricsSchema } from "./models/networkMetrics.js";
|
|
58
|
+
import Database from "./config/db.js"; // Import the new Database class
|
|
59
|
+
import CustomLogger from "./config/logger.js";
|
|
60
|
+
|
|
61
|
+
// SampleSites related imports
|
|
62
|
+
import {
|
|
63
|
+
sampleSitesSchema,
|
|
64
|
+
SampleSites,
|
|
65
|
+
sampleSiteAuditSchema,
|
|
66
|
+
SampleSiteAudit,
|
|
67
|
+
sampleParametersEnum,
|
|
68
|
+
} from "./models/sampleSites.js";
|
|
69
|
+
|
|
70
|
+
export function createLoggerInstance({ DATADOG_API_KEY, APPLICATION_NAME }) {
|
|
71
|
+
return new CustomLogger({ DATADOG_API_KEY, APPLICATION_NAME });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export {
|
|
75
|
+
Database, // Export the Database class
|
|
76
|
+
adminSchema,
|
|
77
|
+
Admin,
|
|
78
|
+
configurationsSchema,
|
|
79
|
+
Configurations,
|
|
80
|
+
measurementsSchema,
|
|
81
|
+
Measurements,
|
|
82
|
+
monitorRequestsSchema,
|
|
83
|
+
MonitorRequests,
|
|
84
|
+
monitorsSchema,
|
|
85
|
+
Monitors,
|
|
86
|
+
organizationsSchema,
|
|
87
|
+
Organizations,
|
|
88
|
+
referenceMonitorInfoSchema,
|
|
89
|
+
ReferenceMonitorInfo,
|
|
90
|
+
usersSchema,
|
|
91
|
+
Users,
|
|
92
|
+
eventsSchema,
|
|
93
|
+
Events,
|
|
94
|
+
lightMonitorSchema,
|
|
95
|
+
LightMonitors,
|
|
96
|
+
monitorSuppliersSchema,
|
|
97
|
+
MonitorSuppliers,
|
|
98
|
+
contextsSchema,
|
|
99
|
+
Contexts,
|
|
100
|
+
parametersSchema,
|
|
101
|
+
Parameters,
|
|
102
|
+
announcementSchema,
|
|
103
|
+
Announcements,
|
|
104
|
+
jobsSchema,
|
|
105
|
+
Jobs,
|
|
106
|
+
apiKeySchema,
|
|
107
|
+
ApiKey,
|
|
108
|
+
UsageMetrics,
|
|
109
|
+
usageMetricsSchema,
|
|
110
|
+
Audit,
|
|
111
|
+
auditSchema,
|
|
112
|
+
EventsAudit,
|
|
113
|
+
eventsAuditSchema,
|
|
114
|
+
MonitorAudit,
|
|
115
|
+
monitorAuditSchema,
|
|
116
|
+
parametersEnum,
|
|
117
|
+
AlertsAudit,
|
|
118
|
+
Alerts,
|
|
119
|
+
alertsSchema,
|
|
120
|
+
alertsAuditSchema,
|
|
121
|
+
Features,
|
|
122
|
+
featuresSchema,
|
|
123
|
+
dataCompletenessSchema,
|
|
124
|
+
DataCompleteness,
|
|
125
|
+
networkMetricsSchema,
|
|
126
|
+
NetworkMetrics,
|
|
127
|
+
// SampleSites related exports
|
|
128
|
+
sampleSitesSchema,
|
|
129
|
+
SampleSites,
|
|
130
|
+
sampleSiteAuditSchema,
|
|
131
|
+
SampleSiteAudit,
|
|
132
|
+
sampleParametersEnum,
|
|
133
|
+
};
|
|
@@ -158,6 +158,7 @@ measurementsSchema.pre("findOneAndDelete", async function () {
|
|
|
158
158
|
});
|
|
159
159
|
|
|
160
160
|
// Pre-hook to log multiple document deletions
|
|
161
|
+
/*
|
|
161
162
|
measurementsSchema.pre("deleteMany", async function () {
|
|
162
163
|
console.log("deleteMany pre-hook triggered");
|
|
163
164
|
const docsToDelete = await this.model.find(this.getFilter()).lean();
|
|
@@ -183,6 +184,7 @@ measurementsSchema.pre("deleteMany", async function () {
|
|
|
183
184
|
await Audit.insertMany(auditLogs);
|
|
184
185
|
}
|
|
185
186
|
});
|
|
187
|
+
*/
|
|
186
188
|
|
|
187
189
|
// Pre-hook to log a single document deletion (for deleteOne)
|
|
188
190
|
measurementsSchema.pre("deleteOne", async function () {
|
|
@@ -1,255 +1,255 @@
|
|
|
1
|
-
import mongoose from "mongoose";
|
|
2
|
-
const sampleParametersEnum = [
|
|
3
|
-
"C6H6", // Benzene
|
|
4
|
-
"PB", // Lead
|
|
5
|
-
"AS", // Arsenic
|
|
6
|
-
"CD", // Cadmium
|
|
7
|
-
"CR", // Chromium
|
|
8
|
-
"NI", // Nickel
|
|
9
|
-
"BA", // Barium
|
|
10
|
-
"FE", // Iron
|
|
11
|
-
"CU", // Copper
|
|
12
|
-
"ZN", // Zinc
|
|
13
|
-
];
|
|
14
|
-
|
|
15
|
-
const noteSchema = mongoose.Schema({
|
|
16
|
-
note: {
|
|
17
|
-
type: String,
|
|
18
|
-
required: true,
|
|
19
|
-
},
|
|
20
|
-
type: {
|
|
21
|
-
type: String,
|
|
22
|
-
required: true,
|
|
23
|
-
},
|
|
24
|
-
adminId: {
|
|
25
|
-
type: mongoose.Types.ObjectId,
|
|
26
|
-
ref: "Admin",
|
|
27
|
-
required: true,
|
|
28
|
-
},
|
|
29
|
-
adminName: {
|
|
30
|
-
type: String,
|
|
31
|
-
},
|
|
32
|
-
date: {
|
|
33
|
-
type: Date,
|
|
34
|
-
default: Date.now,
|
|
35
|
-
},
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// Sample Site Audit Schema
|
|
39
|
-
const sampleSiteAuditSchema = mongoose.Schema(
|
|
40
|
-
{
|
|
41
|
-
sampleSiteId: { type: mongoose.Types.ObjectId, ref: "SampleSites" },
|
|
42
|
-
orgId: { type: mongoose.Types.ObjectId, ref: "Organizations" },
|
|
43
|
-
timeUpdated: Date,
|
|
44
|
-
deletedAt: { type: Date, default: Date.now }, // Only populated on delete
|
|
45
|
-
sampleSiteLocation: {
|
|
46
|
-
// Sample Site GPS location (lat, lon)
|
|
47
|
-
type: { type: String, enum: ["Point"], required: true },
|
|
48
|
-
coordinates: {
|
|
49
|
-
latitude: { type: Number, required: true },
|
|
50
|
-
longitude: { type: Number, required: true }
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
timestamps: true,
|
|
56
|
-
}
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
// Create the SampleSiteAudit model
|
|
60
|
-
const SampleSiteAudit = mongoose.model("SampleSiteAudit", sampleSiteAuditSchema);
|
|
61
|
-
|
|
62
|
-
// Sample Sites Schema
|
|
63
|
-
const sampleSitesSchema = mongoose.Schema(
|
|
64
|
-
{
|
|
65
|
-
name: { type: String },
|
|
66
|
-
managedBy: { type: String },
|
|
67
|
-
sponsoredBy: { type: String },
|
|
68
|
-
lastSamplePeriodStartDate: { type: Date },
|
|
69
|
-
lastSamplePeriodEndDate: { type: Date },
|
|
70
|
-
isPrivate: { type: Boolean, default: false, required: true },
|
|
71
|
-
sponsor: { type: mongoose.Types.ObjectId, ref: "Organizations" },
|
|
72
|
-
sponsorName: { type: String },
|
|
73
|
-
/*
|
|
74
|
-
* Location object: unified structure for all address and geospatial data.
|
|
75
|
-
* - monitorLatitude and monitorLongitude are deprecated and no longer used.
|
|
76
|
-
* - The location object (including location.gps) is now built in config/google.js.
|
|
77
|
-
* - location.gps is a GeoJSON Point used for all geospatial queries (replaces gpsLocation).
|
|
78
|
-
*/
|
|
79
|
-
location: {
|
|
80
|
-
city: { type: String },
|
|
81
|
-
state: { type: String },
|
|
82
|
-
county: { type: String },
|
|
83
|
-
street: { type: String },
|
|
84
|
-
zip_code: { type: String },
|
|
85
|
-
neighborhood: { type: String },
|
|
86
|
-
gps: {
|
|
87
|
-
type: {
|
|
88
|
-
type: String,
|
|
89
|
-
enum: ["Point"],
|
|
90
|
-
required: true,
|
|
91
|
-
},
|
|
92
|
-
coordinates: {
|
|
93
|
-
latitude: { type: Number, required: true },
|
|
94
|
-
longitude: { type: Number, required: true }
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
context: { type: [String] },
|
|
99
|
-
parameters: [
|
|
100
|
-
{
|
|
101
|
-
type: String,
|
|
102
|
-
enum: sampleParametersEnum,
|
|
103
|
-
},
|
|
104
|
-
],
|
|
105
|
-
notes: { type: [noteSchema] },
|
|
106
|
-
image: { type: String },
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
timestamps: true,
|
|
110
|
-
}
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
// Geographic queries - already exists
|
|
114
|
-
sampleSitesSchema.index({ "location.gps": "2dsphere" }); // use location.gps for geospatial queries
|
|
115
|
-
|
|
116
|
-
// Sponsor-based queries
|
|
117
|
-
sampleSitesSchema.index({ sponsor: 1, isPrivate: 1 });
|
|
118
|
-
|
|
119
|
-
// Location-based filtering
|
|
120
|
-
sampleSitesSchema.index({ "location.city": 1, "location.state": 1 });
|
|
121
|
-
sampleSitesSchema.index({ "location.neighborhood": 1 });
|
|
122
|
-
sampleSitesSchema.index({ "location.county": 1 });
|
|
123
|
-
|
|
124
|
-
// Query parameter filtering
|
|
125
|
-
sampleSitesSchema.index({ context: 1 });
|
|
126
|
-
sampleSitesSchema.index({ parameters: 1 });
|
|
127
|
-
|
|
128
|
-
// Pre-hook to log single document deletions
|
|
129
|
-
sampleSitesSchema.pre("findOneAndDelete", async function () {
|
|
130
|
-
const docToDelete = await this.model.findOne(this.getFilter()).lean();
|
|
131
|
-
if (docToDelete) {
|
|
132
|
-
console.log("Logging findOneAndDelete to sample site audit", docToDelete);
|
|
133
|
-
let coordinates = null;
|
|
134
|
-
if (docToDelete.location && docToDelete.location.gps && Array.isArray(docToDelete.location.gps.coordinates)) {
|
|
135
|
-
coordinates = docToDelete.location.gps.coordinates;
|
|
136
|
-
} else if (docToDelete.gpsLocation && Array.isArray(docToDelete.gpsLocation.coordinates)) {
|
|
137
|
-
coordinates = docToDelete.gpsLocation.coordinates;
|
|
138
|
-
}
|
|
139
|
-
if (coordinates) {
|
|
140
|
-
const auditLog = new SampleSiteAudit({
|
|
141
|
-
sampleSiteId: docToDelete._id,
|
|
142
|
-
orgId: docToDelete.sponsor,
|
|
143
|
-
timeUpdated: docToDelete.updatedAt,
|
|
144
|
-
sampleSiteLocation: {
|
|
145
|
-
type: "Point",
|
|
146
|
-
coordinates,
|
|
147
|
-
},
|
|
148
|
-
deletedAt: new Date(),
|
|
149
|
-
});
|
|
150
|
-
await auditLog.save();
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
// Pre-hook to log multiple document deletions
|
|
156
|
-
sampleSitesSchema.pre("deleteMany", async function () {
|
|
157
|
-
console.log("deleteMany pre-hook triggered for sample sites");
|
|
158
|
-
const docsToDelete = await this.model.find(this.getFilter()).lean();
|
|
159
|
-
|
|
160
|
-
if (docsToDelete.length) {
|
|
161
|
-
console.log(`Logging ${docsToDelete.length} sample site documents to audit`);
|
|
162
|
-
const auditLogs = docsToDelete.map((doc) => {
|
|
163
|
-
let coordinates = null;
|
|
164
|
-
if (doc.location && doc.location.gps && Array.isArray(doc.location.gps.coordinates)) {
|
|
165
|
-
coordinates = doc.location.gps.coordinates;
|
|
166
|
-
} else if (doc.gpsLocation && Array.isArray(doc.gpsLocation.coordinates)) {
|
|
167
|
-
coordinates = doc.gpsLocation.coordinates;
|
|
168
|
-
}
|
|
169
|
-
if (coordinates) {
|
|
170
|
-
return {
|
|
171
|
-
sampleSiteId: doc._id,
|
|
172
|
-
orgId: doc.sponsor,
|
|
173
|
-
timeUpdated: doc.updatedAt,
|
|
174
|
-
sampleSiteLocation: {
|
|
175
|
-
type: "Point",
|
|
176
|
-
coordinates,
|
|
177
|
-
},
|
|
178
|
-
deletedAt: new Date(),
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
return null;
|
|
182
|
-
}).filter(Boolean);
|
|
183
|
-
|
|
184
|
-
if (auditLogs.length) {
|
|
185
|
-
await SampleSiteAudit.insertMany(auditLogs);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
// Pre-hook to log a single document deletion (for deleteOne)
|
|
191
|
-
sampleSitesSchema.pre("deleteOne", async function () {
|
|
192
|
-
console.log("deleteOne pre-hook triggered for sample sites");
|
|
193
|
-
const docToDelete = await this.model.findOne(this.getFilter()).lean();
|
|
194
|
-
|
|
195
|
-
if (docToDelete) {
|
|
196
|
-
console.log("Logging deleteOne to sample site audit", docToDelete);
|
|
197
|
-
let coordinates = null;
|
|
198
|
-
if (docToDelete.location && docToDelete.location.gps && Array.isArray(docToDelete.location.gps.coordinates)) {
|
|
199
|
-
coordinates = docToDelete.location.gps.coordinates;
|
|
200
|
-
} else if (docToDelete.gpsLocation && Array.isArray(docToDelete.gpsLocation.coordinates)) {
|
|
201
|
-
coordinates = docToDelete.gpsLocation.coordinates;
|
|
202
|
-
}
|
|
203
|
-
if (coordinates) {
|
|
204
|
-
const auditLog = new SampleSiteAudit({
|
|
205
|
-
sampleSiteId: docToDelete._id,
|
|
206
|
-
orgId: docToDelete.sponsor,
|
|
207
|
-
timeUpdated: docToDelete.updatedAt,
|
|
208
|
-
sampleSiteLocation: {
|
|
209
|
-
type: "Point",
|
|
210
|
-
coordinates,
|
|
211
|
-
},
|
|
212
|
-
deletedAt: new Date(),
|
|
213
|
-
});
|
|
214
|
-
await auditLog.save();
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
// Pre-hook to log multiple document updates
|
|
220
|
-
sampleSitesSchema.pre("updateMany", async function () {
|
|
221
|
-
const docsToUpdate = await this.model.find(this.getFilter()).lean();
|
|
222
|
-
if (docsToUpdate.length) {
|
|
223
|
-
console.log(`Logging ${docsToUpdate.length} sample site documents to audit`);
|
|
224
|
-
const auditLogs = docsToUpdate.map((doc) => {
|
|
225
|
-
let coordinates = null;
|
|
226
|
-
if (doc.location && doc.location.gps && Array.isArray(doc.location.gps.coordinates)) {
|
|
227
|
-
coordinates = doc.location.gps.coordinates;
|
|
228
|
-
} else if (doc.gpsLocation && Array.isArray(doc.gpsLocation.coordinates)) {
|
|
229
|
-
coordinates = doc.gpsLocation.coordinates;
|
|
230
|
-
}
|
|
231
|
-
if (coordinates) {
|
|
232
|
-
return {
|
|
233
|
-
sampleSiteId: doc._id,
|
|
234
|
-
orgId: doc.sponsor,
|
|
235
|
-
timeUpdated: doc.updatedAt,
|
|
236
|
-
sampleSiteLocation: {
|
|
237
|
-
type: "Point",
|
|
238
|
-
coordinates,
|
|
239
|
-
},
|
|
240
|
-
deletedAt: null, // Not a deletion, so this field is null
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
return null;
|
|
244
|
-
}).filter(Boolean);
|
|
245
|
-
|
|
246
|
-
if (auditLogs.length) {
|
|
247
|
-
await SampleSiteAudit.insertMany(auditLogs);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
// Create the SampleSites model
|
|
253
|
-
const SampleSites = mongoose.model("SampleSites", sampleSitesSchema);
|
|
254
|
-
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
const sampleParametersEnum = [
|
|
3
|
+
"C6H6", // Benzene
|
|
4
|
+
"PB", // Lead
|
|
5
|
+
"AS", // Arsenic
|
|
6
|
+
"CD", // Cadmium
|
|
7
|
+
"CR", // Chromium
|
|
8
|
+
"NI", // Nickel
|
|
9
|
+
"BA", // Barium
|
|
10
|
+
"FE", // Iron
|
|
11
|
+
"CU", // Copper
|
|
12
|
+
"ZN", // Zinc
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const noteSchema = mongoose.Schema({
|
|
16
|
+
note: {
|
|
17
|
+
type: String,
|
|
18
|
+
required: true,
|
|
19
|
+
},
|
|
20
|
+
type: {
|
|
21
|
+
type: String,
|
|
22
|
+
required: true,
|
|
23
|
+
},
|
|
24
|
+
adminId: {
|
|
25
|
+
type: mongoose.Types.ObjectId,
|
|
26
|
+
ref: "Admin",
|
|
27
|
+
required: true,
|
|
28
|
+
},
|
|
29
|
+
adminName: {
|
|
30
|
+
type: String,
|
|
31
|
+
},
|
|
32
|
+
date: {
|
|
33
|
+
type: Date,
|
|
34
|
+
default: Date.now,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Sample Site Audit Schema
|
|
39
|
+
const sampleSiteAuditSchema = mongoose.Schema(
|
|
40
|
+
{
|
|
41
|
+
sampleSiteId: { type: mongoose.Types.ObjectId, ref: "SampleSites" },
|
|
42
|
+
orgId: { type: mongoose.Types.ObjectId, ref: "Organizations" },
|
|
43
|
+
timeUpdated: Date,
|
|
44
|
+
deletedAt: { type: Date, default: Date.now }, // Only populated on delete
|
|
45
|
+
sampleSiteLocation: {
|
|
46
|
+
// Sample Site GPS location (lat, lon)
|
|
47
|
+
type: { type: String, enum: ["Point"], required: true },
|
|
48
|
+
coordinates: {
|
|
49
|
+
latitude: { type: Number, required: true },
|
|
50
|
+
longitude: { type: Number, required: true }
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
timestamps: true,
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// Create the SampleSiteAudit model
|
|
60
|
+
const SampleSiteAudit = mongoose.model("SampleSiteAudit", sampleSiteAuditSchema);
|
|
61
|
+
|
|
62
|
+
// Sample Sites Schema
|
|
63
|
+
const sampleSitesSchema = mongoose.Schema(
|
|
64
|
+
{
|
|
65
|
+
name: { type: String },
|
|
66
|
+
managedBy: { type: String },
|
|
67
|
+
sponsoredBy: { type: String },
|
|
68
|
+
lastSamplePeriodStartDate: { type: Date },
|
|
69
|
+
lastSamplePeriodEndDate: { type: Date },
|
|
70
|
+
isPrivate: { type: Boolean, default: false, required: true },
|
|
71
|
+
sponsor: { type: mongoose.Types.ObjectId, ref: "Organizations" },
|
|
72
|
+
sponsorName: { type: String },
|
|
73
|
+
/*
|
|
74
|
+
* Location object: unified structure for all address and geospatial data.
|
|
75
|
+
* - monitorLatitude and monitorLongitude are deprecated and no longer used.
|
|
76
|
+
* - The location object (including location.gps) is now built in config/google.js.
|
|
77
|
+
* - location.gps is a GeoJSON Point used for all geospatial queries (replaces gpsLocation).
|
|
78
|
+
*/
|
|
79
|
+
location: {
|
|
80
|
+
city: { type: String },
|
|
81
|
+
state: { type: String },
|
|
82
|
+
county: { type: String },
|
|
83
|
+
street: { type: String },
|
|
84
|
+
zip_code: { type: String },
|
|
85
|
+
neighborhood: { type: String },
|
|
86
|
+
gps: {
|
|
87
|
+
type: {
|
|
88
|
+
type: String,
|
|
89
|
+
enum: ["Point"],
|
|
90
|
+
required: true,
|
|
91
|
+
},
|
|
92
|
+
coordinates: {
|
|
93
|
+
latitude: { type: Number, required: true },
|
|
94
|
+
longitude: { type: Number, required: true }
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
context: { type: [String] },
|
|
99
|
+
parameters: [
|
|
100
|
+
{
|
|
101
|
+
type: String,
|
|
102
|
+
enum: sampleParametersEnum,
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
notes: { type: [noteSchema] },
|
|
106
|
+
image: { type: String },
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
timestamps: true,
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// Geographic queries - already exists
|
|
114
|
+
sampleSitesSchema.index({ "location.gps": "2dsphere" }); // use location.gps for geospatial queries
|
|
115
|
+
|
|
116
|
+
// Sponsor-based queries
|
|
117
|
+
sampleSitesSchema.index({ sponsor: 1, isPrivate: 1 });
|
|
118
|
+
|
|
119
|
+
// Location-based filtering
|
|
120
|
+
sampleSitesSchema.index({ "location.city": 1, "location.state": 1 });
|
|
121
|
+
sampleSitesSchema.index({ "location.neighborhood": 1 });
|
|
122
|
+
sampleSitesSchema.index({ "location.county": 1 });
|
|
123
|
+
|
|
124
|
+
// Query parameter filtering
|
|
125
|
+
sampleSitesSchema.index({ context: 1 });
|
|
126
|
+
sampleSitesSchema.index({ parameters: 1 });
|
|
127
|
+
|
|
128
|
+
// Pre-hook to log single document deletions
|
|
129
|
+
sampleSitesSchema.pre("findOneAndDelete", async function () {
|
|
130
|
+
const docToDelete = await this.model.findOne(this.getFilter()).lean();
|
|
131
|
+
if (docToDelete) {
|
|
132
|
+
console.log("Logging findOneAndDelete to sample site audit", docToDelete);
|
|
133
|
+
let coordinates = null;
|
|
134
|
+
if (docToDelete.location && docToDelete.location.gps && Array.isArray(docToDelete.location.gps.coordinates)) {
|
|
135
|
+
coordinates = docToDelete.location.gps.coordinates;
|
|
136
|
+
} else if (docToDelete.gpsLocation && Array.isArray(docToDelete.gpsLocation.coordinates)) {
|
|
137
|
+
coordinates = docToDelete.gpsLocation.coordinates;
|
|
138
|
+
}
|
|
139
|
+
if (coordinates) {
|
|
140
|
+
const auditLog = new SampleSiteAudit({
|
|
141
|
+
sampleSiteId: docToDelete._id,
|
|
142
|
+
orgId: docToDelete.sponsor,
|
|
143
|
+
timeUpdated: docToDelete.updatedAt,
|
|
144
|
+
sampleSiteLocation: {
|
|
145
|
+
type: "Point",
|
|
146
|
+
coordinates,
|
|
147
|
+
},
|
|
148
|
+
deletedAt: new Date(),
|
|
149
|
+
});
|
|
150
|
+
await auditLog.save();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Pre-hook to log multiple document deletions
|
|
156
|
+
sampleSitesSchema.pre("deleteMany", async function () {
|
|
157
|
+
console.log("deleteMany pre-hook triggered for sample sites");
|
|
158
|
+
const docsToDelete = await this.model.find(this.getFilter()).lean();
|
|
159
|
+
|
|
160
|
+
if (docsToDelete.length) {
|
|
161
|
+
console.log(`Logging ${docsToDelete.length} sample site documents to audit`);
|
|
162
|
+
const auditLogs = docsToDelete.map((doc) => {
|
|
163
|
+
let coordinates = null;
|
|
164
|
+
if (doc.location && doc.location.gps && Array.isArray(doc.location.gps.coordinates)) {
|
|
165
|
+
coordinates = doc.location.gps.coordinates;
|
|
166
|
+
} else if (doc.gpsLocation && Array.isArray(doc.gpsLocation.coordinates)) {
|
|
167
|
+
coordinates = doc.gpsLocation.coordinates;
|
|
168
|
+
}
|
|
169
|
+
if (coordinates) {
|
|
170
|
+
return {
|
|
171
|
+
sampleSiteId: doc._id,
|
|
172
|
+
orgId: doc.sponsor,
|
|
173
|
+
timeUpdated: doc.updatedAt,
|
|
174
|
+
sampleSiteLocation: {
|
|
175
|
+
type: "Point",
|
|
176
|
+
coordinates,
|
|
177
|
+
},
|
|
178
|
+
deletedAt: new Date(),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}).filter(Boolean);
|
|
183
|
+
|
|
184
|
+
if (auditLogs.length) {
|
|
185
|
+
await SampleSiteAudit.insertMany(auditLogs);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Pre-hook to log a single document deletion (for deleteOne)
|
|
191
|
+
sampleSitesSchema.pre("deleteOne", async function () {
|
|
192
|
+
console.log("deleteOne pre-hook triggered for sample sites");
|
|
193
|
+
const docToDelete = await this.model.findOne(this.getFilter()).lean();
|
|
194
|
+
|
|
195
|
+
if (docToDelete) {
|
|
196
|
+
console.log("Logging deleteOne to sample site audit", docToDelete);
|
|
197
|
+
let coordinates = null;
|
|
198
|
+
if (docToDelete.location && docToDelete.location.gps && Array.isArray(docToDelete.location.gps.coordinates)) {
|
|
199
|
+
coordinates = docToDelete.location.gps.coordinates;
|
|
200
|
+
} else if (docToDelete.gpsLocation && Array.isArray(docToDelete.gpsLocation.coordinates)) {
|
|
201
|
+
coordinates = docToDelete.gpsLocation.coordinates;
|
|
202
|
+
}
|
|
203
|
+
if (coordinates) {
|
|
204
|
+
const auditLog = new SampleSiteAudit({
|
|
205
|
+
sampleSiteId: docToDelete._id,
|
|
206
|
+
orgId: docToDelete.sponsor,
|
|
207
|
+
timeUpdated: docToDelete.updatedAt,
|
|
208
|
+
sampleSiteLocation: {
|
|
209
|
+
type: "Point",
|
|
210
|
+
coordinates,
|
|
211
|
+
},
|
|
212
|
+
deletedAt: new Date(),
|
|
213
|
+
});
|
|
214
|
+
await auditLog.save();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Pre-hook to log multiple document updates
|
|
220
|
+
sampleSitesSchema.pre("updateMany", async function () {
|
|
221
|
+
const docsToUpdate = await this.model.find(this.getFilter()).lean();
|
|
222
|
+
if (docsToUpdate.length) {
|
|
223
|
+
console.log(`Logging ${docsToUpdate.length} sample site documents to audit`);
|
|
224
|
+
const auditLogs = docsToUpdate.map((doc) => {
|
|
225
|
+
let coordinates = null;
|
|
226
|
+
if (doc.location && doc.location.gps && Array.isArray(doc.location.gps.coordinates)) {
|
|
227
|
+
coordinates = doc.location.gps.coordinates;
|
|
228
|
+
} else if (doc.gpsLocation && Array.isArray(doc.gpsLocation.coordinates)) {
|
|
229
|
+
coordinates = doc.gpsLocation.coordinates;
|
|
230
|
+
}
|
|
231
|
+
if (coordinates) {
|
|
232
|
+
return {
|
|
233
|
+
sampleSiteId: doc._id,
|
|
234
|
+
orgId: doc.sponsor,
|
|
235
|
+
timeUpdated: doc.updatedAt,
|
|
236
|
+
sampleSiteLocation: {
|
|
237
|
+
type: "Point",
|
|
238
|
+
coordinates,
|
|
239
|
+
},
|
|
240
|
+
deletedAt: null, // Not a deletion, so this field is null
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
return null;
|
|
244
|
+
}).filter(Boolean);
|
|
245
|
+
|
|
246
|
+
if (auditLogs.length) {
|
|
247
|
+
await SampleSiteAudit.insertMany(auditLogs);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Create the SampleSites model
|
|
253
|
+
const SampleSites = mongoose.model("SampleSites", sampleSitesSchema);
|
|
254
|
+
|
|
255
255
|
export { sampleSitesSchema, SampleSites, sampleSiteAuditSchema, SampleSiteAudit, sampleParametersEnum };
|