aboutsummaryrefslogtreecommitdiffstats
path: root/src/sheets.ts
diff options
context:
space:
mode:
authorJan Tuomi <jan@jantuomi.fi>2026-05-06 21:46:21 +0300
committerJan Tuomi <jan@jantuomi.fi>2026-05-06 21:46:21 +0300
commit713ced9c68cd70a8e56e8ce5ed27cb9bbe3e55c4 (patch)
tree29f9997386b9f46db9f263ef63a5e01ca19db393 /src/sheets.ts
parentcba8c3d49b53702a488eaff16109e29e49d94b6d (diff)
Add shopping list features, rework
Diffstat (limited to 'src/sheets.ts')
-rw-r--r--src/sheets.ts203
1 files changed, 0 insertions, 203 deletions
diff --git a/src/sheets.ts b/src/sheets.ts
deleted file mode 100644
index 3578198..0000000
--- a/src/sheets.ts
+++ /dev/null
@@ -1,203 +0,0 @@
-import { google } from "googleapis";
-import {
- add,
- set,
- Duration,
- parse,
- format,
- getDay,
- subDays,
- addDays,
-} from "date-fns";
-
-import config from "./config";
-
-interface SheetsRawEntry {
- intervalStr: string;
- name: string;
- lastDoneDateStr: string;
-}
-
-interface SheetsProcessedEntry {
- name: string;
- nextDateStamp: string;
- nextDate: Date;
- lastDateStamp: string;
- lastDate?: Date;
- interval: string;
-}
-
-const INTERVAL_ABBREVS = {
- pv: 1,
- vk: 7,
- kk: 30,
- v: 365,
-} as const;
-
-type IntervalAbbrevKey = keyof typeof INTERVAL_ABBREVS;
-
-const strToDate = (s: string) => parse(s, "yyyy-MM-dd", new Date());
-const dateToStr = (d: Date) => format(d, "yyyy-MM-dd");
-
-const calculateDuration = (intervalStr: string) => {
- const match = /(\d+)(\w+)/.exec(intervalStr);
-
- if (!match) {
- throw new Error("Given interval string does not match regular expression");
- }
-
- const number = Number.parseInt(match[1]);
- const abbrev = match[2];
-
- if (number <= 0) {
- throw new Error(`"${number}" is an invalid interval number`);
- }
-
- if (!Object.keys(INTERVAL_ABBREVS).includes(abbrev)) {
- throw new Error(`"${abbrev}" is an invalid interval string`);
- }
-
- const days = INTERVAL_ABBREVS[abbrev as IntervalAbbrevKey];
-
- const totalDuration: Duration = {
- days: number * days,
- };
-
- return totalDuration;
-};
-
-const processRow = (row: SheetsRawEntry) => {
- const duration = calculateDuration(row.intervalStr);
-
- const calcValues = () => {
- if (row.lastDoneDateStr) {
- const lastDate = strToDate(row.lastDoneDateStr);
- const nextDate = add(lastDate, duration);
- const nextDateStamp = dateToStr(nextDate);
-
- return { lastDate, nextDate, nextDateStamp };
- } else {
- const lastDate = undefined;
- const nextDate = new Date();
- const nextDateStamp = dateToStr(nextDate);
-
- return { lastDate, nextDate, nextDateStamp };
- }
- };
-
- const processedEntry: SheetsProcessedEntry = {
- ...calcValues(),
- name: row.name,
- lastDateStamp: row.lastDoneDateStr,
- interval: row.intervalStr,
- };
-
- return processedEntry;
-};
-
-const isThisWeek = (entry: SheetsProcessedEntry): boolean => {
- const nextDate = entry.nextDate;
- const currentDateTime = new Date();
- const today = set(currentDateTime, {
- hours: 0,
- minutes: 0,
- seconds: 0,
- milliseconds: 0,
- });
- const dayOfWeek = (getDay(today) + 7 - 1) % 7; // getDay returns sunday = 0
- const currentWeekStart = subDays(today, dayOfWeek);
- const currentWeekEnd = addDays(currentWeekStart, 7);
-
- return nextDate >= currentWeekStart && nextDate < currentWeekEnd;
-};
-
-// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
-export const buildSheetsClient = async () => {
- const auth = new google.auth.GoogleAuth({
- // Scopes can be specified either as an array or as a single, space-delimited string.
- scopes: [
- "https://www.googleapis.com/auth/spreadsheets",
- "https://www.googleapis.com/auth/spreadsheets.readonly",
- ],
- keyFile: config.googleSaJsonPath,
- });
-
- google.options({ auth });
- const sheets = google.sheets({ version: "v4" });
-
- const fetchSheetData = async (): Promise<SheetsRawEntry[]> => {
- try {
- const res = await sheets.spreadsheets.values.get({
- spreadsheetId: config.sheetsSpreadsheetId,
- range: config.sheetsRange,
- });
-
- const entriesAsLists = res.data.values || [];
- const rawEntries: SheetsRawEntry[] = entriesAsLists.map((row) => ({
- intervalStr: row[0],
- name: row[1],
- lastDoneDateStr: row[2],
- }));
-
- console.log(`Fetched ${rawEntries.length} rows from Sheets.`);
- return rawEntries;
- } catch (err) {
- console.error(err);
- throw err;
- }
- };
-
- const getRightmostColumnInRange = (range: string): string => {
- const match = /(.+!)?([A-Z]+?)(\d+):([A-Z]+?)(\d+)/.exec(range);
-
- if (!match) {
- throw new Error("Given range does not match regular expression");
- }
-
- const sheetName = match[1];
- const topLeftRow = Number.parseInt(match[3]);
- const bottomRightCol = match[4];
- const bottomRightRow = Number.parseInt(match[5]);
-
- const rmCol = bottomRightCol;
- const rmRowStart = topLeftRow;
- const rmRowEnd = bottomRightRow;
-
- return `${sheetName}${rmCol}${rmRowStart}:${rmCol}${rmRowEnd}`;
- };
-
- const updateSheetLastDoneColumn = async (newDateStamps: string[]) => {
- const lastDoneColumnRange = getRightmostColumnInRange(config.sheetsRange);
-
- const body = {
- values: newDateStamps.map((nds) => [nds]),
- };
-
- await sheets.spreadsheets.values.update({
- spreadsheetId: config.sheetsSpreadsheetId,
- range: lastDoneColumnRange,
- valueInputOption: "RAW",
- requestBody: body,
- });
- };
-
- const processRows = (rows: SheetsRawEntry[]) => rows.map(processRow);
-
- const filterOnlyThisWeek = (
- entries: SheetsProcessedEntry[],
- ): SheetsProcessedEntry[] => entries.filter(isThisWeek);
-
- const updatedLastDoneDateStamps = (
- entries: SheetsProcessedEntry[],
- ): string[] =>
- entries.map((e) => (isThisWeek(e) ? e.nextDateStamp : e.lastDateStamp));
-
- return {
- fetchSheetData,
- getRightmostColumnInRange,
- updateSheetLastDoneColumn,
- processRows,
- filterOnlyThisWeek,
- updatedLastDoneDateStamps,
- };
-};