Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Completed officers backend #32

Merged
merged 10 commits into from
Sep 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
OFFICERS_SPREADSHEET_ID = "1EU-Fx5DlvRV91_prtY1NQXB7XvjvjSQ2xcdZcICV8PQ"
SERVICE_ACCOUNT={"type": "service_account","project_id": "oceanic-hold-377104","private_key_id": "86335ea7e7574639d67867825731505f6d1de14c","private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCmcjLBY0Hm1t0P\nIPA0gdtwHGKRGzWXS3jcwgMGGTgRoNdSTz1fnYN+An/DUJO1XlUBDCCMLDN4Y1Tn\nedGmiczVvaZ61s+qVLSWCJr+gwOtdizd5CGYl5xXblDABehIOgPIgQGe5Blgwvtr\ne3Cuf0yCCR3CAhtpws0cF6qlvH1iG9Z9drRy0U6B3QQH7RECvzKXDeBqE9DvfugQ\nNHVb8FB9dNFojMG0cg578IYhvPGsmug+WA4gGceAlGlqIqEbfaxL2kkKAM2LEqo7\nVjzNFZ3iAK3uKGQn+FYcQiUG7eJNcT8kRG5jrN88g3JGgdsyZjQfUJ1e9+v/zCT+\ncfFkBFihAgMBAAECggEAAKZH7+zC/FNg0/cJMO9Dy7V0pgPQ6vj6rQPdzqeJCjqS\nNIpGH0omUfbRCFJXbTkDxGgHdXyTq9fZZDjbulZl0/y0Olz9vPitJru8XRtmR4ZD\nSQ9jmSyYSdgOnR04yZVeX+ku+C+EMNY59HKsHyY0hnpiIRhwgnJl6gLmodRxOdGH\n4JnLiRt3TYAWLWi4lURnyI8jhTwVpHU4oDTiV8jLwuRn//1Sgl01d0qNB3ru5fbB\nHHR3E5ORAhkbm1RWHNvX/yA6BuLyWOo+GKcChYfG07hMlKyYfRgzy9OGNF097/cd\nJbEqCd3y5jaGiFl96ccfvpwv4UlGJDwqdBYIvZEiNQKBgQDc+bkh2QXTpmbcqlcq\nlsTprx2nPk1M0kR7nBlMd06g67JIr3xvbJxycNA2fT3AFaE47cOPP9m/A12EADP4\nUJXodh1CWuqqq5V8rH6VzSFepvB5dMakblcaU31soLc2lJdUOvW0FjTaw5f66xO1\nRRyF/DYZl0IQzN8JYfG0C/hR3QKBgQDA0+V5rI/C9hkinX/i7h3Ez/4oR/KunP1H\nvWrwK0/PaY2XF1Vj3ILy4NFp4xh3Aq2JEXEuAWO7v+nduBDAHsfg5MeiOP9hUoLs\nGV8cUvyl1ZaJFVuUfiF30HSce8nDSbED4Tz61Z1JoMivyYNn+MMGvVKUUrEshPLs\n0FBdxkfPlQKBgEnHErmrVNeychtwwAOXLQ18iyEAmxxwA69lkcjG+LifmPnNpXUq\nRR2sbsgSjqBoWFjCgY19Wvz8aaedzbw1Y6emb+RsqHbtLM7gZK8tOtv4TseuZngV\n4v0GLRUsn58Yg2H7AXfsBh+YOFDtVboq5fgSGhn3N3aWgE0Ip4Nej7z9AoGAR2Ep\nHTVnsj/vZgTvhk2m72gfKUIY79JlX90abPVbgegM5dVZYrIKN2ZJnuzV7ZgsLQUr\n5F/XT3TnWY/9g2DhlLqbu81lm90zYbqynoWJyMmcA/rH/9wA/51GzXZ1ZbktscX7\nEM9qfgrqv0fVQPOxHhmPLCDAPT6pvkgOt+fiRVECgYEAnZepXtUGQdVyENk95LRS\noN3Xt1GvfrB8xmH8h3eLtdeR8bVnGc7QJWxylmxLhSVxxI45sqa1ewomlUgW9SJl\nCDbPzXu3tJx1BL7dWqCw1+gUDYcuo3OiAUZhOHRZ2cFRfxvvW5imlA2lLtu5Za15\nynsFXa1mjyNcQMZNUfJcTNY=\n-----END PRIVATE KEY-----\n","client_email": "acm-google-service-account@oceanic-hold-377104.iam.gserviceaccount.com","client_id": "108352367882056422349","auth_uri": "https://accounts.google.com/o/oauth2/auth","token_uri": "https://oauth2.googleapis.com/token","auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs","client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/acm-google-service-account%40oceanic-hold-377104.iam.gserviceaccount.com"}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ yarn-error.log*

# local env files
.env*.local
.env

# vercel
.vercel
Expand Down
2 changes: 1 addition & 1 deletion components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function Navbar() {
</div>
<div className="navbar-items">
<div className="navbar-link">
<Link href="/otherPage">Other Page</Link>
<Link href="/teamPage">Team</Link>
</div>
<div className="navbar-link">
<Link href="/eventsPage">Events Page</Link>
Expand Down
125 changes: 125 additions & 0 deletions getOfficers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
var dotenv = require("dotenv");
var googleapis_1 = require("googleapis");
// .env config
dotenv.config();
var SPREADSHEET_ID = process.env.OFFICERS_SPREADSHEET_ID;
var SERVICE_ACCOUNT = (_a = process.env.SERVICE_ACCOUNT) !== null && _a !== void 0 ? _a : '{}';
function getOfficerData(committeeName) {
return __awaiter(this, void 0, void 0, function () {
var sheets, service_account, jwtClient, res, rows, committees, officers, currCommittee, officerID;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
sheets = googleapis_1.google.sheets({ version: 'v4' });
service_account = JSON.parse(SERVICE_ACCOUNT);
jwtClient = new googleapis_1.google.auth.JWT(service_account.client_email, '', service_account.private_key, ['https://www.googleapis.com/auth/spreadsheets']);
jwtClient.authorize(function (err) {
if (err) {
throw err;
}
});
return [4 /*yield*/, sheets.spreadsheets.values.get({
auth: jwtClient,
spreadsheetId: SPREADSHEET_ID,
range: 'Officers',
})];
case 1:
res = _a.sent();
rows = res === null || res === void 0 ? void 0 : res.data.values;
if (!rows || rows.length == 0) {
throw new Error('Error: no data found');
}
committees = new Map([
['AI', 'ai'],
['Cyber', 'cyber'],
['Design', 'design'],
['Studio', 'studio'],
['Hack', 'hack'],
['ICPC', 'icpc'],
['Teach LA', 'teachla'],
['W', 'w'],
]);
officers = [];
currCommittee = '';
officerID = 1;
rows.forEach(function (row) {
var _a, _b, _c, _d, _e, _f;
if (row.length == 0)
// empty row
return;
if (committees.has(row[0])) {
// row with only committee name
var committee = row[0];
currCommittee = (_a = committees.get(committee)) !== null && _a !== void 0 ? _a : ''; // empty string means ACM Board
return;
}
if (currCommittee != committeeName)
// skip all rows other than desired committee
return;
// push row data into officers list
var image = row[10];
if (!image) {
image = '/acm-logo-wordmark-extended.png';
}
else if (image.includes('drive.google.com')) {
var fileID = image.match(/\/file\/d\/(.+?)\//)[1];
image = "https://drive.google.com/uc?export=download&id=".concat(fileID);
}
// create officer
var officer = {
id: officerID,
position: (_b = row[0]) !== null && _b !== void 0 ? _b : null,
name: (_c = row[1]) !== null && _c !== void 0 ? _c : null,
pronouns: (_d = row[2]) !== null && _d !== void 0 ? _d : null,
email: (_e = row[3]) !== null && _e !== void 0 ? _e : null,
github: (_f = row[9]) !== null && _f !== void 0 ? _f : null,
imageURL: image !== null && image !== void 0 ? image : null,
};
officers.push(officer);
officerID++;
});
return [2 /*return*/, officers];
}
});
});
}
exports.default = getOfficerData;
87 changes: 87 additions & 0 deletions getOfficers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import * as dotenv from 'dotenv';
import { google } from 'googleapis';

// .env config
dotenv.config();
const SPREADSHEET_ID = process.env.OFFICERS_SPREADSHEET_ID;
const SERVICE_ACCOUNT = process.env.SERVICE_ACCOUNT ?? '{}';
export default async function getOfficerData(
committeeName: string,
): Promise<object[]> {
const sheets = google.sheets({ version: 'v4' });
// Get JWT Token to access sheet
const service_account = JSON.parse(SERVICE_ACCOUNT);
const jwtClient = new google.auth.JWT(
service_account.client_email,
'',
service_account.private_key,
['https://www.googleapis.com/auth/spreadsheets'],
);
jwtClient.authorize(function (err) {
if (err) {
throw err;
}
});
// Get officer data from google spreadsheets
const res = await sheets.spreadsheets.values.get({
auth: jwtClient,
spreadsheetId: SPREADSHEET_ID,
range: 'Officers',
});
const rows = res?.data.values;
if (!rows || rows.length == 0) {
throw new Error('Error: no data found');
}
// Map committee names in the spreadsheet to more concise names
// Ignore board as it's not a committee
const committees = new Map<string, string>([
['AI', 'ai'],
['Cyber', 'cyber'],
['Design', 'design'],
['Studio', 'studio'],
['Hack', 'hack'],
['ICPC', 'icpc'],
['Teach LA', 'teachla'],
['W', 'w'],
]);
// // Store officer data
const officers: object[] = []; // list of officers in desired committee
let currCommittee = '';
let officerID = 1;
rows.forEach((row) => {
if (row.length == 0)
// empty row
return;
if (committees.has(row[0])) {
// row with only committee name
const committee = row[0];
currCommittee = committees.get(committee) ?? ''; // empty string means ACM Board
return;
}
if (currCommittee != committeeName)
// skip all rows other than desired committee
return;
// push row data into officers list
let image = row[10];
if (!image) {
image = '/acm-logo-wordmark-extended.png';
} else if (image.includes('drive.google.com')) {
const fileID = image.match(/\/file\/d\/(.+?)\//)[1];
image = `https://drive.google.com/uc?export=download&id=${fileID}`;
}
// create officer
const officer = {
id: officerID,
position: row[0] ?? null,
name: row[1] ?? null,
pronouns: row[2] ?? null,
email: row[3] ?? null,
github: row[9] ?? null,
imageURL: image ?? null,
};
officers.push(officer);
officerID++;
});

return officers;
}
7 changes: 7 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ const nextConfig = {
eslint: {
dirs: ['components', 'pages'],
},
images: {
domains: [
'lh3.googleusercontent.com',
'drive.google.com',
'teachla.uclaacm.com',
],
},
};

module.exports = nextConfig;
Loading
Loading