-
Notifications
You must be signed in to change notification settings - Fork 0
finances: convert gasbill and mspdf to the new, unified index.ts command #15
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
Merged
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
53ba07a
kitchener_utilities.ts: parse statement date
denversc 3db7577
gasbill.ts: deleted
denversc 2110a29
kitchener_utilities.ts: done
denversc eb5ab35
morgan_stanley.ts: added with identify() and calculateFileName() impl…
denversc 54100a5
morgan_stanley.ts: awardId parse
denversc bd33ec0
morgan_stanley.ts: settlement date parse
denversc eb47ec2
document_utils.ts: created with repeated parsing logic
denversc 7cd4b87
morgan_stanley.ts: vested value
denversc 4837c31
morgan_stanley.ts: sale amount
denversc 1a0f29d
morgan_stanley.ts: shares sold
denversc 33171f5
morgan_stanley.ts: sale price
denversc f2e0a08
morgan_stanley.ts: forgot pdf suffix
denversc 8e44de1
mspdf.ts: deleted
denversc 2708227
kitchener_utilities.ts: updated to use helper functions
denversc 3229782
finances.yml: allow triggering by workflow dispatch
denversc 94c4018
finances.yml: rename workflow to "finances"
denversc 4988c41
cleanup
denversc c142c60
document_utils.ts: rewrite stringFromLines() to be more efficient
denversc 438d5d0
document_utils.ts: improve robustness by not conflating undefined wit…
denversc File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| name: Finances CI | ||
| name: finances | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| push: | ||
| branches: [main] | ||
| paths: | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| import { isDocumentParseError, type DocumentParseError } from "./document.ts"; | ||
| import { parseDateToYYYYMMDD, isParseDateError } from "./date.ts"; | ||
|
|
||
| interface StringFromLinesOptions { | ||
| resultPrefix: string; | ||
| } | ||
|
|
||
| export function stringFromLines( | ||
| lines: readonly string[], | ||
| regex: RegExp, | ||
| options?: Partial<StringFromLinesOptions>, | ||
| ): string | DocumentParseError { | ||
| for (const line of lines) { | ||
| const trimmedLine = line.trim(); | ||
| const match = trimmedLine.match(regex); | ||
| if (!match) { | ||
| continue; | ||
| } | ||
|
|
||
| const matchingString = match[1]; | ||
| if (typeof matchingString === "undefined") { | ||
| throw new Error( | ||
| `internal error stdp7xw6rb: regex should have matched line; ` + | ||
| `regex=${regex.source}, line=${trimmedLine}`, | ||
| ); | ||
| } | ||
|
|
||
| const resultPrefix = options?.resultPrefix; | ||
| if (typeof resultPrefix === "undefined") { | ||
| return matchingString; | ||
| } else { | ||
| return resultPrefix + matchingString; | ||
| } | ||
| } | ||
|
|
||
| return { | ||
| type: "DocumentParseError", | ||
| message: `line not found matching regex: ${regex.source}`, | ||
| }; | ||
| } | ||
|
|
||
| export function yyyymmddDateFromLines( | ||
| lines: readonly string[], | ||
| regex: RegExp, | ||
| dateFormat: string, | ||
| ): string | DocumentParseError { | ||
| const dateStr = stringFromLines(lines, regex); | ||
| if (isDocumentParseError(dateStr)) { | ||
| return dateStr; | ||
| } | ||
|
|
||
| const date = parseDateToYYYYMMDD(dateFormat, dateStr); | ||
| if (isParseDateError(date)) { | ||
| const { message } = date; | ||
| return { | ||
| type: "DocumentParseError", | ||
| message: | ||
| `unable to parse date "${dateStr}" ` + | ||
| `using format "${dateFormat}": ${message}`, | ||
| }; | ||
| } | ||
|
|
||
| return date; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,21 @@ | ||
| import { rogersBill } from "./documents/rogers.ts"; | ||
| import { publicMobileStatement } from "./documents/public_mobile.ts"; | ||
| import { kitchenerUtilitiesBill } from "./documents/kitchener_utilities.ts"; | ||
| import { morganStanleyRelease } from "./documents/morgan_stanley.ts"; | ||
| import { | ||
| questradeMarginStatement, | ||
| questradeRESPStatement, | ||
| questradeRRSPStatement, | ||
| } from "./documents/questrade.ts"; | ||
|
|
||
| export const allDocuments = Object.freeze([ | ||
| kitchenerUtilitiesBill, | ||
| morganStanleyRelease, | ||
| publicMobileStatement, | ||
| rogersBill, | ||
| questradeMarginStatement, | ||
| questradeRESPStatement, | ||
| questradeRRSPStatement, | ||
| rogersBill, | ||
| ]); | ||
|
|
||
| export type Documents = Exclude<(typeof allDocuments)[number], undefined>; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| import { | ||
| isDocumentParseError, | ||
| type Document, | ||
| type DocumentParseError, | ||
| } from "../document.ts"; | ||
| import { stringFromLines, yyyymmddDateFromLines } from "../document_utils.ts"; | ||
|
|
||
| export interface ParsedKitchenerUtilitiesBill { | ||
| type: "KitchenerUtilitiesBill"; | ||
| statementDate: string; | ||
| amountDue: string; | ||
| } | ||
|
|
||
| class KitchenerUtilitiesBill implements Document< | ||
| ParsedKitchenerUtilitiesBill, | ||
| "KitchenerUtilitiesBill" | ||
| > { | ||
| readonly type = "KitchenerUtilitiesBill" as const; | ||
|
|
||
| identify(lines: readonly string[]): boolean { | ||
| return lines.some((line) => line.includes("UTILITIES@KITCHENER.CA")); | ||
| } | ||
|
|
||
| calculateFileName(pdf: Readonly<ParsedKitchenerUtilitiesBill>): string { | ||
| const { statementDate, amountDue } = pdf; | ||
| return `${statementDate} Kitchener Utilities Bill ${amountDue}.pdf`; | ||
| } | ||
|
|
||
| parse( | ||
| lines: readonly string[], | ||
| ): ParsedKitchenerUtilitiesBill | DocumentParseError { | ||
| const statementDate = yyyymmddDateFromLines( | ||
| lines, | ||
| /^Statement Date:\s*(\w+\s+\d+\s+\d+)\s+/i, | ||
| "MMM D YYYY", | ||
| ); | ||
|
denversc marked this conversation as resolved.
|
||
| if (isDocumentParseError(statementDate)) { | ||
| return statementDate; | ||
| } | ||
|
denversc marked this conversation as resolved.
|
||
|
|
||
| const amountDue = stringFromLines( | ||
| lines, | ||
| /^Pre-authorized Withdrawal:\s*(\d+\.\d+)$/i, | ||
| { resultPrefix: "$" }, | ||
| ); | ||
|
denversc marked this conversation as resolved.
denversc marked this conversation as resolved.
|
||
| if (isDocumentParseError(amountDue)) { | ||
| return amountDue; | ||
| } | ||
|
denversc marked this conversation as resolved.
|
||
|
|
||
| return { type: "KitchenerUtilitiesBill", statementDate, amountDue }; | ||
| } | ||
| } | ||
|
|
||
| export const kitchenerUtilitiesBill: Readonly< | ||
| Document<ParsedKitchenerUtilitiesBill, "KitchenerUtilitiesBill"> | ||
| > = Object.freeze(new KitchenerUtilitiesBill()); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| import { | ||
| isDocumentParseError, | ||
| type Document, | ||
| type DocumentParseError, | ||
| } from "../document.ts"; | ||
| import { stringFromLines, yyyymmddDateFromLines } from "../document_utils.ts"; | ||
|
|
||
| export interface ParsedMorganStanleyRelease { | ||
| type: "MorganStanleyRelease"; | ||
| awardId: string; | ||
| settlementDate: string; | ||
| vestedValue: string; | ||
| saleAmount: string; | ||
| sharesSold: string; | ||
| salePrice: string; | ||
| } | ||
|
|
||
| class MorganStanleyRelease implements Document< | ||
| ParsedMorganStanleyRelease, | ||
| "MorganStanleyRelease" | ||
| > { | ||
| readonly type = "MorganStanleyRelease" as const; | ||
|
|
||
| identify(lines: readonly string[]): boolean { | ||
| return lines.some((line) => line.toLowerCase() === "release confirmation"); | ||
|
denversc marked this conversation as resolved.
|
||
| } | ||
|
|
||
| calculateFileName(pdf: Readonly<ParsedMorganStanleyRelease>): string { | ||
| const { | ||
| awardId, | ||
| settlementDate, | ||
| vestedValue, | ||
| saleAmount, | ||
| sharesSold, | ||
| salePrice, | ||
| } = pdf; | ||
| return ( | ||
| `${settlementDate} Morgan Stanley Release Confirmation ` + | ||
| `${awardId} ${sharesSold} shares vested for ` + | ||
| `${vestedValue} sold for ${saleAmount} ` + | ||
| `(${salePrice} per share).pdf` | ||
| ); | ||
| } | ||
|
|
||
| parse( | ||
| lines: readonly string[], | ||
| ): ParsedMorganStanleyRelease | DocumentParseError { | ||
| const awardId = stringFromLines(lines, /^Award ID:\s+([\w\d]+)$/i); | ||
|
denversc marked this conversation as resolved.
|
||
| if (isDocumentParseError(awardId)) { | ||
| return awardId; | ||
| } | ||
|
denversc marked this conversation as resolved.
|
||
|
|
||
| const settlementDate = yyyymmddDateFromLines( | ||
| lines, | ||
| /^Settlement Date:\s*(\d+-\w+-\d+)$/i, | ||
| "DD-MMM-YYYY", | ||
| ); | ||
|
denversc marked this conversation as resolved.
|
||
| if (isDocumentParseError(settlementDate)) { | ||
| return settlementDate; | ||
| } | ||
|
denversc marked this conversation as resolved.
|
||
|
|
||
| const vestedValue = stringFromLines( | ||
| lines, | ||
| /^Total Gain.*:\s*(\$[\d,.]+)$/i, | ||
| ); | ||
|
denversc marked this conversation as resolved.
|
||
| if (isDocumentParseError(vestedValue)) { | ||
| return vestedValue; | ||
| } | ||
|
denversc marked this conversation as resolved.
|
||
|
|
||
| const saleAmount = stringFromLines( | ||
| lines, | ||
| /^Sale PricexQuantity Sold:\s*\((\$[\d,.]+)\)$/i, | ||
| ); | ||
|
denversc marked this conversation as resolved.
denversc marked this conversation as resolved.
|
||
| if (isDocumentParseError(saleAmount)) { | ||
| return saleAmount; | ||
| } | ||
|
denversc marked this conversation as resolved.
|
||
|
|
||
| const sharesSold = stringFromLines( | ||
| lines, | ||
| /^Quantity Sold:\s*\((\d+\.\d{3})0*\)$/i, | ||
| ); | ||
|
denversc marked this conversation as resolved.
denversc marked this conversation as resolved.
|
||
| if (isDocumentParseError(sharesSold)) { | ||
| return sharesSold; | ||
| } | ||
|
denversc marked this conversation as resolved.
|
||
|
|
||
| const salePrice = stringFromLines( | ||
| lines, | ||
| /shares at (\$\d+\.\d{4})0* per share/i, | ||
| ); | ||
|
denversc marked this conversation as resolved.
denversc marked this conversation as resolved.
|
||
| if (isDocumentParseError(salePrice)) { | ||
| return salePrice; | ||
| } | ||
|
denversc marked this conversation as resolved.
|
||
|
|
||
| return { | ||
| type: "MorganStanleyRelease", | ||
| awardId, | ||
| settlementDate, | ||
| vestedValue, | ||
| saleAmount, | ||
| sharesSold, | ||
| salePrice, | ||
| }; | ||
| } | ||
| } | ||
|
|
||
| export const morganStanleyRelease: Readonly< | ||
| Document<ParsedMorganStanleyRelease, "MorganStanleyRelease"> | ||
| > = Object.freeze(new MorganStanleyRelease()); | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.