Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 108 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,113 @@
# CT CALC
# CT Calc

Hello! This is the source code for CT Calc. More information here: https://ctcalc.com/about
CT Calc is a **Contact Time (CT) Calculator** for drinking water treatment. It calculates the CT value (disinfectant concentration × contact time) needed to achieve a target log-inactivation of *Giardia* or *viruses* using various disinfectants, in compliance with the EPA Surface Water Treatment Rule (EPA Manual EPA815-R-99-013).

## 🧞 Commands
Live site: https://ctcalc.com — About page: https://ctcalc.com/about

All commands are run from the root of the project, from a terminal:
---

| Command | Action |
## 📂 Repository Structure

```
ctcalc/
├── ingest/ # Data pipeline: CSV → JSON lookup table
│ ├── csv/ # Raw EPA reference data (one CSV per disinfectant/pathogen)
│ └── ingest.py # Python script that converts CSVs to inactivations.json
└── web/ # Astro web application (deployed to Vercel)
├── public/ # Static assets (favicon)
├── cypress/ # End-to-end tests (Cypress)
└── src/
├── components/
│ ├── card/ # Card.astro — generic card layout wrapper
│ ├── form/ # Form.svelte — interactive calculator form
│ └── nav/ # Nav.astro — site navigation
├── data/
│ └── inactivations.json # Generated CT lookup table (from ingest/)
├── layouts/
│ └── Layout.astro # Base HTML page layout
├── pages/
│ ├── index.astro # Main calculator page (/)
│ ├── about/index.astro # About page (/about)
│ └── api/calc.ts # REST API endpoint (GET /api/calc)
└── utils/
├── formula.ts # FC empirical formula calculations
├── inactivation.ts # JSON lookup + interpolation orchestration
├── interpolate.ts # Linear and trilinear interpolation helpers
├── parseQueryParams.ts # URL query-string parser
└── validate.ts # Input validation and TypeScript types
```

---

## 🛠 Key Technologies

| Layer | Technology | Purpose |
| :---- | :--------- | :------ |
| Web framework | [Astro](https://astro.build) | SSR pages, static pre-rendering, API routes |
| UI component | [Svelte](https://svelte.dev) | Interactive calculator form (`Form.svelte`) |
| Language | TypeScript | Type-safe utilities and API handler |
| Styling | SCSS / CSS Modules | Scoped component styles |
| Deployment | [Vercel](https://vercel.com) (Edge) | Production hosting with analytics |
| Node adapter | `@astrojs/node` | Local/standalone server for development |
| E2E testing | [Cypress](https://cypress.io) | End-to-end browser tests |
| Data pipeline | Python 3 | `ingest.py` converts CSV reference tables to JSON |

---

## ⚙️ How It Works

### 1. Data ingestion (`ingest/`)

EPA reference tables (from *EPA815-R-99-013*) are stored as CSV files in `ingest/csv/`. Each file is named `<disinfectant>__<pathogen>.csv` (e.g. `chloramine__giardia.csv`). Free chlorine + Giardia is a special case with its own sub-directory (`free_chlorine__giardia/`) because it varies across three dimensions (temperature, pH, and concentration).

Running the ingest script converts all CSVs into a single nested JSON lookup table saved to `web/src/data/inactivations.json`:

```bash
python ingest/ingest.py ingest/csv > web/src/data/inactivations.json
```

### 2. Web application (`web/`)

The app is a server-rendered Astro site with one interactive Svelte component. The request flow is:

```
Browser (Form.svelte)
→ GET /api/calc?disinfectant=...&pathogen=...&temperature=...&...
→ parseQueryParams – parse URL query string values as JSON
→ validate – validate all inputs; return typed params or error
→ getResults – compute CT value via two methods and return both
├── formula (fcFormula) – empirical EPA formula
└── interpolation (linear / trilinear from inactivations.json)
← JSON { formulaResult, interpolatedResult }
```

**Supported inputs:**

| Parameter | Options / Range |
| :-------- | :-------------- |
| Disinfectant | `free_chlorine`, `chlorine_dioxide`, `chloramine`, `ozone` |
| Pathogen | `giardia`, `virus` |
| Temperature | 0–25 °C |
| pH *(free chlorine + Giardia only)* | 6.0–9.0 |
| Concentration *(free chlorine + Giardia only)* | 0–3 mg/L |
| Inactivation log (*Giardia*) | 0.5, 1.0, 1.5, 2.0, 2.5, 3.0 |
| Inactivation log (*virus*) | 2.0, 3.0, 4.0 |

**Calculation methods:**
- **Formula** (`formula.ts`): Uses the EPA FC empirical formula (two variants: < 12.5 °C and ≥ 12.5 °C). Currently only applicable to free chlorine + Giardia.
- **Interpolation** (`inactivation.ts`): Looks up bracketing values from `inactivations.json` and interpolates — *linear* interpolation for all disinfectant/pathogen combinations except free chlorine + Giardia, which uses *trilinear* interpolation over temperature, pH, and concentration.

---

## 🚀 Development Commands

All commands are run from the `web/` directory:

| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
| `npm install` | Install dependencies |
| `npm run dev` | Start local dev server at `localhost:3000` |
| `npm run build` | Build production site to `./dist/` |
| `npm run preview` | Preview the production build locally |
| `npm run test:dev` | Run Cypress e2e tests against the dev server |
| `npm run test:preview` | Run Cypress e2e tests against the preview build |
82 changes: 37 additions & 45 deletions web/README.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,47 @@
# Astro Starter Kit: Basics

```
npm create astro@latest -- --template basics
```

[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics)
[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/basics)
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/basics/devcontainer.json)

> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!

![basics](https://user-images.githubusercontent.com/4677417/186188965-73453154-fdec-4d6b-9c34-cb35c248ae5b.png)
# CT Calc — Web Application

This is the Astro web application for [CT Calc](https://ctcalc.com). It provides a browser-based Contact Time (CT) calculator for drinking water treatment compliance.

## 🚀 Project Structure

Inside of your Astro project, you'll see the following folders and files:
## Project Structure

```
/
web/
├── public/
│ └── favicon.svg
├── src/
│ ├── components/
│ │ └── Card.astro
│ ├── layouts/
│ │ └── Layout.astro
│ └── pages/
│ └── index.astro
└── package.json
├── cypress/
│ └── e2e/
│ └── index.cy.ts # End-to-end tests
└── src/
├── components/
│ ├── card/ # Card.astro — layout card wrapper
│ ├── form/ # Form.svelte — interactive calculator form
│ └── nav/ # Nav.astro — site navigation bar
├── data/
│ └── inactivations.json # CT lookup table (generated by ingest/)
├── layouts/
│ └── Layout.astro # Base HTML page layout
├── pages/
│ ├── index.astro # Main calculator page (/)
│ ├── about/index.astro # About page (/about)
│ └── api/calc.ts # REST API endpoint (GET /api/calc)
└── utils/
├── formula.ts # EPA FC empirical formula
├── inactivation.ts # Lookup + interpolation orchestration
├── interpolate.ts # Linear and trilinear interpolation helpers
├── parseQueryParams.ts # URL query-string parser
└── validate.ts # Input validation and TypeScript types
```

Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.

There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.

Any static assets, like images, can be placed in the `public/` directory.

## 🧞 Commands

All commands are run from the root of the project, from a terminal:

| Command | Action |
| :--------------------- | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:3000` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro --help` | Get help using the Astro CLI |
## Commands

## 👀 Want to learn more?
All commands are run from the `web/` directory:

Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Install dependencies |
| `npm run dev` | Start local dev server at `localhost:3000` |
| `npm run build` | Build production site to `./dist/` |
| `npm run preview` | Preview the production build locally |
| `npm run test:dev` | Run Cypress e2e tests against the dev server |
| `npm run test:preview` | Run Cypress e2e tests against the preview build |
| `npm run astro ...` | Run Astro CLI commands (e.g. `astro check`) |