splid-js-googlesheet-updater/app.js
martinshoob 1b05f0453b Fetches and displays Splid group balance data
Connects to Splid, retrieves group balance information, and displays it.

The changes:
- Fetches aliases from a spreadsheet to map names to emojis.
- Retrieves group names from Splid and displays them in the spreadsheet.
- Enhances error handling for column resizing.
- Adds more robust handling of Splid data, ensuring proper display even with partial data.
- Sums balances for the same emoji to handle multiple users with same tag.
2025-05-31 14:35:32 +02:00

226 lines
6.4 KiB
JavaScript

import { SplidClient } from 'splid-js';
import { google } from "googleapis";
import {
NAME_ALIASES,
ALIAS_TO_EMOJI,
SPREADSHEET_ID,
SHEETS_CONFIG,
SHEET_RANGES,
TABLE_RESET_ROW_COUNT,
TABLE_COLUMN_COUNT,
SHEET_ID,
COLUMN_INDICES
} from './constants.js';
async function setAliasesFromSheet() {
const response = await getSheetData(SPREADSHEET_ID, SHEET_RANGES.ALIASES);
const emojis = response[0];
for (let col = 0; col < emojis.length; col++) {
const emoji = emojis[col];
NAME_ALIASES[emoji] = [];
for (let row = 1; row < response.length; row++) {
const alias = response[row][col];
if (alias && alias.trim() !== '') {
const normalized = alias.toLowerCase();
NAME_ALIASES[emoji].push(alias);
ALIAS_TO_EMOJI[normalized] = emoji;
}
}
}
}
function getNameFromAlias(alias) {
return ALIAS_TO_EMOJI[alias.toLowerCase()] || NAME_ALIASES['default'];
}
// Create a reusable sheets API client
async function createSheetsClient() {
const auth = new google.auth.GoogleAuth(SHEETS_CONFIG);
return google.sheets({ version: 'v4', auth });
}
// Get data from Google Sheets
async function getSheetData(spreadsheetId, range) {
try {
const sheets = await createSheetsClient();
const response = await sheets.spreadsheets.values.get({
spreadsheetId,
range
});
return response.data.values;
} catch (err) {
console.error('Error getting sheet data:', err);
throw err;
}
}
// Update data in Google Sheets
async function updateSheetData(spreadsheetId, range, values) {
try {
const sheets = await createSheetsClient();
const response = sheets.spreadsheets.values.update({
spreadsheetId,
range,
valueInputOption: 'USER_ENTERED',
resource: { values },
});
return response.data;
} catch (err) {
console.error('Error updating sheet data:', err);
throw err;
}
}
async function getSplidData(splidCode) {
const splid = new SplidClient();
const groupRes = await splid.group.getByInviteCode(splidCode);
const groupId = groupRes.result.objectId;
const groupInfo = await splid.groupInfo.getOneByGroup(groupId);
const members = await splid.person.getAllByGroup(groupId);
const expensesAndPayments = await splid.entry.getAllByGroup(groupId);
const balance = SplidClient.getBalance(
members,
expensesAndPayments,
groupInfo
);
// Create a map of member IDs to their names/initials
const memberMap = {};
for (const member of members) {
memberMap[member.GlobalId] = {
name: member.name,
emoji: getNameFromAlias(member.name)
};
}
// Enhance the balance object with member information
const enhancedBalance = {};
for (const [memberId, balanceInfo] of Object.entries(balance)) {
enhancedBalance[memberId] = {
...balanceInfo,
memberInfo: memberMap[memberId] || { name: 'Unknown', emoji: NAME_ALIASES['default'] },
};
}
return {
balance: enhancedBalance,
groupName: groupInfo.name
};
}
async function resetTableData() {
// Create an array of arrays with "-" for each cell
const dashData = Array(TABLE_RESET_ROW_COUNT).fill().map(() => Array(TABLE_COLUMN_COUNT).fill("-"));
try {
await updateSheetData(SPREADSHEET_ID, SHEET_RANGES.DATA_RESET, dashData);
} catch (err) {
console.error('Error resetting table data:', err);
throw err;
}
}
async function autoResizeColumns(spreadsheetId, sheetId, startColumnIndex, endColumnIndex) {
try {
const sheets = await createSheetsClient();
const request = {
spreadsheetId: spreadsheetId,
resource: {
requests: [
{
autoResizeDimensions: {
dimensions: {
sheetId: sheetId,
dimension: 'COLUMNS',
startIndex: startColumnIndex,
endIndex: endColumnIndex + 1
}
}
}
]
}
};
const response = await sheets.spreadsheets.batchUpdate(request);
console.log('Columns auto-resized successfully');
return response.data;
} catch (err) {
console.error('Error auto-resizing columns:', err);
throw err;
}
}
async function main() {
let RAW = [];
await setAliasesFromSheet();
const splidCodes = await getSheetData(SPREADSHEET_ID, SHEET_RANGES.SPLID_CODES);
const userTags = await getSheetData(SPREADSHEET_ID, SHEET_RANGES.USER_TAGS);
console.log(splidCodes);
console.log("Resetting table data.")
await resetTableData();
const groupNames = []; // Array to collect group names
for (let i = 0; i < splidCodes.length; i++) {
const splidCodeString = splidCodes[i][0];
if (!splidCodeString) {
groupNames.push(['-']); // Add placeholder for empty rows
continue;
}
console.log(`Processing Splid code: ${splidCodeString}`);
const splidData = await getSplidData(splidCodeString);
const balance = splidData.balance;
// Add group name to our collection
groupNames.push([splidData.groupName]);
const rowData = [];
for (const tag of userTags[0]) {
const matchingMembers = Object.entries(balance).filter(([_, info]) => info.memberInfo.emoji === tag);
const totalBalance = matchingMembers.reduce((sum, [_, info]) => sum + parseFloat(info.balance), 0);
const balanceValue = matchingMembers.length > 0 ?
String(totalBalance.toFixed(2)).replace('.', ',') :
'-';
rowData.push(balanceValue);
}
// Update the sheet with the row data
await updateSheetData(SPREADSHEET_ID, SHEET_RANGES.DATA_ROW(i), [rowData]);
RAW.push([`Row ${i} Data:`, JSON.stringify(rowData)]);
RAW.push([`Balance ${i}:`, JSON.stringify(balance, null, 2)]);
}
// Update column B with group names
if (groupNames.length > 0) {
const columnBRange = `B2:B${groupNames.length + 1}`; // B2 to B(2+count-1)
await updateSheetData(SPREADSHEET_ID, columnBRange, groupNames);
console.log(`Updated column B with ${groupNames.length} group names`);
}
console.log("Sheets updated, adjusting column width.")
try {
await autoResizeColumns(SPREADSHEET_ID, SHEET_ID, COLUMN_INDICES.F, COLUMN_INDICES.N);
} catch (err) {
console.error('Failed to auto-resize columns, but data was updated successfully:', err);
}
RAW.push(['Splid Codes:', JSON.stringify(splidCodes)]);
RAW.push(['User Tags:', JSON.stringify(userTags)]);
await updateSheetData(SPREADSHEET_ID, SHEET_RANGES.RAW, RAW);
}
main().catch(console.error);