diff --git a/backend/src/app.js b/backend/src/app.js
index e2893f6d..2c442a64 100644
--- a/backend/src/app.js
+++ b/backend/src/app.js
@@ -13,6 +13,7 @@ const districtRouter = require("./routes/district-router");
const downloadRouter = require("./routes/download-router");
const authorityRouter = require("./routes/authority-router");
const offshoreRouter = require("./routes/offshore-router");
+const schoolRouter = require("./routes/school-router");
const app = express();
const publicPath = path.join(__dirname, "public");
@@ -76,6 +77,7 @@ apiRouter.use("/v1/institute", instituteRouter);
apiRouter.use("/v1/district", districtRouter);
apiRouter.use("/v1/authority", authorityRouter);
apiRouter.use("/v1/offshore", offshoreRouter);
+apiRouter.use("/v1/school", schoolRouter);
//Handle 500 error
app.use((err, _req, res, next) => {
diff --git a/backend/src/components/cache.js b/backend/src/components/cache.js
index eb85035f..b10b797c 100644
--- a/backend/src/components/cache.js
+++ b/backend/src/components/cache.js
@@ -1,6 +1,8 @@
const NodeCache = require("node-cache");
const listCache = new NodeCache({ stdTTL: 21600 });
+const schoolCache = new NodeCache({ stdTTL: 21600 });
+const codeCache = new NodeCache({ stdTTL: 21600 });
module.exports = {
- listCache
+ listCache,schoolCache
};
\ No newline at end of file
diff --git a/backend/src/components/utils.js b/backend/src/components/utils.js
index 985ed6f5..7964d73c 100644
--- a/backend/src/components/utils.js
+++ b/backend/src/components/utils.js
@@ -20,9 +20,20 @@ const ALLOWED_FILENAMES = new Set([
'chairperson',
'secretary-treasurer',
'executive-admin-assistant',
- 'exceldistrictcontacts'
+ 'exceldistrictcontacts',
+ 'publicschoolcontacts',
+ 'independschoolcontacts',
+ 'allschoolcontacts'
// Add more allowed filepaths as needed
]);
+const ALLOWED_SCHOOLCATEGORYCODES = new Set([
+ 'PUBLIC',
+ 'INDEPEND'
+ // Add more allowed filepaths as needed
+]);
+function isAllowedSchoolCategory(category) {
+ return ALLOWED_SCHOOLCATEGORYCODES.has(category);
+}
function isSafeFilePath(filepath) {
return ALLOWED_FILENAMES.has(filepath);
}
@@ -90,5 +101,138 @@ function addDistrictLabels(jsonData, districtList) {
}
return 0;
}
-
- module.exports = { createList, isSafeFilePath, addDistrictLabels, districtNumberSort };
\ No newline at end of file
+
+ function rearrangeAndRelabelObjectProperties(object, propertyList) {
+ const reorderedObject = {};
+ propertyList.forEach((propertyInfo) => {
+ const prop = propertyInfo.property;
+ const label = propertyInfo.label;
+ reorderedObject[label] = object.hasOwnProperty(prop) ? object[prop] : "";
+ });
+ return reorderedObject;
+ }
+ function createSchoolCache(schoolData, schoolGrades) {
+ // Preload convertedGrades with schoolGrades.schoolGradeCode and set the value to "N"
+
+
+ // Map over each school object
+ return schoolData.map((school) => {
+ const convertedGrades = {};
+ schoolGrades.forEach((grade) => {
+ convertedGrades[grade.schoolGradeCode] = "N";
+ });
+
+ const addressFields = {
+ mailing: {},
+ physical: {},
+ };
+
+ // Loop through the grades and set the value to "Y" for each grade
+ school.grades.forEach((grade) => {
+ convertedGrades[grade.schoolGradeCode] = "Y";
+ });
+
+ // Extract and format principal contact information if it exists
+ const principalContact = school.contacts.find((contact) => contact.schoolContactTypeCode === "PRINCIPAL");
+ if (principalContact) {
+ school.firstName = principalContact.firstName;
+ school.lastName = principalContact.lastName;
+ school.email = principalContact.email;
+ school.phoneNumber = principalContact.phoneNumber;
+ }
+
+ // Loop through addresses and update the fields based on addressTypeCode
+ school.addresses.forEach((address) => {
+ if (address.addressTypeCode === "MAILING") {
+ Object.keys(address).forEach((field) => {
+ // Exclude the specified fields
+ if (![
+ "createUser",
+ "updateUser",
+ "createDate",
+ "updateDate",
+ "schoolAddressId",
+ "schoolId",
+ "addressTypeCode"
+ ].includes(field)) {
+ addressFields.mailing[`mailing_${field}`] = address[field];
+ }
+ });
+ } else if (address.addressTypeCode === "PHYSICAL") {
+ Object.keys(address).forEach((field) => {
+ if (![
+ "createUser",
+ "updateUser",
+ "createDate",
+ "updateDate",
+ "schoolAddressId",
+ "schoolId",
+ "addressTypeCode"
+ ].includes(field)) {
+ addressFields.mailing[`physical_${field}`] = address[field];
+ }
+ });
+ }
+ });
+
+ // Concatenate neighborhoodLearningTypeCode into a single string
+ const nlc = school.neighborhoodLearning.map(learning => learning.neighborhoodLearningTypeCode).join(' | ');
+
+ // Merge the address fields and nlc into the school object
+ Object.assign(school, convertedGrades, addressFields.mailing, addressFields.physical, { nlc });
+
+ // Remove the original grades property and the updated address object
+ delete school.grades;
+ delete school.addresses;
+ delete school.neighborhoodLearning;
+ delete school.createUser;
+ delete school.updateUser;
+ delete school.updateDate;
+ delete school.createDate;
+ delete school.schoolId;
+ delete school.openedDate;
+ delete school.closedDate;
+ delete school.notes;
+ delete school.schoolMove.createUser;
+ delete school.schoolMove;
+
+ // Remove the contacts property
+ delete school.contacts;
+ const propertyOrder = [
+ { property: "districtNumber", label: "District Number" },
+ { property: "mincode", label: "School Code" },
+ { property: "displayName", label: "School Name" },
+ { property: "mailing_addressLine1", label: "Address" },
+ { property: "mailing_city", label: "City" },
+ { property: "mailing_provinceCode", label: "Province" },
+ { property: "mailing_postal", label: "Postal Code" },
+ // { property: "principalTitle", label: "Principal Title" },
+ { property: "firstName", label: "Principal First Name" },
+ { property: "lastName", label: "Principal Last Name" },
+ { property: "schoolCategoryCode", label: "Type" },
+ // { property: "gradeRange", label: "Grade Range" },
+ // { property: "schoolCategory", label: "School Category" },
+ // { property: "fundingGroups", label: "Funding Group(s)" },
+ { property: "phoneNumber", label: "Phone" },
+ // { property: "fax", label: "Fax" },
+ { property: "email", label: "Email" },
+ { property: "GRADE01", label: "Grade 1 Enrollment" },
+ { property: "GRADE02", label: "Grade 2 Enrollment" },
+ { property: "GRADE03", label: "Grade 3 Enrollment" },
+ { property: "GRADE04", label: "Grade 4 Enrollment" },
+ { property: "GRADE05", label: "Grade 5 Enrollment" },
+ { property: "GRADE06", label: "Grade 6 Enrollment" },
+ { property: "GRADE07", label: "Grade 7 Enrollment" },
+ { property: "GRADE08", label: "Grade 8 Enrollment" },
+ { property: "GRADE09", label: "Grade 9 Enrollment" },
+ { property: "GRADE10", label: "Grade 10 Enrollment" },
+ { property: "GRADE11", label: "Grade 11 Enrollment" },
+ { property: "GRADE12", label: "Grade 12 Enrollment" }
+
+
+ ];
+ const schools = rearrangeAndRelabelObjectProperties(school,propertyOrder)
+ return schools;
+ });
+}
+ module.exports = { createList, isSafeFilePath,isAllowedSchoolCategory, addDistrictLabels, districtNumberSort, createSchoolCache };
\ No newline at end of file
diff --git a/backend/src/routes/download-router.js b/backend/src/routes/download-router.js
index 95b0a78f..7dc4a14e 100644
--- a/backend/src/routes/download-router.js
+++ b/backend/src/routes/download-router.js
@@ -47,13 +47,16 @@ async function addDistrictLabels(req, res, next) {
let districtList = [];
- if (listCache.has("districtList")) {
- districtList= listCache.get("districtList");
+ if (listCache.has("districtlist")) {
+ districtList= listCache.get("districtlist");
} else {
try {
- const response = await axios.get('http://localhost:8080/api/v1/institute/district/list', { headers: { Authorization: `Bearer ${req.accessToken}` } });
+ const path = "/api/v1/institute/district/list"
+ const url = `${req.protocol}://${req.hostname}:8080${path}`;
+
+ const response = await axios.get(url, { headers: { Authorization: `Bearer ${req.accessToken}` } });
const districts = response.data;
- listCache.set("districtList", districts);
+ listCache.set("districtlist", districts);
districtList = districts
} catch (error) {
@@ -99,10 +102,17 @@ async function getDownload(req, res,next){
return res.sendFile(filePath);
}else{
try {
- const url = `${config.get('server:instituteAPIURL')}` + req.url.replace('/csv', '');
+ const path = req.url.replace('/csv', ''); // Modify the URL path as needed
+ const url = `${req.protocol}://${req.hostname}:8080/api/v1${path}`;
+ console.log(url)
const response = await axios.get(url, { headers: { Authorization: `Bearer ${req.accessToken}` } });
// Attach the fetched data to the request object
- req.jsonData = response.data.content;
+ if (response.data?.content) {
+ req.jsonData = response.data.content;
+ }else{
+ req.jsonData = response.data;
+ }
+
next(); // Call the next middleware
} catch (error) {
console.error("Error:", error);
diff --git a/backend/src/routes/institute-router.js b/backend/src/routes/institute-router.js
index 3b4d5b8f..4ce162fb 100644
--- a/backend/src/routes/institute-router.js
+++ b/backend/src/routes/institute-router.js
@@ -5,16 +5,34 @@ const config = require("../config/index");
const axios = require("axios");
const { checkToken } = require("../components/auth");
-const { createList, addDistrictLabels, districtNumberSort } = require("../components/utils");
+const { createList, addDistrictLabels, districtNumberSort, isAllowedSchoolCategory } = require("../components/utils");
const { listCache } = require("../components/cache");
const schoolListOptions = { fields: ["mincode", "displayName", "schoolId"], fieldToInclude: "closedDate", valueToInclude: null, sortField: "mincode" };
const districtListOptions = { fields: ["displayName", "districtId","districtNumber"] ,fieldToInclude: "districtStatusCode", valueToInclude: "ACTIVE", sortField: "districtNumber"};
const authorityListOptions = { fields: ["displayName", "authorityNumber","independentAuthorityId"], fieldToInclude: "closedDate", valueToInclude: null, sortField: "authorityNumber" };
+const openSchoolListOptions = { fields: [
+ "schoolId",
+ "districtId",
+ "mincode",
+ "schoolNumber",
+ "faxNumber",
+ "phoneNumber",
+ "email",
+ "website",
+ "displayName",
+ "schoolCategoryCode",
+ "facilityTypeCode",
+ "openedDate",
+ "closedDate",
+ "districtNumber"
+],fieldToInclude: "closedDate", valueToInclude: null, sortField: "mincode" };
//Batch Routes
router.get("/contact-type-codes", checkToken, getContactTypeCodes);
+router.get("/grade-codes", checkToken, getGradeCodes);
router.get("/offshore-school/list", checkToken, getOffshoreSchoolList);
+// router.get("/school", checkToken, getOpenSchools);
router.get("/school/list", checkToken, getSchoolList);
router.get("/authority/list", checkToken, getAuthorityList);
router.get("/district/list", checkToken, getDistrictList);
@@ -123,6 +141,30 @@ async function getAuthorityList(req, res) {
res.json(authorityList);
}
}
+async function getOpenSchools(req, res) {
+
+
+ if (await !listCache.has("openschoollist")) {
+ const url = `${config.get("server:instituteAPIURL")}/institute/school`; // Update the URL according to your API endpoint
+ axios
+ .get(url, { headers: { Authorization: `Bearer ${req.accessToken}` } })
+ .then((response) => {
+ const openSchoolList = createList(response.data, openSchoolListOptions);
+ res.json(openSchoolList);
+ listCache.set("openschoollist", openSchoolList);
+ log.info(req.url);
+ })
+ .catch((e) => {
+ log.error(
+ "getSchoolsList Error",
+ e.response ? e.response.status : e.message
+ );
+ });
+ } else {
+ const openSchoolList = await listCache.get("openschoollist");
+ res.json(openSchoolList);
+ }
+}
async function getSchoolList(req, res) {
if (await !listCache.has("schoollist")) {
const url = `${config.get("server:instituteAPIURL")}/institute/school`; // Update the URL according to your API endpoint
@@ -201,4 +243,28 @@ async function getDistrictContactsAPI(req, res) {
log.error("getData Error", e.response ? e.response.status : e.message);
});
}
+
+async function getGradeCodes(req, res) {
+ if (await !codeCache.has("gradelist")) {
+ const url = `${config.get("server:instituteAPIURL")}/institute/grade-codes`; // Update the URL according to your API endpoint
+ axios
+ .get(url, { headers: { Authorization: `Bearer ${req.accessToken}` } })
+ .then((response) => {
+ //const districtList = response.data;
+ const gradeCodes = response.data;
+ codeCache.set("gradelist", gradeList);
+ res.json(gradeCodes);
+ log.info(req.url);
+ })
+ .catch((e) => {
+ log.error(
+ "getDistrictList Error",
+ e.response ? e.response.status : e.message
+ );
+ });
+ } else {
+ const gradeList = await codeCache.get("gradelist");
+ res.json(gradeList);
+ }
+}
module.exports = router;
diff --git a/frontend/src/components/AuthoritySelect.vue b/frontend/src/components/AuthoritySelect.vue
index 1016584c..f51ef6e0 100644
--- a/frontend/src/components/AuthoritySelect.vue
+++ b/frontend/src/components/AuthoritySelect.vue
@@ -53,6 +53,14 @@ function downloadAuthorityContacts() {
/>
+
+
+ All Independent Schools
Contacts for All
Authorities
-
diff --git a/frontend/src/components/SchoolSelect.vue b/frontend/src/components/SchoolSelect.vue
index d71915b3..57f4f3d6 100644
--- a/frontend/src/components/SchoolSelect.vue
+++ b/frontend/src/components/SchoolSelect.vue
@@ -30,10 +30,6 @@ function goToSchoolSearch() {
})
}
-function downloadAllSchoolsInfo() {
- alert('TODO - Implement all schools info extract download')
-}
-
function downloadAllSchoolsMailing() {
alert('TODO - Implement all schools mailing extract download')
}
@@ -93,9 +89,13 @@ function downloadAllSchoolsMailing() {
- All Schools
- Info
+
+ All Schools Info