internship tooling · 2024

linebarger

internal tools from my software internship at a tax-collection law firm: a scraper that triages delinquent property-tax accounts, and a chrome extension that decodes property codes.

lgbs on github ↗PID-to-TSC on github ↗

the problem

The firm collects delinquent property taxes. Before filing suit on a batch of accounts, somebody has to check each one against the county's public tax site: is it exempt, how much is due, which years are due, has anything been paid since the judgment. Doing that by hand means loading four pages per account and reading tables. A real batch from my input files: 43 accounts — 172 page loads. That's someone's whole day.

lgbs

So i wrote a python scraper. Feed it a list of account numbers and each account's judgment-year ranges, and for every account it pulls the four pages from the county site, parses the soup with BeautifulSoup and some regex, and spits out a CSV plus a color-coded console report: exemptions, total due, years due collapsed into ranges ("2010-2014, 2019"), last payment date, and a suit-type flag. The point is triage — it tallies the accounts that aren't worth pursuing (business personal property, mobile homes, judgment years already paid, invalid account numbers) so a human only looks at the rest.

Some of the domain logic is delightfully cursed: whether an account is BPP or a mobile home is encoded in specific digits of the account number itself, so the classifier is literally account_number[5] == '9'. That's not a hack, that's how the county numbers accounts. You just have to know.

the scraper's color-coded console output mid-run: account summaries with amounts due and flags
The console report mid-run.

fighting the county server

The county's server speaks TLS so old that modern python refuses to shake hands with it. The fix lives in web_scraper.py: drop to a raw http client and set ssl.OP_LEGACY_SERVER_CONNECT. Government infrastructure is forever.

There's also a progress bar module whose per-tick sleep is a random delay with the comment # how long u wanna stall. It looks cosmetic. It's also the politeness throttle between requests. Dual-purpose engineering.

PID-to-TSC

The second tool is a manifest v3 chrome extension. Appraisal-district property pages show a bare "State Code" like A1, and nobody memorizes all seventy of them. One click injects a script into the page, finds the code, and translates it — "A1 → Real, Residential, Single-Family" — from an embedded lookup table. Nine commits over four days in january 2025. Sometimes a tool is just a lookup table with a button, and that's enough.

milestones

The first commit of lgbs, october 2024, is message-for-message my first git commit ever: "uploading all files to github repo first commit ever in my life". The commit messages that follow chart the learning curve — "hell yeah cimmot", "f it", "final perchance?" — though there was, to my credit, a real branch-and-merge workflow attempt on day one.

Full disclosure: the public repo is the sanitized show copy — real account data was scrubbed before publishing, and a later cleanup pass renamed a bunch of functions in the modules without updating main.py, so the github version doesn't actually run as-is. A working checkout from before the rename pass still lives on my laptop — complete with a graveyard/ folder of abandoned first attempts. The lesson about running your code after refactoring it has since been learned.

There was also an OCR side of the internship — the "cool OCR stuff" — that never made it to github.

stack

lgbs
Python, BeautifulSoup4, colorama, raw http.client with the legacy-TLS workaround. Output to CSV.
pid-to-tsc
Vanilla javascript chrome extension, manifest v3, chrome.scripting injection.
status
Retired — internship tooling, last touched early 2025. Both repos public.