From 565ab41f7eb48f6412e829227fb6d7418dfd108b Mon Sep 17 00:00:00 2001 From: "DESKTOP-C9V2M01\\Zeeri" Date: Mon, 15 Aug 2022 07:01:20 -0700 Subject: [PATCH 1/3] Tracked down a bug in a call to MongoDB that failed to ever return. It was trying to remove an index and the call never finished. --- imports/api/admin.js | 4 ++++ imports/api/asset-assignments.js | 12 +++++------- imports/api/asset-types.js | 3 +++ imports/api/assets.js | 3 +++ imports/api/data-collection.js | 4 ++++ imports/api/index.js | 2 ++ imports/api/sites.js | 4 ++++ imports/api/staff.js | 3 +++ imports/api/students.js | 6 +++++- imports/api/users.js | 4 ++++ imports/startup/accounts-config.js | 4 ++++ package-lock.json | 26 ++++++++++++++++++++++++++ server/CheckEnvironment.js | 14 +++++++++----- server/DataCollection.js | 4 ++++ server/google-oauth.js | 4 ++++ server/logging.js | 4 ++++ 16 files changed, 88 insertions(+), 13 deletions(-) diff --git a/imports/api/admin.js b/imports/api/admin.js index cdfcff1..0bbcfb0 100644 --- a/imports/api/admin.js +++ b/imports/api/admin.js @@ -2,6 +2,8 @@ import {Meteor} from "meteor/meteor"; import { _ } from 'underscore'; import { Roles } from 'meteor/alanning:roles'; +// console.log("Setting Up Admin...") + if (Meteor.isServer) { Meteor.methods({ 'admin.fixRecords'(input) { @@ -54,3 +56,5 @@ if (Meteor.isServer) { }, }); } + +// console.log("Admin setup.") \ No newline at end of file diff --git a/imports/api/asset-assignments.js b/imports/api/asset-assignments.js index 2656e9a..cad8a39 100644 --- a/imports/api/asset-assignments.js +++ b/imports/api/asset-assignments.js @@ -5,6 +5,8 @@ import { Roles } from 'meteor/alanning:roles'; //import SimpleSchema from "simpl-schema"; import {AssetTypes} from "./asset-types"; +// console.log("Setting Up Asset Assignments...") + export const AssetAssignments = new Mongo.Collection('assetAssignments'); /* const TYPE_STUDENT = 1; @@ -42,8 +44,8 @@ if (Meteor.isServer) { //try {AssetAssignments._dropIndex("name")} catch(e) {} //AssetAssignments.createIndex({name: "text"}, {name: "name", unique: false}); - try {AssetTypes._dropIndex("AssetID")} catch(e) {} //Typo put this as an index in AssetTypes instead of AssetAssignments. - AssetAssignments.createIndex({assetId: 1}, {name: "AssetID", unique: false}); + //try {AssetTypes._dropIndex("AssetID")} catch(e) {} //Typo put this as an index in AssetTypes instead of AssetAssignments. + //AssetAssignments.createIndex({assetId: 1}, {name: "AssetID", unique: false}); // This code only runs on the server Meteor.publish('assetAssignments', function(assetId) { @@ -57,11 +59,6 @@ if (Meteor.isServer) { }); } Meteor.methods({ - 'AssetAssignments.getOne'(assetId) { - check(assetId, String); - - return AssetAssignments.findOne(assetId); - }, /** * Assigns the asset to the assignee. The assignee should either be a Student or Staff member. * @param assetId The Mongo ID of the asset (asset._id). @@ -91,3 +88,4 @@ Meteor.methods({ }, }); +// console.log("Asset assignments setup.") \ No newline at end of file diff --git a/imports/api/asset-types.js b/imports/api/asset-types.js index 0f185c8..2865146 100644 --- a/imports/api/asset-types.js +++ b/imports/api/asset-types.js @@ -4,6 +4,8 @@ import { check } from 'meteor/check'; import { Roles } from 'meteor/alanning:roles'; //import SimpleSchema from "simpl-schema"; +// console.log("Setting Up Asset Types...") + // // An asset type is a specific type of equipment. Example: Lenovo 100e Chromebook. // @@ -74,3 +76,4 @@ Meteor.methods({ }, }); +// console.log("Asset types setup.") diff --git a/imports/api/assets.js b/imports/api/assets.js index e040593..59d9105 100644 --- a/imports/api/assets.js +++ b/imports/api/assets.js @@ -7,6 +7,8 @@ import {AssetTypes} from "./asset-types"; import { ReactiveAggregate } from 'meteor/tunguska:reactive-aggregate'; import {AssetAssignments} from "/imports/api/asset-assignments"; +// console.log("Setting Up Assets...") + export const Assets = new Mongo.Collection('assets'); /* @@ -193,3 +195,4 @@ Meteor.methods({ } }); +// console.log("Assets setup.") \ No newline at end of file diff --git a/imports/api/data-collection.js b/imports/api/data-collection.js index 587b025..7d4f058 100644 --- a/imports/api/data-collection.js +++ b/imports/api/data-collection.js @@ -5,6 +5,8 @@ import { MongoClient } from 'mongodb'; import {Assets} from "/imports/api/assets"; //import {Roles} from 'alanning/roles'; +// console.log("Setting Up Data Collection...") + //export const Records = new Mongo.Collection('records'); let client; let database; @@ -104,3 +106,5 @@ if (Meteor.isServer) { // }, }); } + +// console.log("Data Collection setup.") \ No newline at end of file diff --git a/imports/api/index.js b/imports/api/index.js index 1d8d2d1..082304b 100644 --- a/imports/api/index.js +++ b/imports/api/index.js @@ -7,3 +7,5 @@ import "./sites.js"; import "./asset-types.js"; import "./assets.js"; import "./asset-assignments.js"; + +// console.log("Finished setting up server side models."); \ No newline at end of file diff --git a/imports/api/sites.js b/imports/api/sites.js index efe2f49..728a720 100644 --- a/imports/api/sites.js +++ b/imports/api/sites.js @@ -4,6 +4,8 @@ import {Students} from "./students"; import {Staff} from "./staff"; import { Roles } from 'meteor/alanning:roles'; +// console.log("Setting Up Sites...") + export const Sites = new Mongo.Collection('sites'); if (Meteor.isServer) { @@ -36,3 +38,5 @@ Meteor.methods({ } }, }); + +// console.log("Sites setup.") \ No newline at end of file diff --git a/imports/api/staff.js b/imports/api/staff.js index 6c10789..34b35e7 100644 --- a/imports/api/staff.js +++ b/imports/api/staff.js @@ -6,6 +6,8 @@ import {Sites} from "/imports/api/sites"; import {parse} from "csv-parse"; import { ReactiveAggregate } from 'meteor/tunguska:reactive-aggregate'; +// console.log("Setting Up Staff...") + export const Staff = new Mongo.Collection('staff'); if (Meteor.isServer) { @@ -160,3 +162,4 @@ Meteor.methods({ } }); +// console.log("Staff setup.") \ No newline at end of file diff --git a/imports/api/students.js b/imports/api/students.js index 0b9c4e4..cb4a4ce 100644 --- a/imports/api/students.js +++ b/imports/api/students.js @@ -6,6 +6,8 @@ import { Roles } from 'meteor/alanning:roles'; import {parse} from 'csv-parse'; import { ReactiveAggregate } from 'meteor/tunguska:reactive-aggregate'; +// console.log("Setting Up Students...") + export const Students = new Mongo.Collection('students'); if (Meteor.isServer) { @@ -170,4 +172,6 @@ if (Meteor.isServer) { } }); -} \ No newline at end of file +} + +// console.log("Students setup.") \ No newline at end of file diff --git a/imports/api/users.js b/imports/api/users.js index a00bcbe..3a5262a 100644 --- a/imports/api/users.js +++ b/imports/api/users.js @@ -2,6 +2,8 @@ import { Meteor } from 'meteor/meteor'; import { Roles } from 'meteor/alanning:roles'; import { check } from 'meteor/check'; +// console.log("Setting Up Users...") + if (Meteor.isServer) { Meteor.publish(null, function() { if(this.userId) { @@ -66,3 +68,5 @@ if (Meteor.isServer) { // }, }); } + +// console.log("Users setup.") \ No newline at end of file diff --git a/imports/startup/accounts-config.js b/imports/startup/accounts-config.js index 8d1a8d0..2e1d414 100644 --- a/imports/startup/accounts-config.js +++ b/imports/startup/accounts-config.js @@ -2,6 +2,8 @@ import { Accounts } from 'meteor/accounts-base' import { Roles } from 'meteor/alanning:roles' import {Meteor} from "meteor/meteor"; +console.log("Setting up accounts-config...") + if(Meteor.isClient) { Accounts.ui.config({ passwordSignupFields: 'USERNAME_ONLY' @@ -68,3 +70,5 @@ if(Meteor.isServer) { }); } } + +console.log("Finished setting up accounts-config.") \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4d97d73..9547b4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3148,6 +3148,22 @@ "node": ">=0.10.0" } }, + "node_modules/rollup": { + "version": "2.78.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.78.0.tgz", + "integrity": "sha512-4+YfbQC9QEVvKTanHhIAFVUFSRsezvQF8vFOJwtGfb9Bb+r014S+qryr9PSmw8x6sMnPkmFBGAvIFVQxvJxjtg==", + "dev": true, + "peer": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/rollup-plugin-css-only": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/rollup-plugin-css-only/-/rollup-plugin-css-only-3.1.0.tgz", @@ -6171,6 +6187,16 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" }, + "rollup": { + "version": "2.78.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.78.0.tgz", + "integrity": "sha512-4+YfbQC9QEVvKTanHhIAFVUFSRsezvQF8vFOJwtGfb9Bb+r014S+qryr9PSmw8x6sMnPkmFBGAvIFVQxvJxjtg==", + "dev": true, + "peer": true, + "requires": { + "fsevents": "~2.3.2" + } + }, "rollup-plugin-css-only": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/rollup-plugin-css-only/-/rollup-plugin-css-only-3.1.0.tgz", diff --git a/server/CheckEnvironment.js b/server/CheckEnvironment.js index dc0b4a3..f356de4 100644 --- a/server/CheckEnvironment.js +++ b/server/CheckEnvironment.js @@ -1,10 +1,12 @@ +console.log("Checking Environment..."); + if(!process.env.MONGO_URL) { - console.log("Must provide the MONGO_URL environment variable point to the district central's main database. Should be of the format: `mongodb://localhost:27017/DatabaseName` or `mongodb://user_name:password@host.domain.com,host2.domain.com,host3.domain.com/DatabaseName?replicaSet=set_name`.") + console.error("Must provide the MONGO_URL environment variable point to the district central's main database. Should be of the format: `mongodb://localhost:27017/DatabaseName` or `mongodb://user_name:password@host.domain.com,host2.domain.com,host3.domain.com/DatabaseName?replicaSet=set_name`.") process.exit(0); } if(!process.env.MONGO_URL2) { - console.log("Must provide the MONGO_URL2 environment variable pointing to the chromebook data collection dataset. Should be of the format: `mongodb://localhost:27017/DatabaseName` or `mongodb://user_name:password@host.domain.com,host2.domain.com,host3.domain.com/DatabaseName?replicaSet=set_name`.") + console.error("Must provide the MONGO_URL2 environment variable pointing to the chromebook data collection dataset. Should be of the format: `mongodb://localhost:27017/DatabaseName` or `mongodb://user_name:password@host.domain.com,host2.domain.com,host3.domain.com/DatabaseName?replicaSet=set_name`.") process.exit(0); } @@ -12,8 +14,8 @@ try { let settings = Assets.getText('settings.json'); } catch(e) { - console.log("Must have a /private/settings.json file with the following format:"); - console.log(` + console.error("Must have a /private/settings.json file with the following format:"); + console.error(` { "google": { "clientId": "xxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com", @@ -29,4 +31,6 @@ catch(e) { `); process.exit(0); -} \ No newline at end of file +} + +console.log("Environment Checking Complete.") \ No newline at end of file diff --git a/server/DataCollection.js b/server/DataCollection.js index 48cc19b..f3591be 100644 --- a/server/DataCollection.js +++ b/server/DataCollection.js @@ -1,6 +1,8 @@ import {MongoInternals} from 'meteor/mongo'; import {Meteor} from 'meteor/meteor'; +console.log("Setting up data collection database connection..."); + let uri = process.env.MONGO_URL2; //"mongodb://localhost:27017/avusd_data_collection"; //uri = "mongodb://localhost:27017/avusd_data_collection"; //console.log(uri); @@ -11,3 +13,5 @@ Meteor.Records = collection; // let results = collection.find({deviceId: "1e3e99ef-adf4-4aa2-8784-205bc60f0ce3"}).fetch(); // console.log(results); + +console.log("Database connection setup.") \ No newline at end of file diff --git a/server/google-oauth.js b/server/google-oauth.js index 9c2ff4f..9a75d12 100644 --- a/server/google-oauth.js +++ b/server/google-oauth.js @@ -4,6 +4,8 @@ * Loads the information from the /private/settings.json file. */ +console.log("Setting up Google OAuth..."); + try { let settings = Assets.getText('settings.json'); @@ -48,3 +50,5 @@ ServiceConfiguration.configurations.upsert( } ); */ + +console.log("Finished OAuth setup.") \ No newline at end of file diff --git a/server/logging.js b/server/logging.js index edd9272..e4ab080 100644 --- a/server/logging.js +++ b/server/logging.js @@ -3,6 +3,8 @@ import winston from "winston"; import moment from "moment"; import _ from 'underscore'; +// console.log("Setting up logging...."); + let production = (process.env.NODE_ENV === "production"); let logPath = process.env.LOG_PATH; @@ -88,3 +90,5 @@ console.warn = function(d) { console.error = function(e) { logger.log("error", e.stack || e); } + +// console.log("Logger setup."); \ No newline at end of file From 2fd0ebb0de42c8f100d52b41315b7109ba2f5b60 Mon Sep 17 00:00:00 2001 From: "DESKTOP-C9V2M01\\Zeeri" Date: Mon, 15 Aug 2022 07:02:26 -0700 Subject: [PATCH 2/3] Removed reactive-aggregate. --- .meteor/versions | 1 - 1 file changed, 1 deletion(-) diff --git a/.meteor/versions b/.meteor/versions index 726a39a..4c7787c 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -98,7 +98,6 @@ templating-runtime@1.6.0 templating-tools@1.2.2 tmeasday:check-npm-versions@1.0.2 tracker@1.2.0 -tunguska:reactive-aggregate@1.3.8 typescript@4.5.4 underscore@1.0.10 url@1.3.2 From 63174bbc92bfd235baafc96df0cdad83eecce8e0 Mon Sep 17 00:00:00 2001 From: "DESKTOP-C9V2M01\\Zeeri" Date: Mon, 15 Aug 2022 07:49:34 -0700 Subject: [PATCH 3/3] Added assignment history and unassignment feature (not yet tested). --- imports/api/asset-assignment-history.js | 14 ++++ imports/api/assets.js | 74 ++++++++++++++-------- imports/api/index.js | 1 + imports/ui/Assets/AssignmentByAsset.svelte | 25 ++++++++ 4 files changed, 88 insertions(+), 26 deletions(-) create mode 100644 imports/api/asset-assignment-history.js diff --git a/imports/api/asset-assignment-history.js b/imports/api/asset-assignment-history.js new file mode 100644 index 0000000..36df18f --- /dev/null +++ b/imports/api/asset-assignment-history.js @@ -0,0 +1,14 @@ +import {Mongo} from "meteor/mongo"; + +export const AssetAssignmentHistory = new Mongo.Collection('assetAssignmentHistory'); + +/* +Maintains a historical record of asset assignments. +assetId: The asset's assigned ID (not a MongoID). +assigneeType: One of 'Student' or 'Staff'. +assigneeId: The MongoID of the student or staff the asset was assigned to. +startDate: The date/time of the assignment. +endDate: The date/time of the unassignment. +startCondition: TODO +endCondition: TODO + */ \ No newline at end of file diff --git a/imports/api/assets.js b/imports/api/assets.js index 86f6f82..49b6293 100644 --- a/imports/api/assets.js +++ b/imports/api/assets.js @@ -5,6 +5,7 @@ import { Roles } from 'meteor/alanning:roles'; //import SimpleSchema from "simpl-schema"; import {AssetTypes} from "./asset-types"; import {AssetAssignments} from "/imports/api/asset-assignments"; +import {AssetAssignmentHistory} from "/imports/api/asset-assignment-history"; // console.log("Setting Up Assets...") @@ -91,6 +92,7 @@ Meteor.methods({ Assets.insert({assetTypeId, assetId}); } } + else throw new Meteor.Error("User Permission Error"); }, 'assets.update'(_id, assetId, serial) { check(_id, String); @@ -101,6 +103,7 @@ Meteor.methods({ //TODO: Need to first verify there are no checked out assets to the staff member. Assets.update({_id}, {$set: {assetId, serial}}); } + else throw new Meteor.Error("User Permission Error"); }, 'assets.remove'(_id) { check(_id, String); @@ -109,6 +112,7 @@ Meteor.methods({ //TODO: Ensure we have not assigned this asset??? Not sure if we should require unassigning first. Assets.remove({_id}); } + else throw new Meteor.Error("User Permission Error"); }, /** * Assigns the asset to the assignee. The assignee should either be a Student or Staff member. @@ -129,8 +133,9 @@ Meteor.methods({ // Should never happen. console.error("Error: Received incorrect assignee type in adding an assignment."); console.error(assigneeType); + throw new Meteor.Error("Error: Received incorrect assignee type in adding an assignment."); } - else if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) { + else if(Roles.userIsInRole(Meteor.userId(), "laptop-management", {anyScope:true})) { let asset = Assets.findOne({assetId}); if(asset) { @@ -147,6 +152,7 @@ Meteor.methods({ console.error("Could not find the asset: " + assetId) } } + else throw new Meteor.Error("User Permission Error"); }, /** * Removes an assignment for the asset. @@ -160,37 +166,53 @@ Meteor.methods({ if(!date) date = new Date(); - if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) { - Assets.update({assetId}, {$unset: {assigneeType, assigneeId, assignmentDate}}); + if(Roles.userIsInRole(Meteor.userId(), "laptop-management", {anyScope:true})) { + let asset = Assets.findOne({assetId}); + + if(asset) { + try { + AssetAssignmentHistory.insert({assetId, assigneeType: asset.assigneeType, assigneeId: asset.assigneeId, startDate: asset.assignmentDate, endDate: date}); + } catch (e) { + console.error(e); + } + Assets.update({assetId}, {$unset: {assigneeType, assigneeId, assignmentDate}}); + } + else { + console.error("Could not find the asset: " + assetId); + throw new Meteor.Error("Could not find the asset: " + assetId); + } } + else throw new Meteor.Error("User Permission Error"); }, /** * A fix to remove the AssetAssignment collection and merge it with the Asset collection. */ 'assets.fixAssetAssignments'() { - let assignmentDate = new Date(); - //This function just removes the need for the asset-assignments collection and merges it with assets. - if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) { - let assets = Assets.find({}).fetch(); - let assetAssignments = AssetAssignments.find({}).fetch(); - - let assetMap = assets.reduce((map, obj) => { - map[obj.assetId] = obj; - return map; - }, {}); - - console.log(assetMap); - console.log(""); - - for(let next of assetAssignments) { - console.log(next); - let asset = assetMap[next.assetId]; - console.log("Updating " + asset.assetId + " to be assigned to " + next.assigneeType + ": " + next.assigneeId); - let c = Assets.update({assetId: asset.assetId}, {$set: {assigneeType: next.assigneeType, assigneeId: next.assigneeId, assignmentDate}}); - console.log("Updated " + c + " Assets"); - console.log(Assets.findOne({assetId: asset.assetId})); - } - } + // Removed this since it should no longer be relevant. + + // let assignmentDate = new Date(); + // //This function just removes the need for the asset-assignments collection and merges it with assets. + // if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) { + // let assets = Assets.find({}).fetch(); + // let assetAssignments = AssetAssignments.find({}).fetch(); + // + // let assetMap = assets.reduce((map, obj) => { + // map[obj.assetId] = obj; + // return map; + // }, {}); + // + // console.log(assetMap); + // console.log(""); + // + // for(let next of assetAssignments) { + // console.log(next); + // let asset = assetMap[next.assetId]; + // console.log("Updating " + asset.assetId + " to be assigned to " + next.assigneeType + ": " + next.assigneeId); + // let c = Assets.update({assetId: asset.assetId}, {$set: {assigneeType: next.assigneeType, assigneeId: next.assigneeId, assignmentDate}}); + // console.log("Updated " + c + " Assets"); + // console.log(Assets.findOne({assetId: asset.assetId})); + // } + // } } }); diff --git a/imports/api/index.js b/imports/api/index.js index 082304b..1066491 100644 --- a/imports/api/index.js +++ b/imports/api/index.js @@ -7,5 +7,6 @@ import "./sites.js"; import "./asset-types.js"; import "./assets.js"; import "./asset-assignments.js"; +import "./asset-assignment-history.js"; // console.log("Finished setting up server side models."); \ No newline at end of file diff --git a/imports/ui/Assets/AssignmentByAsset.svelte b/imports/ui/Assets/AssignmentByAsset.svelte index 081a485..72a8013 100644 --- a/imports/ui/Assets/AssignmentByAsset.svelte +++ b/imports/ui/Assets/AssignmentByAsset.svelte @@ -9,6 +9,8 @@ import {Assets} from "/imports/api/assets"; import {Students} from "/imports/api/students"; import {AssetTypes} from "/imports/api/asset-types"; + import Button, { Label } from '@smui/button'; + import Dialog, { Title, Content, Actions } from '@smui/dialog'; onMount(async () => { Meteor.subscribe('assets'); @@ -53,6 +55,11 @@ const formatDate = (date) => { return date.toLocaleDateString('en-us', {weekday: 'long', year: 'numeric', month: 'short', day: 'numeric'}); } + const unassign = () => { + if(confirm("Unassign Asset?")) { + Meteor.call("assets.unassign", foundAsset.assetId); + } + }
@@ -75,8 +82,26 @@
Assigned on: {formatDate(foundAsset.assignmentDate)}
Assigned to: {foundAssignee.firstName} {foundAssignee.lastName} {#if foundAssignee.grade} ~ {foundAssignee.grade} {/if}({foundAssignee.email})
+ + {/if} {/if} + + + + + + + + + + + + + +