Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
0a2ca21
Changes to list overview in Clients and Need Occurences
Damon-D-Ma Jan 15, 2024
830c727
Revert local backend index.js changes
Damon-D-Ma Jan 15, 2024
735256f
Base Code for Service Waitlist Type
Damon-D-Ma Feb 2, 2024
e91597d
Completed Service Waitlist Feature
Damon-D-Ma Feb 12, 2024
1739f27
Add capacity to service and program occurrences
htysc Feb 25, 2024
a1ec584
Add occupancy to service and program occurrences
htysc Mar 1, 2024
9910ac4
Add occurrence status (string for now)
htysc Mar 2, 2024
69de1f4
Use options for program and service occurrences
htysc Mar 2, 2024
92e6d45
Let capacities be unlimited
htysc Mar 2, 2024
6b2e375
Set occupancy to 0 on creation
htysc Mar 2, 2024
2839427
Bugfix on NumberField & partial CO deletion.
LesterLyu Mar 4, 2024
193163b
Registration statuses
htysc Mar 5, 2024
dd34179
Customized service registration status options
htysc Mar 5, 2024
652e572
Customized program registration status options
htysc Mar 5, 2024
d95b58a
Registration status option fixes
htysc Mar 5, 2024
74adf7a
Service registration status backend (except for waitlists)
htysc Mar 6, 2024
76a096f
Merge branch 'master' into capacities
htysc Mar 6, 2024
fa09dde
Fix bugs
htysc Mar 6, 2024
07bb558
Program registration status backend (except for waitlists)
htysc Mar 6, 2024
0f232af
Fix bugs
htysc Mar 6, 2024
a660b8d
Update waitlist TODOs
htysc Mar 6, 2024
00dd1f3
Updates to Waitlist + New Entry Model
Damon-D-Ma Mar 6, 2024
708cbf0
Added Required Functions to Manipulate Service Waitlists
Damon-D-Ma Mar 7, 2024
f926dc1
Fix occupancy update bug
htysc Mar 11, 2024
ea4ef26
Added auto-create waitlist for serviceOccurrence
Damon-D-Ma Mar 12, 2024
1e55e15
Updated waitlistEntryModel
Damon-D-Ma Mar 12, 2024
fa466b4
Forgot to fix import statements
Damon-D-Ma Mar 12, 2024
cb0d9dd
Changes to backend waitlistEntry Components
Damon-D-Ma Mar 12, 2024
cf4e8a6
Changes to waitlistEntry Structure
Damon-D-Ma Mar 12, 2024
45ce943
Forgot to Rename serviceWaitlist Attribute
Damon-D-Ma Mar 12, 2024
b34b528
Updated waitlist functions
Damon-D-Ma Mar 12, 2024
23f989b
Implemented Changes From Code Review
Damon-D-Ma Mar 15, 2024
5b67143
More Fixes From Code Review
Damon-D-Ma Mar 15, 2024
ca862ae
Created Models for program waitlists + entries
Damon-D-Ma Mar 15, 2024
bc88bed
Added Program Waitlists
Damon-D-Ma Mar 15, 2024
2eb86a4
Use waitlist functions (not fully tested yet)
htysc Mar 15, 2024
e19c7b6
Fix bugs
htysc Mar 18, 2024
63b4839
Implement service occurrence capacity change checks
htysc Mar 18, 2024
1c99ac1
Waitlists for program occurrences
htysc Mar 18, 2024
5c40d5f
Fix bug
htysc Mar 18, 2024
8c1a524
Added functionality for programWaitlist, code style changes
Damon-D-Ma Mar 20, 2024
ec1832e
Fix bugs
htysc Mar 20, 2024
5422bce
Prevent updating a registration's occurrence
htysc Mar 20, 2024
194de23
Added New General FAQ Guide for Codebase Newcomers
Damon-D-Ma Mar 22, 2024
8c94e97
Guide got ignored by gitignore for some reason
Damon-D-Ma Mar 22, 2024
6d91055
Merge branch 'master' into capacities
LesterLyu Mar 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ if (isProduction)

const config = {
graphdb: {
addr: isProduction ? 'http://localhost:7200' : `http://${isDocker ? 'host.docker.internal' : 'localhost'}:7200`,
addr: isProduction ? 'http://127.0.0.1:7200' : `http://${isDocker ? 'host.docker.internal' : '127.0.0.1'}:7200`,
},
mongodb: {
addr: isProduction ? 'mongodb://localhost:27017/snmi' : `mongodb://localhost:27017/${process.env.test ? "snmiTest" : "snmi"}`
Expand Down
3 changes: 3 additions & 0 deletions backend/loaders/express.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ app.use('/public', partnerNetworkPublicRoute);
await initOptions('Appointment Statuses',
["Requested", "Confirmed", "Cancelled", "Fulfilled", "Client No Show", "Postponed"],
'AppointmentStatus', 'appointmentStatus');
await initOptions('Service and Program Occurrence Statuses',
["Registered", "Not Registered", "Waitlisted"],
'OccurrenceStatus', 'occurrenceStatus');
})()


Expand Down
3 changes: 3 additions & 0 deletions backend/models/program/programOccurrence.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const GDBProgramOccurrenceModel = createGraphDBModel({
needSatisfiers: {type: [GDBNeedSatisfierModel], internalKey: ':hasNeedSatisfier'},
// needSatisfierOccurrence: {type: [GDBNeedSatisfierOccurrenceModel], internalKey: ':hasNeedSatisfierOccurrence', onDelete: DeleteType.CASCADE},
description: {type: String, internalKey: 'cids:hasDescription'},
capacity: {type: Number, internalKey: ':hasCapacity'},
occupancy: {type: Number, internalKey: ':hasOccupancy'},
status: {type: String, internalKey: ':hasOccurrenceStatus'},
characteristicOccurrences: {type: [GDBCOModel], internalKey: ':hasCharacteristicOccurrence'}
}, {
rdfTypes: [':ProgramOccurrence'], name: 'programOccurrence'
Expand Down
3 changes: 3 additions & 0 deletions backend/models/service/serviceOccurrence.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const GDBServiceOccurrenceModel = createGraphDBModel({
needSatisfiers: {type: [GDBNeedSatisfierModel], internalKey: ':hasNeedSatisfier'},
// needSatisfierOccurrence: {type: [GDBNeedSatisfierOccurrenceModel], internalKey: ':hasNeedSatisfierOccurrence', onDelete: DeleteType.CASCADE},
description: {type: String, internalKey: 'cids:hasDescription'},
capacity: {type: Number, internalKey: ':hasCapacity'},
occupancy: {type: Number, internalKey: ':hasOccupancy'},
status: {type: String, internalKey: ':hasOccurrenceStatus'},
characteristicOccurrences: {type: [GDBCOModel], internalKey: ':hasCharacteristicOccurrence'}
}, {
rdfTypes: [':ServiceOccurrence'], name: 'serviceOccurrence'
Expand Down
26 changes: 26 additions & 0 deletions backend/models/service/serviceWaitlist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const {createGraphDBModel, DeleteType, Types} = require("graphdb-utils");




//These might need tweaking, need to figure out what things we need for this model
const {GDBServiceModel} = require("./service");
const {GDBClientModel} = require("../ClientFunctionalities/client");
const { GDBCOModel } = require("../ClientFunctionalities/characteristicOccurrence");


const GDBServiceWaitlistModel = createGraphDBModel({

clients: {type: [GDBClientModel], internalKey: ':hasClient'},
service: {type: GDBServiceModel, internalKey: ':hasService'},
characteristicOccurences: {type: [GDBCOModel], internalKey: ':hasCharacteristicOccurence'}

},
{
rdfTypes: [':ServiceWaitlist'], name: 'serviceWaitlist'
}

);
module.exports = {
GDBServiceWaitlistModel
}
31 changes: 31 additions & 0 deletions backend/services/characteristics/predefined/general.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,35 @@ module.exports = [{
fieldType: FieldTypes.NumberField,
},
},
{
name: 'Capacity',
description: 'The capacity of a program or service occurrence.',
predefinedProperty: 'http://snmi#hasCapacity',
implementation: {
label: 'Capacity',
valueDataType: 'xsd:number',
fieldType: FieldTypes.NumberField,
},
},
{
name: 'Occupancy',
description: 'The current occupancy of a program or service occurrence.',
predefinedProperty: 'http://snmi#hasOccupancy',
implementation: {
label: 'Occupancy',
valueDataType: 'xsd:number',
fieldType: FieldTypes.NumberField,
},
},
{
name: 'Occurrence Status',
description: 'The status of a program or service occurrence.',
predefinedProperty: 'http://snmi#hasOccurrenceStatus',
implementation: {
label: 'Occurrence Status',
valueDataType: 'xsd:string',
fieldType: FieldTypes.SingleSelectField,
optionsFromClass: ':OccurrenceStatus'
}
},
]
29 changes: 29 additions & 0 deletions backend/services/characteristics/predefined/internalTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,35 @@ module.exports = [
}
},



// below are for servic waitlist
{
name: 'clientForServiceWaitlist',
predefinedProperty: 'http://snmi#hasClient',
formType: 'serviceWaitlist',
implementation: {
label: 'Client',
valueDataType: 'owl:NamedIndividual',
fieldType: FieldTypes.MultiSelectField,
optionsFromClass: 'http://snmi#Client'
}
},
{
name: 'serviceForServiceWaitlist',
predefinedProperty: 'http://snmi#hasService',
formType: 'serviceWaitlist',
implementation: {
label: 'Service',
valueDataType: 'owl:NamedIndividual',
fieldType: FieldTypes.SingleSelectField,
optionsFromClass: 'http://snmi#Service'
}
},




// below are for program Provision
{
name: 'needOccurrenceForProgramProvision',
Expand Down
21 changes: 20 additions & 1 deletion backend/services/genericData/checkers.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
const {GDBCharacteristicModel} = require("../../models");
const {Server400Error} = require("../../utils");
const {PredefinedCharacteristics} = require("../characteristics");

function noQuestion(characteristics, questions) {
if (Object.keys(questions).length > 0)
throw new Server400Error('Service should not contain question.');
}

module.exports = {noQuestion}
function checkCapacity(characteristics, questions, fields) {
const capacityId = Object.keys(characteristics).find(id => characteristics[id].name === 'Capacity');
if (!!capacityId) {
if (fields['characteristic_' + capacityId] < 0) {
throw new Server400Error('Capacity must be zero or greater.');
}
}
}

async function setOccupancy(characteristics, questions, fields) {
const occupancyId = PredefinedCharacteristics['Occupancy']._id;
const occupancyCharacteristic = await GDBCharacteristicModel.findOne({_id: occupancyId},
{populates: ['implementation']});
characteristics[occupancyId] = occupancyCharacteristic;
fields[`characteristic_${occupancyId}`] = '0';
}

module.exports = {noQuestion, checkCapacity, setOccupancy}
46 changes: 35 additions & 11 deletions backend/services/genericData/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ const {GDBAppointmentModel} = require("../../models/appointment");
const {GDBPersonModel} = require("../../models/person");
const {GDBServiceOccurrenceModel} = require("../../models/service/serviceOccurrence");
const {GDBProgramOccurrenceModel} = require("../../models/program/programOccurrence");

const {GDBServiceWaitlistModel} = require("../../models/service/serviceWaitlist");

const {GDBInternalTypeModel} = require("../../models/internalType");
const {noQuestion} = require('./checkers')
const {noQuestion, checkCapacity, setOccupancy} = require('./checkers')
const {
serviceOccurrenceInternalTypeCreateTreater, serviceOccurrenceInternalTypeFetchTreater,
serviceOccurrenceInternalTypeUpdateTreater
Expand Down Expand Up @@ -123,6 +126,7 @@ const {
afterDeleteVolunteer
} = require("./volunteerInternalTypeTreater");
const {GDBEligibilityModel} = require("../../models/eligibility");
const { serviceWaitlistInternalTypeCreateTreater, serviceWaitlistInternalTypeFetchTreater, serviceWaitlistInternalTypeUpdateTreater } = require("./serviceWaitlist");
const genericType2Model = {
'client': GDBClientModel,
'organization': GDBOrganizationModel,
Expand All @@ -131,6 +135,7 @@ const genericType2Model = {
'program': GDBProgramModel,
'appointment': GDBAppointmentModel,
'serviceOccurrence': GDBServiceOccurrenceModel,
'serviceWaitlist': GDBServiceWaitlistModel,
'programOccurrence': GDBProgramOccurrenceModel,
'referral': GDBReferralModel,
'serviceRegistration': GDBServiceRegistrationModel,
Expand All @@ -157,18 +162,28 @@ const genericType2Populates = {
'service': ['serviceProvider.organization.address', 'serviceProvider.volunteer.address'],
'program': ['serviceProvider.organization.address', 'serviceProvider.volunteer.address', 'serviceProvider.organization', 'serviceProvider.volunteer', 'manager'],
'serviceOccurrence': ['address', 'occurrenceOf'],
'serviceWaitlist': ['service', 'clients'],
'programOccurrence': ['address', 'occurrenceOf'],
'client': ['address', 'needs'],
'appointment': ['address', 'referral'],
'person': ['address'],
'volunteer': ['partnerOrganizations', 'organization', 'address'],
};

const genericType2Checker = {
'service': noQuestion,
'serviceOccurrence': noQuestion,
'programOccurrence': noQuestion,
'program': noQuestion
const genericType2BeforeCreateChecker = {
'service': [noQuestion],
'serviceOccurrence': [noQuestion, checkCapacity, setOccupancy],
'programOccurrence': [noQuestion, checkCapacity, setOccupancy],
'program': [noQuestion],
'serviceWaitlist': [noQuestion],
};

const genericType2BeforeUpdateChecker = {
'service': [noQuestion],
'serviceOccurrence': [noQuestion, checkCapacity],
'programOccurrence': [noQuestion, checkCapacity],
'program': [noQuestion],
'serviceWaitlist': [noQuestion],
};


Expand Down Expand Up @@ -203,7 +218,8 @@ const genericType2InternalTypeCreateTreater = {
'outcomeOccurrence': outcomeOccurrenceInternalTypeCreateTreater,
'clientAssessment': clientAssessmentInternalTypeCreateTreater,
'person': personInternalTypeCreateTreater,
'volunteer': volunteerInternalTypeCreateTreater
'volunteer': volunteerInternalTypeCreateTreater,
'serviceWaitlist': serviceWaitlistInternalTypeCreateTreater
};

const genericType2InternalTypeFetchTreater = {
Expand All @@ -222,7 +238,9 @@ const genericType2InternalTypeFetchTreater = {
'outcomeOccurrence': outcomeOccurrenceInternalTypeFetchTreater,
'clientAssessment': clientAssessmentInternalTypeFetchTreater,
'person': personInternalTypeFetchTreater,
'volunteer': volunteerInternalTypeFetchTreater
'volunteer': volunteerInternalTypeFetchTreater,
'serviceWaitlist': serviceWaitlistInternalTypeFetchTreater

};

const genericType2InternalTypeUpdateTreater = {
Expand All @@ -241,7 +259,8 @@ const genericType2InternalTypeUpdateTreater = {
'outcomeOccurrence': outcomeOccurrenceInternalTypeUpdateTreater,
'clientAssessment': clientAssessmentInternalTypeUpdateTreater,
'person': personInternalTypeUpdateTreater,
'volunteer': volunteerInternalTypeUpdateTreater
'volunteer': volunteerInternalTypeUpdateTreater,
'serviceWaitlist': serviceWaitlistInternalTypeUpdateTreater
};

const genericType2AfterCreateTreater = {
Expand Down Expand Up @@ -416,8 +435,9 @@ const createSingleGenericHelper = async (data, genericType) => {

// extract questions and characteristics based on fields from the database
await fetchCharacteristicQuestionsInternalTypesBasedOnForms(characteristics, questions, internalTypes, data.fields);
if (genericType2Checker[genericType])
genericType2Checker[genericType](characteristics, questions);
if (genericType2BeforeCreateChecker[genericType])
for (const checker of genericType2BeforeCreateChecker[genericType])
await checker(characteristics, questions, data.fields);

const instanceData = {characteristicOccurrences: [], questionOccurrences: []};
// iterating over all fields and create occurrences and store them into instanceData
Expand Down Expand Up @@ -535,6 +555,10 @@ async function updateSingleGenericHelper(genericId, data, genericType) {
const internalTypes = {};
await fetchCharacteristicQuestionsInternalTypesBasedOnForms(characteristics, questions, internalTypes, data.fields);

if (genericType2BeforeUpdateChecker[genericType])
for (const checker of genericType2BeforeUpdateChecker[genericType])
await checker(characteristics, questions, data.fields);

// check should we update or create a characteristicOccurrence or questionOccurrence
// in other words, is there a characteristicOccurrence/questionOccurrence belong to this user,
// and related to the characteristic/question
Expand Down
34 changes: 34 additions & 0 deletions backend/services/genericData/serviceWaitlist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const {getPredefinedProperty} = require("./helperFunctions");
const {GDBInternalTypeModel} = require("../../models/internalType");
const {SPARQL} = require("graphdb-utils");
const FORMTYPE = 'serviceWaitlist'

const serviceWaitlistInternalTypeCreateTreater = async (internalType, instanceData, value) => {
//get the property name from the internalType
const property = getPredefinedProperty(FORMTYPE, internalType);
//if the property is client, person or user, then set the value to the instanceData
if (property === 'clients' || property === 'service'){
instanceData[property] = value;
}
};

const serviceWaitlistInternalTypeFetchTreater = async (data) => {
const result = {};
const schema = data.schema;
// for each property in data, if the property is client, person or user, then set the value to the result
for (const property in data) {
if (property === 'clients' || property === 'service') {
const internalType = await GDBInternalTypeModel.findOne({predefinedProperty: schema[property].internalKey, formType: FORMTYPE});
result[ 'internalType_'+ internalType._id] = SPARQL.ensureFullURI(data[property]);
}
}
return result;
};


const serviceWaitlistInternalTypeUpdateTreater = async (internalType, value, result) => {
await serviceWaitlistInternalTypeCreateTreater(internalType, result, value);
}


module.exports = {serviceWaitlistInternalTypeCreateTreater, serviceWaitlistInternalTypeFetchTreater, serviceWaitlistInternalTypeUpdateTreater}
69 changes: 69 additions & 0 deletions frontend/src/components/ServiceWaitlists.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from 'react';
import { Link } from './shared';
import { GenericPage } from "./shared";
import { deleteSingleGeneric, fetchMultipleGeneric, fetchSingleGeneric } from "../api/genericDataApi";
import {getAddressCharacteristicId} from "./shared/CharacteristicIds";

const TYPE = 'serviceWaitlist';

const columnsWithoutOptions = [
{
label: 'Service Name',
body: ({service}) => <Link color to={`/${TYPE}/${service?._id}/edit`}>{service?.name}</Link>

},
{
label: 'Clients Waitlisted',
body: ({clients}) => clients ? clients.length : 0
},
// {
// label: 'Category',
// body: ({category}) => category
// }
];

export default function ServiceWaitlists() {

const nameFormatter = serviceWaitlist => {
if (serviceWaitlist.description) {
return serviceWaitlist.description;
} else {
return 'Service Waitlist ' + serviceWaitlist._id;
}
}

const linkFormatter = serviceWaitlist => `/${TYPE}/${serviceWaitlist._id}`;

const fetchData = async () => {
const addressCharacteristicId = await getAddressCharacteristicId();
const serviceWaitlists = (await fetchMultipleGeneric(TYPE)).data;
console.log(serviceWaitlists)
const data = [];
for (const serviceWaitlist of serviceWaitlists) {
const serviceWaitlistData = {_id: serviceWaitlist._id};
if (serviceWaitlist.clients)
serviceWaitlistData.clients = serviceWaitlist.clients;
if (serviceWaitlist.service)
serviceWaitlistData.service = serviceWaitlist.service;
data.push(serviceWaitlistData);
}
return data;
};

const deleteService = (id) => deleteSingleGeneric('serviceWaitlist', id);

return (
<GenericPage
type={TYPE}
title={"Service Waitlists"}
columnsWithoutOptions={columnsWithoutOptions}
fetchData={fetchData}
deleteItem={deleteService}
nameFormatter={nameFormatter}
linkFormatter={linkFormatter}
tableOptions={{
idField: '_id'
}}
/>
);
}
Loading