Compare commits

..

2 commits

Author SHA1 Message Date
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
3a439285aa Refactors constants for spreadsheet data
Updates constants to reflect changes in the Google Sheet structure.

Removes hardcoded name aliases and introduces a new sheet to manage them.
Updates sheet ranges and column indices to align with the updated structure.

This change centralizes alias management, improving maintainability and data integrity.
2025-05-31 14:35:16 +02:00
2 changed files with 112 additions and 99 deletions

123
app.js
View file

@ -2,6 +2,7 @@ import { SplidClient } from 'splid-js';
import { google } from "googleapis"; import { google } from "googleapis";
import { import {
NAME_ALIASES, NAME_ALIASES,
ALIAS_TO_EMOJI,
SPREADSHEET_ID, SPREADSHEET_ID,
SHEETS_CONFIG, SHEETS_CONFIG,
SHEET_RANGES, SHEET_RANGES,
@ -11,8 +12,30 @@ import {
COLUMN_INDICES COLUMN_INDICES
} from './constants.js'; } 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) { function getNameFromAlias(alias) {
return NAME_ALIASES[alias.toLowerCase()] || NAME_ALIASES['default']; return ALIAS_TO_EMOJI[alias.toLowerCase()] || NAME_ALIASES['default'];
} }
// Create a reusable sheets API client // Create a reusable sheets API client
@ -40,7 +63,7 @@ async function getSheetData(spreadsheetId, range) {
async function updateSheetData(spreadsheetId, range, values) { async function updateSheetData(spreadsheetId, range, values) {
try { try {
const sheets = await createSheetsClient(); const sheets = await createSheetsClient();
const response = await sheets.spreadsheets.values.update({ const response = sheets.spreadsheets.values.update({
spreadsheetId, spreadsheetId,
range, range,
valueInputOption: 'USER_ENTERED', valueInputOption: 'USER_ENTERED',
@ -54,40 +77,43 @@ async function updateSheetData(spreadsheetId, range, values) {
} }
async function getSplidData(splidCode) { async function getSplidData(splidCode) {
const splid = new SplidClient(); const splid = new SplidClient();
const groupRes = await splid.group.getByInviteCode(splidCode); const groupRes = await splid.group.getByInviteCode(splidCode);
const groupId = groupRes.result.objectId; const groupId = groupRes.result.objectId;
const groupInfo = await splid.groupInfo.getOneByGroup(groupId); const groupInfo = await splid.groupInfo.getOneByGroup(groupId);
const members = await splid.person.getAllByGroup(groupId); const members = await splid.person.getAllByGroup(groupId);
const expensesAndPayments = await splid.entry.getAllByGroup(groupId); const expensesAndPayments = await splid.entry.getAllByGroup(groupId);
const balance = SplidClient.getBalance( const balance = SplidClient.getBalance(
members, members,
expensesAndPayments, expensesAndPayments,
groupInfo groupInfo
); );
// Create a map of member IDs to their names/initials // Create a map of member IDs to their names/initials
const memberMap = {}; const memberMap = {};
for (const member of members) { for (const member of members) {
memberMap[member.GlobalId] = { memberMap[member.GlobalId] = {
name: member.name, name: member.name,
emoji: getNameFromAlias(member.name) emoji: getNameFromAlias(member.name)
}; };
} }
// Enhance the balance object with member information // Enhance the balance object with member information
const enhancedBalance = {}; const enhancedBalance = {};
for (const [memberId, balanceInfo] of Object.entries(balance)) { for (const [memberId, balanceInfo] of Object.entries(balance)) {
enhancedBalance[memberId] = { enhancedBalance[memberId] = {
...balanceInfo, ...balanceInfo,
memberInfo: memberMap[memberId] || { name: 'Unknown', emoji: NAME_ALIASES['default'] } memberInfo: memberMap[memberId] || { name: 'Unknown', emoji: NAME_ALIASES['default'] },
}; };
} }
return enhancedBalance; return {
balance: enhancedBalance,
groupName: groupInfo.name
};
} }
async function resetTableData() { async function resetTableData() {
@ -134,6 +160,8 @@ async function autoResizeColumns(spreadsheetId, sheetId, startColumnIndex, endCo
} }
async function main() { async function main() {
let RAW = [];
await setAliasesFromSheet();
const splidCodes = await getSheetData(SPREADSHEET_ID, SHEET_RANGES.SPLID_CODES); const splidCodes = await getSheetData(SPREADSHEET_ID, SHEET_RANGES.SPLID_CODES);
const userTags = await getSheetData(SPREADSHEET_ID, SHEET_RANGES.USER_TAGS); const userTags = await getSheetData(SPREADSHEET_ID, SHEET_RANGES.USER_TAGS);
@ -141,35 +169,58 @@ async function main() {
console.log("Resetting table data.") console.log("Resetting table data.")
await resetTableData(); await resetTableData();
const groupNames = []; // Array to collect group names
for (let i = 0; i < splidCodes.length; i++) { for (let i = 0; i < splidCodes.length; i++) {
const splidCodeString = splidCodes[i][0]; const splidCodeString = splidCodes[i][0];
if (!splidCodeString) { if (!splidCodeString) {
groupNames.push(['-']); // Add placeholder for empty rows
continue; continue;
} }
console.log(`Processing Splid code: ${splidCodeString}`); console.log(`Processing Splid code: ${splidCodeString}`);
const balance = await getSplidData(splidCodeString); const splidData = await getSplidData(splidCodeString);
const balance = splidData.balance;
// Add group name to our collection
groupNames.push([splidData.groupName]);
const rowData = []; const rowData = [];
for (const tag of userTags[0]) { for (const tag of userTags[0]) {
const memberEntry = Object.entries(balance).find(([_, info]) => info.memberInfo.emoji === tag); const matchingMembers = Object.entries(balance).filter(([_, info]) => info.memberInfo.emoji === tag);
const balanceValue = memberEntry ? const totalBalance = matchingMembers.reduce((sum, [_, info]) => sum + parseFloat(info.balance), 0);
String(memberEntry[1].balance).replace('.', ',') : const balanceValue = matchingMembers.length > 0 ?
'-'; String(totalBalance.toFixed(2)).replace('.', ',') :
'-';
rowData.push(balanceValue); rowData.push(balanceValue);
} }
// Update the sheet with the row data // Update the sheet with the row data
await updateSheetData(SPREADSHEET_ID, SHEET_RANGES.DATA_ROW(i), [rowData]); 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.") console.log("Sheets updated, adjusting column width.")
try { try {
await autoResizeColumns(SPREADSHEET_ID, SHEET_ID, COLUMN_INDICES.E, COLUMN_INDICES.M); await autoResizeColumns(SPREADSHEET_ID, SHEET_ID, COLUMN_INDICES.F, COLUMN_INDICES.N);
} catch (err) { } catch (err) {
console.error('Failed to auto-resize columns, but data was updated successfully:', 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); main().catch(console.error);

View file

@ -1,53 +1,9 @@
export const NAME_ALIASES = { export let NAME_ALIASES = {
// 🙃 aliases
'vrouba von vrbicz': '🙃',
'vróbič': '🙃',
'vrba vrbička': '🙃',
'jirka v.': '🙃',
// 👨‍🏭 aliases
'párek z mourkova': '👨‍🏭',
'marek': '👨‍🏭',
'mourek': '👨‍🏭',
'mourek (jen láhve)': '👨‍🏭',
// 🤒 aliases
'kruzík ruzický': '🤒',
'kruzík ruzík': '🤒',
'krouzič': '🤒',
// 🤑 aliases
'matuzalém kremžský': '🤑',
'matuzalém i/ii.': '🤑',
'matúš': '🤑',
// 😎 aliases
'martin': '😎',
'martin brony veleblil': '😎',
'veleblil bobajz z broníkova': '😎',
'bronos': '😎',
// 🤯 aliases
'ja': '🤯',
'kuba': '🤯',
'kuba-buba': '🤯',
"d'artakuba zlominoha": '🤯',
'kuba zlominoha': '🤯',
// 😴 aliases
'sam': '😴',
'ditrpich von šalina': '😴',
// 🍖 aliases
'šunka šunkovič šunkovský': '🍖',
'šunka pražský': '🍖',
'šunkovič': '🍖',
'dan': '🍖',
// Default for unrecognized aliases
'default': 'Ostatní' 'default': 'Ostatní'
}; };
export const ALIAS_TO_EMOJI = {};
export const SPREADSHEET_ID = '1RxeQTnirJILgqXDuvI2RQt9vljn1Jz_JFCzVDRQviIg'; export const SPREADSHEET_ID = '1RxeQTnirJILgqXDuvI2RQt9vljn1Jz_JFCzVDRQviIg';
// Google Sheets configuration // Google Sheets configuration
@ -58,10 +14,13 @@ export const SHEETS_CONFIG = {
// Sheet ranges // Sheet ranges
export const SHEET_RANGES = { export const SHEET_RANGES = {
SPLID_CODES: 'Splidy!C2:C', SPLID_CODES: 'Splidy!D2:D',
USER_TAGS: 'Splidy!E1:M1', SPLID_NAMES: 'Splidy!B2:B',
DATA_RESET: 'Splidy!E2:M', USER_TAGS: 'Splidy!F1:N1',
DATA_ROW: (rowIndex) => `Splidy!E${rowIndex + 2}:M${rowIndex + 2}` DATA_RESET: 'Splidy!F2:N',
DATA_ROW: (rowIndex) => `Splidy!F${rowIndex + 2}:N${rowIndex + 2}`,
ALIASES: 'Přezdívky!A:H',
RAW: 'raw'
}; };
// Other constants // Other constants
@ -69,8 +28,11 @@ export const TABLE_RESET_ROW_COUNT = 100;
export const TABLE_COLUMN_COUNT = 9; // Columns E through M export const TABLE_COLUMN_COUNT = 9; // Columns E through M
export const COLUMN_INDICES = { export const COLUMN_INDICES = {
B: 2,
E: 4, E: 4,
M: 12 F: 5,
M: 12,
N: 13
}; };
export const SHEET_ID = 0; export const SHEET_ID = 0;