Skip to content

Commit

Permalink
Merge pull request #171 from waterloop/steven.xiong/additions
Browse files Browse the repository at this point in the history
Draft: Steven.xiong/additions
  • Loading branch information
SteveShengStar committed Mar 30, 2023
2 parents 1bc8bc4 + f5e06d4 commit c9552e3
Show file tree
Hide file tree
Showing 17 changed files with 350 additions and 485 deletions.
2 changes: 1 addition & 1 deletion backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ Deletes a member with the specified ID.
},
"currentSchoolTerm": "1B"
},
"phone": "0000000000",
"phoneNumber": "0000000000",
"studentId": "77"
}
}
Expand Down
77 changes: 0 additions & 77 deletions backend/data/handlers/constants.js

This file was deleted.

185 changes: 92 additions & 93 deletions backend/data/handlers/googlesheets.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
const { OAuth2Client } = require('google-auth-library');
const Member = require('../schema/Member');
const authConfig = require('./auth.config.json');
const { google } = require('googleapis');
const { getAll } = require('./members');

const {
RETURNING_MEMBERS_FIELDS,
TEAM_ROSTER_FIELDS,
START_TERM_FIELDS,
RETURNING_MEMBERS_SPREADSHEET_HEADERS,
TEAM_ROSTER_SPREADSHEET_HEADERS,
START_TERM_SPREADSHEET_HEADERS,
} = require('./constants');
const { getAll: getAllMembersData } = require('./members');
const { fetchFormAndMemberData } = require('./forms');

const googlesheets = {};

/**
* Note: This function is currently not in use. It is here just for tesing purposes.
*/
googlesheets.readfile = async (token) => {
const client = new OAuth2Client(authConfig['client_id']);
client.setCredentials({
Expand All @@ -28,97 +24,100 @@ googlesheets.readfile = async (token) => {
return metadata;
};

googlesheets.writefile = async (token, formName) => {
switch (formName) {
case 'register':
case 'startofterm':
return exportRegister(token);
case 'returning':
return exportReturning(token);
}
googlesheets.writefile = async (userId, token, formName) => {
return exportDataToGoogleSheets(userId, token, formName);
};

const exportRegister = async (token) => {
const fields = getFields(TEAM_ROSTER_FIELDS);
const userData = (await getAll(fields)).map((row) => {
return {
fullName: row.name.first + ' ' + row.name.last,
email: row.email ?? '',
phoneNumber: row.miscDetails?.phone ?? '',
memberType: row.memberType?.name ?? '',
program: row.program ?? '',
skills: row.skills?.map((skill) => skill.name).join() ?? '',
subteam:
row.subteams && row.subteams.length > 0
? row.subteams[0].name
: '',
termStatus: row.miscDetails?.termStatus ?? '',
activeSchoolTerms: row.activeSchoolTerms?.join() ?? '',
ssdc: row.miscDetails?.designCentreSafety ? 'yes' : 'no',
whmis: row.miscDetails?.whmis ? 'yes' : 'no',
machineShop: row.miscDetails?.machineShop ? 'yes' : 'no',
};
});

const spreadsheetData = [TEAM_ROSTER_SPREADSHEET_HEADERS];
spreadsheetData.push(...userData.map((data) => Object.values(data)));

//create new file with Drive api
const currentDate = new Date();
const fileName = `Waterloop Roster - ${currentDate.toLocaleString('en-CA', {
timeZone: 'EST',
})}`;
const fileMetadata = {
name: fileName,
parents: [],
mimeType: 'application/vnd.google-apps.spreadsheet',
};
const driveRequest = {
resource: fileMetadata,
fields: 'id',
};
const googleDrive = getGoogleDriveClient(token);
const driveResponse = await googleDrive.files.create(driveRequest);

// update spreadsheet
const request = {
spreadsheetId: driveResponse.data.id,
range: 'Sheet1',
valueInputOption: 'USER_ENTERED',
resource: {
values: spreadsheetData,
},
};
const googleSheets = getGoogleSheetsClient(token);
const response = await googleSheets.spreadsheets.values.update(request);
return response.config.url;
const exportDataToGoogleSheets = async (userId, token, formName) => {
const formAndMemberData = await fetchFormAndMemberData(userId, formName);
const headerText = formAndMemberData.form.sections.map(
(section) => section.section.display
);
const memberData = await getFormsAndMembersData(formAndMemberData);
const spreadsheetData = [headerText, ...memberData];

return await createGoogleSheetsFile(
formAndMemberData.form.title,
spreadsheetData,
token
);
};

const exportReturning = async (token) => {
const fields = getFields(RETURNING_MEMBERS_FIELDS);
const userData = (await getAll(fields)).map((row) => {
return {
email: row.email ?? '',
upcomingTerm: row.miscDetails?.nextSchoolTerm ?? '',
activeSchoolTerms: row.activeSchoolTerms?.join() ?? '',
subteam:
row.subteams && row.subteams.length > 0
? row.subteams[0].name
: '',
nextTermActivity: row.miscDetails?.nextTermActivity ?? '',
nextTermRole: row.miscDetails?.nextTermRole ?? '',
personalEmail: row.miscDetails?.personalEmail ?? '',
termComments: row.miscDetails?.termComments ?? '',
desiredWork: row.miscDetails?.desiredWork ?? '',
};
const getFormsAndMembersData = async (formAndMemberData) => {
const fieldNamesToDataTypes = {};
formAndMemberData.form.sections.map((section) => {
fieldNamesToDataTypes[section.section.name] = section.section.type;
});

const spreadsheetData = [RETURNING_MEMBERS_SPREADSHEET_HEADERS];
spreadsheetData.push(...userData.map((data) => Object.values(data)));
const memberFieldNames = new Set(
Object.keys(Member.schema.paths).filter(
(fieldName) => fieldName !== '_id' && fieldName !== '__v'
)
);
let fieldsSelectList = new Set();
const actualFieldNames = [];
Object.keys(fieldNamesToDataTypes).map((fieldName) => {
if (fieldName === 'fullName') {
fieldsSelectList.add('name');
actualFieldNames.push('fullName');
} else if (memberFieldNames.has(fieldName)) {
fieldsSelectList.add(fieldName);
actualFieldNames.push(fieldName);
} else {
fieldsSelectList.add('miscDetails');
actualFieldNames.push(fieldName);
}
});
fieldsSelectList = getFields(fieldsSelectList);

const formattedUserData = (await getAllMembersData(fieldsSelectList)).map(
(member) => {
const formattedValues = actualFieldNames.map((field) => {
if (field === 'fullName') {
return member.name.first + ' ' + member.name.last;
} else if (field === 'subteams') {
return member.subteams && member.subteams.length > 0
? member.subteams
.map((subteam) => subteam.name)
.join(', ')
: '';
} else if (field === 'skills') {
return member.skills && member.skills.length > 0
? member.skills.map((skill) => skill.name).join(', ')
: '';
} else if (field === 'memberType') {
return member.memberType?.name;
}

let rawValue;
if (member[field] !== undefined) {
rawValue = member[field];
} else if (
member.miscDetails &&
member.miscDetails[field] !== undefined
) {
rawValue = member.miscDetails[field];
}
if (rawValue === undefined || rawValue === null) {
return '';
} else if (Array.isArray(rawValue)) {
return rawValue.join(', ');
} else if (typeof rawValue === 'boolean') {
return rawValue ? 'Yes' : 'No';
} else {
return rawValue;
}
});
return formattedValues;
}
);
return formattedUserData;
};

//create new file with Drive api
const createGoogleSheetsFile = async (formTitle, spreadsheetData, token) => {
// Create new file using Google Drive API
const currentDate = new Date();
const fileName = `Returning Members Form Responses - ${currentDate.toLocaleString(
const fileName = `${formTitle} Responses - ${currentDate.toLocaleString(
'en-CA',
{ timeZone: 'EST' }
)}`;
Expand Down
52 changes: 24 additions & 28 deletions backend/data/handlers/members.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,47 +248,43 @@ members.updateTaskStatus = async (filter, payload) => {

members.replacePayloadWithIds = async (payload) => {
if (payload.interests) {
if (Array.isArray(payload.interests)) {
payload.interests = await util.replaceNamesWithIdsArray(
payload.interests,
interests
);
} else {
throw Error('interests field must be an array or empty.');
if (!Array.isArray(payload.interests)) {
payload.interests = [payload.interests];
}
payload.interests = await util.replaceNamesWithIdsArray(
payload.interests,
interests
);
}

if (payload.skills) {
if (Array.isArray(payload.skills)) {
payload.skills = await util.replaceNamesWithIdsArray(
payload.skills,
skills
);
} else {
throw Error('skills field must be an array or empty.');
if (!Array.isArray(payload.skills)) {
payload.skills = [payload.skills];
}
payload.skills = await util.replaceNamesWithIdsArray(
payload.skills,
skills
);
}

if (payload.subteams) {
if (Array.isArray(payload.subteams)) {
payload.subteams = await util.replaceNamesWithIdsArray(
payload.subteams,
subteams
);
} else {
throw Error('subteams field must be an array or empty.');
if (!Array.isArray(payload.subteams)) {
payload.subteams = [payload.subteams];
}
payload.subteams = await util.replaceNamesWithIdsArray(
payload.subteams,
subteams
);
}

if (payload.projects) {
if (Array.isArray(payload.projects)) {
payload.projects = await util.replaceNamesWithIdsArray(
payload.projects,
projects
);
} else {
throw Error('projects field must be an array or empty.');
if (!Array.isArray(payload.projects)) {
payload.projects = [payload.projects];
}
payload.projects = await util.replaceNamesWithIdsArray(
payload.projects,
projects
);
}

payload.memberType
Expand Down
7 changes: 6 additions & 1 deletion backend/data/schema/Member.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,12 @@ const MemberSchema = new Schema({
required: true,
unique: true,
},
activeSchoolTerms: [
previousTerms: [
{
type: String,
},
],
futureTerms: [
{
type: String,
},
Expand Down
2 changes: 1 addition & 1 deletion backend/data/schema/UserDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const Schema = mongoose.Schema;

const UserDetailsSchema = new Schema(
{
phone: {
phoneNumber: {
type: Number,
},
personalEmail: {
Expand Down

2 comments on commit c9552e3

@vercel
Copy link

@vercel vercel bot commented on c9552e3 Mar 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deployment failed with the following error:

Environment Variable "MONGO_URL" references Secret "teamhub_mongo_url", which does not exist.

@vercel
Copy link

@vercel vercel bot commented on c9552e3 Mar 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

teamhub – ./

teamhub-waterloop.vercel.app
teamhub-git-master-waterloop.vercel.app
hub.waterloop.ca

Please sign in to comment.