Andrew Friedman

afriedman412 [at] gmail [dot] com

Home • Work • Projects • Open Source • Writing • Content


PROJECTS


obomoboe

9-25-2026

A local-first read-it-later app for Mac and Windows. Save an article by pasting a link or clicking a bookmarklet, and obomoboe keeps a full copy on your computer (clean reading view, original page and images) so it's still there when the original changes or disappears. No account, no tracking, works offline.

Under the hood it's a Flask app in a native window (pywebview), with SQLite and FTS5 full-text search across every saved article. Extraction runs on trafilatura plus custom heuristics that check each save against the length the page claims and detect bot checks, falling back to archive.today and keeping whichever copy is fuller. The bookmarklet captures the page as the browser renders it, so it works on logged-in sites and ones that block scrapers.

Tags are suggested offline from publisher labels and word frequency, with optional refinement by Claude. Packaged with PyInstaller and shipped through a GitHub Actions release pipeline that builds, signs and notarizes the Mac app and builds for Windows, with in-app update checks.

SKILLS:
Python, Flask, SQLite / FTS5, Web Scraping, LLMs, PyInstaller, CI/CD, Desktop Apps

Petey

1-1-2026

An open-source Python framework for extracting structured data from PDF forms, driven by versioned declarative blueprints (.bpt) that specify field locations, types and processing rules per form. Distributed as a pip-installable package with a CLI, Python API and FastAPI service.

Fuses deterministic extraction (AcroForm, text-clip, zonal OCR) with eval-gated LLM steps — reliability where it matters, model intelligence only where it helps. Runs fully on-prem with no outbound API calls at extraction time, for regulated environments where documents can't leave the building. Pluggable model-serving layer abstracts LLM and VLM backends behind one interface, with an async concurrency manager splitting CPU and API worker pools to maximize throughput under provider rate limits.

Blueprints are validated by an agentic loop that generates synthetic ground-truth data, fills forms mechanically, extracts, scores the results against what it wrote, diagnoses what failed and revises, iterating until the blueprint converges. Verification uses five per-field verdicts (including silent, non-throwing failures) and an eleven-mode failure taxonomy, run as a regression suite across 62 forms. Factorial sweeps across model × parser × dataset track accuracy, latency and cost. Production-deployed as a containerized FastAPI service on Google Cloud Run with autoscaling. Also ships as a standalone desktop application for macOS and Windows, running entirely on local hardware.

SKILLS:
Python, FastAPI, LLMs, Agentic Systems, Evaluation Frameworks, Synthetic Data, OCR, Async Concurrency, Docker, GCP Cloud Run

Short-Term Rental Revenue Estimator

12-5-2025

Put in a Zillow link in Chicago, the number of bedrooms and bathrooms and how many people it accommodates, get back optimal price per night, expected annual occupancy and predicted total revenue if you were to list it on AirBNB.

Built on a trio of LightGBM models and powered by a neighbor-search stack that uses BallTree for precise local comps, FAISS for fast semantic similarity, and KNN-style aggregation to turn those neighbors into pricing and occupancy signals.

Also includes a user-facing AI interface that synthesizes model predictions into plain-language investment evaluations.

MSDS Capstone project. Blood, sweat and tears. Not investment advice!!!

SKILLS:
Machine Learning, Feature Engineering, Backend Development, Cloud Deployment, DevOps & Tooling

The Shape of Meaning — Model Sensitivity to Textual Perturbation

12-1-2025

An empirical study of how off-the-shelf NLP classifiers respond when language is systematically distorted. Used dialogue from The Office, It's Always Sunny in Philadelphia and South Park — three sitcoms with similar formats but distinct tonal identities — as a natural comparative corpus, splitting ~150,000 lines into ~28,000 overlapping 8-line chunks and scoring each chunk for Emotion (4-model ensemble), Toxicity (Detoxify) and Topic (BART-large-MNLI zero-shot).

Applied a series of controlled perturbations (case/punctuation removal, light and full token shuffling, and a "shuffle with preservation" condition that scrambled word order while keeping punctuation and capitalization intact) then quantified how each signal degraded using PCA + UMAP embeddings, silhouette scores, Jensen-Shannon divergence and Wasserstein distance.

Graduate project, Northwestern MSDS.

SKILLS:
NLP, Model Evaluation, Zero-Shot Classification, Hugging Face, PyTorch, UMAP / PCA, Statistical Analysis

Look At Me

12-4-2023

A light, modular website template built on Python, Jinja2 and not much else.

I made Look At Me because I was having trouble figuring out how to best structure a portfolio website that could showcase projects that fell under multiple categories. For example: I wanted to have a "writing" page and a "data journalism" page, with projects formatted differently on each page. Some projects fell under both categories, but having an entry on each page for the same project didn't seem like the best approach.

Look At Me lets you build a site by filling out some YAML and maybe tweaking some Jinja code if you are feeling spicy. As a bonus, you don't need to learn a new language, or use an external service to do it!

SKILLS:
Flask, Jinja2, CSS

SaysWho

4-1-2023

A Python package for identification and attribution of quotes within a document.

SKILLS:
SpaCy, Named Entity Recognition, Numpy, Regular Expressions

Rap Caviar Gender Balance Tracker

10-1-2022

Automated data collection and visualization of Spotify's influential Rap Caviar playlist, tracking how the gender balance of artists continues to change over time.

(No longer updated due to API changes as of December 2024)

SKILLS:
SQL, AWS, ElasticBeanstalk, Plotly, Pandas

Tracking Use of Passive Voice When Reporting on Police Violence

1-13-2022

I tracked the rate of use of "officer-involved" and similar exonerative language across 20 years of American reporting on police violence. Collaboration with Brandon Soderberg.

SKILLS:
natural language processing

G Sheets Tools

9-1-2020

Light Python package for moving data in and out of Google Sheets.


Housing Court Data Scraper

6-1-2020

I built an app to help housing lawyers fight eviction during the pandemic upheaval.

SKILLS:
Selenium, BeautifulSoup, SQL

Tenant Help Hotline

6-1-2020

I set up a hotline to make it easier for tenants without internet access to contact Brooklyn Eviction Defense if they needed the organization's help.

SKILLS:
Twilio, SQL, AWS