@justair/justair-library 5.0.0-alpha.e9d14dc → 5.0.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/README.md CHANGED
@@ -64,6 +64,17 @@ Use the constants-only entry point — no Mongoose dependency:
64
64
  import { PARAMETERS, HEAVY_METALS } from '@justair/justair-library/constants';
65
65
  ```
66
66
 
67
+ ### New sample parameters in 5.0.0
68
+
69
+ The following parameters were added to `HEAVY_METALS` and are now included in `sampleParametersEnum`. Consuming services that display or filter sample parameters (UI dropdowns, notification thresholds, reporting) should be updated to handle these values:
70
+
71
+ | id | Name |
72
+ |---|---|
73
+ | `BE` | Beryllium |
74
+ | `CO` | Cobalt |
75
+ | `MN` | Manganese |
76
+ | `SE` | Selenium |
77
+
67
78
  ### Object shape
68
79
 
69
80
  Each constant is an object with the following fields:
@@ -92,12 +103,12 @@ For rapid local development without waiting for CI:
92
103
  npm pack
93
104
  ```
94
105
 
95
- This creates a `.tgz` file (e.g., `justair-justair-library-4.9.2.tgz`).
106
+ This creates a `.tgz` file (e.g., `justair-justair-library-5.0.0.tgz`).
96
107
 
97
108
  2. **Install in a consuming service:**
98
109
 
99
110
  ```bash
100
- npm install /path/to/justair-library/justair-justair-library-4.9.2.tgz
111
+ npm install /path/to/justair-library/justair-justair-library-5.0.0.tgz
101
112
  ```
102
113
 
103
114
  Or use `npm link` for live development:
@@ -63,7 +63,7 @@ export namespace PARAMETERS {
63
63
  let impacts_3: any;
64
64
  export { impacts_3 as impacts };
65
65
  }
66
- namespace BrC {
66
+ namespace BRC {
67
67
  let id_4: string;
68
68
  export { id_4 as id };
69
69
  let label_4: string;
package/dist/index.d.ts CHANGED
@@ -56,6 +56,8 @@ import { networkMetricsSchema } from "./models/networkMetrics.js";
56
56
  import { NetworkMetrics } from "./models/networkMetrics.js";
57
57
  import { rateOfChangeSchema } from "./models/rateOfChange.js";
58
58
  import { RateOfChange } from "./models/rateOfChange.js";
59
+ import { sitesSchema } from "./models/sites.js";
60
+ import { Sites } from "./models/sites.js";
59
61
  import { sampleSitesSchema } from "./models/sampleSites.js";
60
62
  import { SampleSites } from "./models/sampleSites.js";
61
63
  import { sampleSiteAuditSchema } from "./models/sampleSites.js";
@@ -68,5 +70,5 @@ import { SamplesAudit } from "./models/samples.js";
68
70
  import { sampleParameterReferenceConcentrations } from "./models/samples.js";
69
71
  import { PARAMETERS } from "./constants/pollutants.js";
70
72
  import { HEAVY_METALS } from "./constants/pollutants.js";
71
- export { Database, adminSchema, Admin, configurationsSchema, Configurations, measurementsSchema, Measurements, monitorRequestsSchema, MonitorRequests, monitorsSchema, Monitors, organizationsSchema, Organizations, usersSchema, Users, eventsSchema, Events, lightMonitorSchema, LightMonitors, monitorSuppliersSchema, MonitorSuppliers, contextsSchema, Contexts, parametersSchema, Parameters, announcementSchema, Announcements, jobsSchema, Jobs, apiKeySchema, ApiKey, UsageMetrics, usageMetricsSchema, Audit, auditSchema, EventsAudit, eventsAuditSchema, MonitorAudit, monitorAuditSchema, parametersEnum, deploymentTypesEnum, AlertsAudit, Alerts, alertsSchema, alertsAuditSchema, Features, featuresSchema, dataCompletenessSchema, DataCompleteness, networkMetricsSchema, NetworkMetrics, rateOfChangeSchema, RateOfChange, sampleSitesSchema, SampleSites, sampleSiteAuditSchema, SampleSiteAudit, sampleParametersEnum, samplesSchema, Samples, samplesAuditSchema, SamplesAudit, sampleParameterReferenceConcentrations, PARAMETERS, HEAVY_METALS };
73
+ export { Database, adminSchema, Admin, configurationsSchema, Configurations, measurementsSchema, Measurements, monitorRequestsSchema, MonitorRequests, monitorsSchema, Monitors, organizationsSchema, Organizations, usersSchema, Users, eventsSchema, Events, lightMonitorSchema, LightMonitors, monitorSuppliersSchema, MonitorSuppliers, contextsSchema, Contexts, parametersSchema, Parameters, announcementSchema, Announcements, jobsSchema, Jobs, apiKeySchema, ApiKey, UsageMetrics, usageMetricsSchema, Audit, auditSchema, EventsAudit, eventsAuditSchema, MonitorAudit, monitorAuditSchema, parametersEnum, deploymentTypesEnum, AlertsAudit, Alerts, alertsSchema, alertsAuditSchema, Features, featuresSchema, dataCompletenessSchema, DataCompleteness, networkMetricsSchema, NetworkMetrics, rateOfChangeSchema, RateOfChange, sitesSchema, Sites, sampleSitesSchema, SampleSites, sampleSiteAuditSchema, SampleSiteAudit, sampleParametersEnum, samplesSchema, Samples, samplesAuditSchema, SamplesAudit, sampleParameterReferenceConcentrations, PARAMETERS, HEAVY_METALS };
72
74
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"AA6EA;;;iBAEC;yBAvBwB,oBAAoB;qBADxB,gBAAgB;4BAvDF,mBAAmB;sBAAnB,mBAAmB;qCAI/C,4BAA4B;+BAA5B,4BAA4B;mCAM5B,0BAA0B;6BAA1B,0BAA0B;sCAI1B,6BAA6B;gCAA7B,6BAA6B;+BAQ7B,sBAAsB;yBAAtB,sBAAsB;oCACsB,2BAA2B;8BAA3B,2BAA2B;4BAQ3C,mBAAmB;sBAAnB,mBAAmB;6BAM/C,oBAAoB;uBAApB,oBAAoB;mCAbuB,2BAA2B;8BAA3B,2BAA2B;uCAItE,8BAA8B;iCAA9B,8BAA8B;+BACI,sBAAsB;yBAAtB,sBAAsB;iCAClB,wBAAwB;2BAAxB,wBAAwB;mCAQnB,2BAA2B;8BAA3B,2BAA2B;2BAC5C,kBAAkB;qBAAlB,kBAAkB;6BACd,oBAAoB;uBAApB,oBAAoB;6BACR,0BAA0B;mCAA1B,0BAA0B;sBA/BpE,0BAA0B;4BAA1B,0BAA0B;4BA2B1B,oBAAoB;kCAApB,oBAAoB;6BAfpB,sBAAsB;mCAAtB,sBAAsB;+BAAtB,sBAAsB;oCAAtB,sBAAsB;4BAyBtB,oBAAoB;uBAApB,oBAAoB;6BAApB,oBAAoB;kCAApB,oBAAoB;yBACc,sBAAsB;+BAAtB,sBAAsB;uCAIxD,8BAA8B;iCAA9B,8BAA8B;qCACgB,4BAA4B;+BAA5B,4BAA4B;mCAChC,0BAA0B;6BAA1B,0BAA0B;kCAYpE,yBAAyB;4BAAzB,yBAAyB;sCAAzB,yBAAyB;gCAAzB,yBAAyB;qCAAzB,yBAAyB;8BASzB,qBAAqB;wBAArB,qBAAqB;mCAArB,qBAAqB;6BAArB,qBAAqB;uDAArB,qBAAqB;2BAlBa,2BAA2B;6BAA3B,2BAA2B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"AAgFA;;;iBAEC;yBA1BwB,oBAAoB;qBADxB,gBAAgB;4BAvDF,mBAAmB;sBAAnB,mBAAmB;qCAI/C,4BAA4B;+BAA5B,4BAA4B;mCAM5B,0BAA0B;6BAA1B,0BAA0B;sCAI1B,6BAA6B;gCAA7B,6BAA6B;+BAQ7B,sBAAsB;yBAAtB,sBAAsB;oCACsB,2BAA2B;8BAA3B,2BAA2B;4BAQ3C,mBAAmB;sBAAnB,mBAAmB;6BAM/C,oBAAoB;uBAApB,oBAAoB;mCAbuB,2BAA2B;8BAA3B,2BAA2B;uCAItE,8BAA8B;iCAA9B,8BAA8B;+BACI,sBAAsB;yBAAtB,sBAAsB;iCAClB,wBAAwB;2BAAxB,wBAAwB;mCAQnB,2BAA2B;8BAA3B,2BAA2B;2BAC5C,kBAAkB;qBAAlB,kBAAkB;6BACd,oBAAoB;uBAApB,oBAAoB;6BACR,0BAA0B;mCAA1B,0BAA0B;sBA/BpE,0BAA0B;4BAA1B,0BAA0B;4BA2B1B,oBAAoB;kCAApB,oBAAoB;6BAfpB,sBAAsB;mCAAtB,sBAAsB;+BAAtB,sBAAsB;oCAAtB,sBAAsB;4BAyBtB,oBAAoB;uBAApB,oBAAoB;6BAApB,oBAAoB;kCAApB,oBAAoB;yBACc,sBAAsB;+BAAtB,sBAAsB;uCAIxD,8BAA8B;iCAA9B,8BAA8B;qCACgB,4BAA4B;+BAA5B,4BAA4B;mCAChC,0BAA0B;6BAA1B,0BAA0B;4BAMxC,mBAAmB;sBAAnB,mBAAmB;kCAS/C,yBAAyB;4BAAzB,yBAAyB;sCAAzB,yBAAyB;gCAAzB,yBAAyB;qCAAzB,yBAAyB;8BASzB,qBAAqB;wBAArB,qBAAqB;mCAArB,qBAAqB;6BAArB,qBAAqB;uDAArB,qBAAqB;2BArBa,2BAA2B;6BAA3B,2BAA2B"}
@@ -1 +1 @@
1
- {"version":3,"file":"measurements.d.ts","sourceRoot":"","sources":["../../src/models/measurements.js"],"names":[],"mappings":"AA+FA,qCA0BE;AAgJF,+BAAwE;AA7KxE,wBAAmD;AA1BnD,8BAwBE;AAlEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCE;qBA5DmB,UAAU"}
1
+ {"version":3,"file":"measurements.d.ts","sourceRoot":"","sources":["../../src/models/measurements.js"],"names":[],"mappings":"AA8FA,qCA0BE;AA2IF,+BAAwE;AAxKxE,wBAAmD;AAzBnD,8BAuBE;AAjEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCE;qBA5DmB,UAAU"}
@@ -1 +1 @@
1
- {"version":3,"file":"monitors.d.ts","sourceRoot":"","sources":["../../src/models/monitors.js"],"names":[],"mappings":"AA6JA,iCA0HE;AA6IF,2BAA4D;AAzS5D,qCA4BE;AAGF,+BAAwE;AAvJxE,sCAA+C"}
1
+ {"version":3,"file":"monitors.d.ts","sourceRoot":"","sources":["../../src/models/monitors.js"],"names":[],"mappings":"AA6JA,iCA8HE;AAuIF,2BAA4D;AAvS5D,qCA4BE;AAGF,+BAAwE;AAvJxE,sCAAoD"}
@@ -3,7 +3,4 @@ export const SampleSites: any;
3
3
  export const sampleSiteAuditSchema: any;
4
4
  export const SampleSiteAudit: any;
5
5
  export const sampleParametersEnum: string[];
6
- export const sampleParameterReferenceConcentrations: {
7
- [k: string]: any;
8
- };
9
6
  //# sourceMappingURL=sampleSites.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sampleSites.d.ts","sourceRoot":"","sources":["../../src/models/sampleSites.js"],"names":[],"mappings":"AA4DA,oCAmDE;AA2JF,8BAAqE;AApOrE,wCAgBE;AAGF,kCAAiF;AAnDjF,4CAAuD;AAIvD;;EAEE"}
1
+ {"version":3,"file":"sampleSites.d.ts","sourceRoot":"","sources":["../../src/models/sampleSites.js"],"names":[],"mappings":"AAsDA,oCAuDE;AAqJF,8BAAqE;AAlOrE,wCAgBE;AAGF,kCAAiF;AA7CjF,4CAAuD"}
@@ -1 +1 @@
1
- {"version":3,"file":"samples.d.ts","sourceRoot":"","sources":["../../src/models/samples.js"],"names":[],"mappings":"AAoCA,gCAkBE;AAoIF,0BAAyD;AAzJzD,+BAAwE;AAvBxE,qCAoBE;AAzBF;;EAEE"}
1
+ {"version":3,"file":"samples.d.ts","sourceRoot":"","sources":["../../src/models/samples.js"],"names":[],"mappings":"AAoCA,gCAkBE;AA6HF,0BAAyD;AAlJzD,+BAAwE;AAvBxE,qCAoBE;AAzBF;;EAEE"}
@@ -0,0 +1,3 @@
1
+ export const sitesSchema: any;
2
+ export const Sites: any;
3
+ //# sourceMappingURL=sites.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sites.d.ts","sourceRoot":"","sources":["../../src/models/sites.js"],"names":[],"mappings":"AAGA,8BAoCE;AAaF,wBAAmD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=enums.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enums.test.d.ts","sourceRoot":"","sources":["../../../src/models/tests/enums.test.js"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sampleSites.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sampleSites.test.d.ts","sourceRoot":"","sources":["../../../src/models/tests/sampleSites.test.js"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sites.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sites.test.d.ts","sourceRoot":"","sources":["../../../src/models/tests/sites.test.js"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,16 +1,22 @@
1
1
  {
2
2
  "name": "@justair/justair-library",
3
- "version": "5.0.0-alpha.e9d14dc",
3
+ "version": "5.0.0",
4
4
  "description": "JustAir Internal Library",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
7
7
  "types": "dist/index.d.ts",
8
8
  "exports": {
9
- ".": "./src/index.js",
10
- "./constants": "./src/constants/pollutants.js"
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./src/index.js"
12
+ },
13
+ "./constants": {
14
+ "types": "./dist/constants/pollutants.d.ts",
15
+ "default": "./src/constants/pollutants.js"
16
+ }
11
17
  },
12
18
  "scripts": {
13
- "test": "echo \"Error: no test specified\" && exit 1",
19
+ "test": "node --experimental-vm-modules node_modules/.bin/jest",
14
20
  "build": "tsc",
15
21
  "prepublish": "npm run build"
16
22
  },
@@ -24,8 +30,15 @@
24
30
  "nanoid": "^5.1.6",
25
31
  "winston": "^3.10.0"
26
32
  },
33
+ "jest": {
34
+ "testEnvironment": "node",
35
+ "transform": {}
36
+ },
27
37
  "devDependencies": {
38
+ "@jest/globals": "^29",
28
39
  "@types/node": "^16.0.0",
40
+ "jest": "^29",
41
+ "jest-environment-node": "^29",
29
42
  "ts-node": "^10.0.0",
30
43
  "typescript": "^5.3.2"
31
44
  }
@@ -41,8 +41,8 @@ export const PARAMETERS = {
41
41
  origin: null,
42
42
  impacts: null,
43
43
  },
44
- BrC: {
45
- id: "BrC",
44
+ BRC: {
45
+ id: "BRC",
46
46
  label: "Brown Carbon",
47
47
  name: "Total Brown Carbon (BrC)",
48
48
  unit: "ng/m³",
@@ -205,7 +205,6 @@ export const PARAMETERS = {
205
205
 
206
206
  // Parameters analyzed from physical air samples in a laboratory.
207
207
  // These correspond to SampleSites.parameters and are not measured by real-time sensors.
208
- // Note: CO here is Cobalt, not Carbon Monoxide — see PARAMETERS.CO for Carbon Monoxide.
209
208
  export const HEAVY_METALS = {
210
209
  C6H6: {
211
210
  id: "C6H6",
@@ -57,6 +57,14 @@ function sharedAssertions(constants) {
57
57
  test.each(entries)("%s: rfc is a number or null", (key, pollutant) => {
58
58
  expect(pollutant.rfc === null || typeof pollutant.rfc === "number").toBe(true);
59
59
  });
60
+
61
+ test.each(entries)("%s: origin is a string or null", (key, pollutant) => {
62
+ expect(pollutant.origin === null || typeof pollutant.origin === "string").toBe(true);
63
+ });
64
+
65
+ test.each(entries)("%s: impacts is a string or null", (key, pollutant) => {
66
+ expect(pollutant.impacts === null || typeof pollutant.impacts === "string").toBe(true);
67
+ });
60
68
  }
61
69
 
62
70
  describe("PARAMETERS", () => {
@@ -65,4 +73,8 @@ describe("PARAMETERS", () => {
65
73
 
66
74
  describe("HEAVY_METALS", () => {
67
75
  sharedAssertions(HEAVY_METALS);
76
+
77
+ test("PB.rfc is exactly 0, not null", () => {
78
+ expect(HEAVY_METALS.PB.rfc).toBe(0);
79
+ });
68
80
  });
package/src/index.js CHANGED
@@ -57,6 +57,9 @@ import Database from "./config/db.js"; // Import the new Database class
57
57
  import CustomLogger from "./config/logger.js";
58
58
  import { PARAMETERS, HEAVY_METALS } from "./constants/pollutants.js";
59
59
 
60
+ // Sites related imports
61
+ import { sitesSchema, Sites } from "./models/sites.js";
62
+
60
63
  // SampleSites related imports
61
64
  import {
62
65
  sampleSitesSchema,
@@ -133,6 +136,9 @@ export {
133
136
  NetworkMetrics,
134
137
  rateOfChangeSchema,
135
138
  RateOfChange,
139
+ // Sites related exports
140
+ sitesSchema,
141
+ Sites,
136
142
  // SampleSites related exports
137
143
  sampleSitesSchema,
138
144
  SampleSites,
@@ -83,7 +83,6 @@ const auditSchema = mongoose.Schema(
83
83
  },
84
84
  annotations: [annotationSchema], // Include annotations in audit trail
85
85
  isCorrected: { type: Boolean },
86
- isFog: { type: Boolean },
87
86
  },
88
87
  {
89
88
  timestamps: true,
@@ -139,7 +138,6 @@ measurementsSchema.index({ "annotations.userId": 1 });
139
138
  measurementsSchema.pre("findOneAndDelete", async function () {
140
139
  const docToDelete = await this.model.findOne(this.getFilter());
141
140
  if (docToDelete) {
142
- console.log("Logging findOneAndDelete to audit", docToDelete);
143
141
  const auditLog = new Audit({
144
142
  monitorId: docToDelete.monitorId,
145
143
  orgId: docToDelete.orgId,
@@ -190,11 +188,9 @@ measurementsSchema.pre("deleteMany", async function () {
190
188
 
191
189
  // Pre-hook to log a single document deletion (for deleteOne)
192
190
  measurementsSchema.pre("deleteOne", async function () {
193
- console.log("deleteOne pre-hook triggered");
194
191
  const docToDelete = await this.model.findOne(this.getFilter()).lean();
195
192
 
196
193
  if (docToDelete) {
197
- console.log("Logging deleteOne to audit", docToDelete);
198
194
  const auditLog = new Audit({
199
195
  monitorId: docToDelete.monitorId,
200
196
  orgId: docToDelete.orgId,
@@ -218,7 +214,6 @@ measurementsSchema.pre("deleteOne", async function () {
218
214
  measurementsSchema.pre("findOneAndUpdate", async function () {
219
215
  const docToUpdate = await this.model.findOne(this.getFilter()).lean();
220
216
  if (docToUpdate) {
221
- console.log("Logging findOneAndUpdate to audit", docToUpdate);
222
217
  const auditLog = new Audit({
223
218
  monitorId: docToUpdate.monitorId,
224
219
  orgId: docToUpdate.orgId,
@@ -242,7 +237,6 @@ measurementsSchema.pre("findOneAndUpdate", async function () {
242
237
  measurementsSchema.pre("updateMany", async function () {
243
238
  const docsToUpdate = await this.model.find(this.getFilter()).lean();
244
239
  if (docsToUpdate.length) {
245
- console.log(`Logging ${docsToUpdate.length} documents to audit`);
246
240
  const auditLogs = docsToUpdate.map((doc) => ({
247
241
  monitorId: doc.monitorId,
248
242
  orgId: doc.orgId,
@@ -1,7 +1,7 @@
1
1
  import mongoose from "mongoose";
2
2
  import { PARAMETERS } from "../constants/pollutants.js";
3
3
 
4
- const parametersEnum = Object.keys(PARAMETERS);
4
+ const parametersEnum = [...Object.keys(PARAMETERS)];
5
5
 
6
6
  const deploymentTypesEnum = {
7
7
  STATIONARY: "stationary",
@@ -269,6 +269,10 @@ const monitorsSchema = mongoose.Schema(
269
269
  enum: Object.values(deploymentTypesEnum),
270
270
  default: deploymentTypesEnum.STATIONARY,
271
271
  },
272
+ siteId: {
273
+ type: mongoose.Types.ObjectId,
274
+ ref: "Sites",
275
+ },
272
276
  // Optional Clarity node ID (1:1 relationship with Node-S sensor)
273
277
  nodeId: { type: String },
274
278
  // Active alarms from Clarity node (keyed by alarmType)
@@ -305,7 +309,6 @@ monitorsSchema.index({ deploymentType: 1 });
305
309
  monitorsSchema.pre("findOneAndDelete", async function () {
306
310
  const docToDelete = await this.model.findOne(this.getFilter()).lean();
307
311
  if (docToDelete) {
308
- console.log("Logging findOneAndDelete to monitor audit", docToDelete);
309
312
  const auditLog = new MonitorAudit({
310
313
  monitorId: docToDelete._id,
311
314
  orgId: docToDelete.sponsor,
@@ -332,11 +335,9 @@ monitorsSchema.pre("findOneAndDelete", async function () {
332
335
 
333
336
  // Pre-hook to log multiple document deletions
334
337
  monitorsSchema.pre("deleteMany", async function () {
335
- console.log("deleteMany pre-hook triggered for monitors");
336
338
  const docsToDelete = await this.model.find(this.getFilter()).lean();
337
339
 
338
340
  if (docsToDelete.length) {
339
- console.log(`Logging ${docsToDelete.length} monitor documents to audit`);
340
341
  const auditLogs = docsToDelete.map((doc) => ({
341
342
  monitorId: doc._id,
342
343
  orgId: doc.sponsor,
@@ -361,11 +362,9 @@ monitorsSchema.pre("deleteMany", async function () {
361
362
 
362
363
  // Pre-hook to log a single document deletion (for deleteOne)
363
364
  monitorsSchema.pre("deleteOne", async function () {
364
- console.log("deleteOne pre-hook triggered for monitors");
365
365
  const docToDelete = await this.model.findOne(this.getFilter()).lean();
366
366
 
367
367
  if (docToDelete) {
368
- console.log("Logging deleteOne to monitor audit", docToDelete);
369
368
  const auditLog = new MonitorAudit({
370
369
  monitorId: docToDelete._id,
371
370
  orgId: docToDelete.sponsor,
@@ -394,7 +393,6 @@ monitorsSchema.pre("deleteOne", async function () {
394
393
  monitorsSchema.pre("updateMany", async function () {
395
394
  const docsToUpdate = await this.model.find(this.getFilter()).lean();
396
395
  if (docsToUpdate.length) {
397
- console.log(`Logging ${docsToUpdate.length} monitor documents to audit`);
398
396
  const auditLogs = docsToUpdate.map((doc) => ({
399
397
  monitorId: doc._id,
400
398
  orgId: doc.sponsor,
@@ -6,12 +6,6 @@ const generateSiteCode = customAlphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 8);
6
6
 
7
7
  const sampleParametersEnum = Object.keys(HEAVY_METALS);
8
8
 
9
- // EPA IRIS chronic inhalation reference concentrations (µg/m³)
10
- // null = no established inhalation RfC
11
- const sampleParameterReferenceConcentrations = Object.fromEntries(
12
- Object.entries(HEAVY_METALS).map(([key, p]) => [key, p.rfc])
13
- );
14
-
15
9
  const noteSchema = mongoose.Schema({
16
10
  note: {
17
11
  type: String,
@@ -105,6 +99,10 @@ const sampleSitesSchema = mongoose.Schema(
105
99
  ],
106
100
  notes: { type: [noteSchema] },
107
101
  image: { type: String },
102
+ siteId: {
103
+ type: mongoose.Types.ObjectId,
104
+ ref: "Sites",
105
+ },
108
106
  },
109
107
  {
110
108
  timestamps: true,
@@ -139,7 +137,6 @@ sampleSitesSchema.index({ parameters: 1 });
139
137
  sampleSitesSchema.pre("findOneAndDelete", async function () {
140
138
  const docToDelete = await this.model.findOne(this.getFilter()).lean();
141
139
  if (docToDelete) {
142
- console.log("Logging findOneAndDelete to sample site audit", docToDelete);
143
140
  let coordinates = null;
144
141
  if (docToDelete.location && docToDelete.location.gps && Array.isArray(docToDelete.location.gps.coordinates)) {
145
142
  coordinates = docToDelete.location.gps.coordinates;
@@ -165,11 +162,9 @@ sampleSitesSchema.pre("findOneAndDelete", async function () {
165
162
 
166
163
  // Pre-hook to log multiple document deletions
167
164
  sampleSitesSchema.pre("deleteMany", async function () {
168
- console.log("deleteMany pre-hook triggered for sample sites");
169
165
  const docsToDelete = await this.model.find(this.getFilter()).lean();
170
166
 
171
167
  if (docsToDelete.length) {
172
- console.log(`Logging ${docsToDelete.length} sample site documents to audit`);
173
168
  const auditLogs = docsToDelete.map((doc) => {
174
169
  let coordinates = null;
175
170
  if (doc.location && doc.location.gps && Array.isArray(doc.location.gps.coordinates)) {
@@ -201,11 +196,9 @@ sampleSitesSchema.pre("deleteMany", async function () {
201
196
 
202
197
  // Pre-hook to log a single document deletion (for deleteOne)
203
198
  sampleSitesSchema.pre("deleteOne", async function () {
204
- console.log("deleteOne pre-hook triggered for sample sites");
205
199
  const docToDelete = await this.model.findOne(this.getFilter()).lean();
206
200
 
207
201
  if (docToDelete) {
208
- console.log("Logging deleteOne to sample site audit", docToDelete);
209
202
  let coordinates = null;
210
203
  if (docToDelete.location && docToDelete.location.gps && Array.isArray(docToDelete.location.gps.coordinates)) {
211
204
  coordinates = docToDelete.location.gps.coordinates;
@@ -233,7 +226,6 @@ sampleSitesSchema.pre("deleteOne", async function () {
233
226
  sampleSitesSchema.pre("updateMany", async function () {
234
227
  const docsToUpdate = await this.model.find(this.getFilter()).lean();
235
228
  if (docsToUpdate.length) {
236
- console.log(`Logging ${docsToUpdate.length} sample site documents to audit`);
237
229
  const auditLogs = docsToUpdate.map((doc) => {
238
230
  let coordinates = null;
239
231
  if (doc.location && doc.location.gps && Array.isArray(doc.location.gps.coordinates)) {
@@ -266,4 +258,4 @@ sampleSitesSchema.pre("updateMany", async function () {
266
258
  // Create the SampleSites model
267
259
  const SampleSites = mongoose.model("SampleSites", sampleSitesSchema);
268
260
 
269
- export { sampleSitesSchema, SampleSites, sampleSiteAuditSchema, SampleSiteAudit, sampleParametersEnum, sampleParameterReferenceConcentrations };
261
+ export { sampleSitesSchema, SampleSites, sampleSiteAuditSchema, SampleSiteAudit, sampleParametersEnum };
@@ -65,7 +65,6 @@ samplesSchema.index({ siteId: 1, uploadDate: -1 });
65
65
  samplesSchema.pre("findOneAndDelete", async function () {
66
66
  const docToDelete = await this.model.findOne(this.getFilter()).lean();
67
67
  if (docToDelete) {
68
- console.log("Logging findOneAndDelete to audit", docToDelete);
69
68
  const auditLog = new SamplesAudit({
70
69
  siteId: docToDelete.siteId,
71
70
  orgId: docToDelete.orgId,
@@ -87,10 +86,8 @@ samplesSchema.pre("findOneAndDelete", async function () {
87
86
 
88
87
  // Pre-hook to log multiple document deletions
89
88
  samplesSchema.pre("deleteMany", async function () {
90
- console.log("deleteMany pre-hook triggered");
91
89
  const docsToDelete = await this.model.find(this.getFilter()).lean();
92
90
  if (docsToDelete.length) {
93
- console.log(`Logging ${docsToDelete.length} documents to audit`);
94
91
  const auditLogs = docsToDelete.map((doc) => ({
95
92
  siteId: doc.siteId,
96
93
  orgId: doc.orgId,
@@ -112,10 +109,8 @@ samplesSchema.pre("deleteMany", async function () {
112
109
 
113
110
  // Pre-hook to log a single document deletion (for deleteOne)
114
111
  samplesSchema.pre("deleteOne", async function () {
115
- console.log("deleteOne pre-hook triggered");
116
112
  const docToDelete = await this.model.findOne(this.getFilter()).lean();
117
113
  if (docToDelete) {
118
- console.log("Logging deleteOne to audit", docToDelete);
119
114
  const auditLog = new SamplesAudit({
120
115
  siteId: docToDelete.siteId,
121
116
  orgId: docToDelete.orgId,
@@ -139,7 +134,6 @@ samplesSchema.pre("deleteOne", async function () {
139
134
  samplesSchema.pre("findOneAndUpdate", async function () {
140
135
  const docToUpdate = await this.model.findOne(this.getFilter()).lean();
141
136
  if (docToUpdate) {
142
- console.log("Logging findOneAndUpdate to audit", docToUpdate);
143
137
  const auditLog = new SamplesAudit({
144
138
  siteId: docToUpdate.siteId,
145
139
  orgId: docToUpdate.orgId,
@@ -163,7 +157,6 @@ samplesSchema.pre("findOneAndUpdate", async function () {
163
157
  samplesSchema.pre("updateMany", async function () {
164
158
  const docsToUpdate = await this.model.find(this.getFilter()).lean();
165
159
  if (docsToUpdate.length) {
166
- console.log(`Logging ${docsToUpdate.length} documents to audit`);
167
160
  const auditLogs = docsToUpdate.map((doc) => ({
168
161
  siteId: doc.siteId,
169
162
  orgId: doc.orgId,
@@ -0,0 +1,55 @@
1
+ import mongoose from "mongoose";
2
+
3
+ // Sites Schema
4
+ const sitesSchema = mongoose.Schema(
5
+ {
6
+ name: { type: String },
7
+ sampleSiteId: {
8
+ type: mongoose.Types.ObjectId,
9
+ ref: "SampleSites",
10
+ },
11
+ monitorIds: {
12
+ type: [{ type: mongoose.Types.ObjectId, ref: "Monitors" }],
13
+ default: undefined,
14
+ },
15
+ sponsor: { type: mongoose.Types.ObjectId, ref: "Organizations" },
16
+ location: {
17
+ city: { type: String },
18
+ state: { type: String },
19
+ county: { type: String },
20
+ street: { type: String },
21
+ zip_code: { type: String },
22
+ neighborhood: { type: String },
23
+ gps: {
24
+ type: {
25
+ type: String,
26
+ enum: ["Point"],
27
+ required: true,
28
+ },
29
+ coordinates: {
30
+ type: [Number],
31
+ required: true,
32
+ },
33
+ },
34
+ },
35
+ isActive: { type: Boolean, default: true },
36
+ },
37
+ {
38
+ timestamps: true,
39
+ }
40
+ );
41
+
42
+ // Geographic queries
43
+ sitesSchema.index({ "location.gps": "2dsphere" });
44
+
45
+ // Sponsor-based queries
46
+ sitesSchema.index({ sponsor: 1, isActive: 1 });
47
+
48
+ // Back-reference lookups
49
+ sitesSchema.index({ sampleSiteId: 1 });
50
+ sitesSchema.index({ monitorIds: 1 });
51
+
52
+ // Create the Sites model
53
+ const Sites = mongoose.model("Sites", sitesSchema);
54
+
55
+ export { sitesSchema, Sites };