Desktop-first analytics for engineers. Import, clean, visualize, and analyze industrial process data — locally, with zero cloud dependencies.
What is FlowMeter? • Quick Start • Features • User Guide • FAQ • Configuration • Technical Reference • Contributing
FlowMeterPresentation_readme.mp4
FlowMeter is a local desktop application for engineers and analysts who work with time-series data from industrial processes. It pairs a FastAPI Python backend with a React frontend into a single, cohesive tool — one that runs entirely on your machine, processes your data locally, and never sends anything to the cloud.
Unlike general-purpose analytics tools, FlowMeter is purpose-built for the problems that come up daily in process engineering: sensor data that needs cleaning, mass balances that need to close, regression models for correlating process variables, and professional reports for management who don't have Python installed.
Who is it for?
- Chemical engineers analyzing reactor performance, conversion, selectivity, and yield
- Process engineers building heat and mass balance models, validating plant data
- Reliability engineers correlating sensor trends with equipment failure events
- Data analysts exploring large CSV exports from DCS, SCADA, or historian systems
- Researchers working with laboratory measurement data and batch experiments
What does it replace?
| Task | Without FlowMeter | With FlowMeter |
|---|---|---|
| Plot time-series data | Export to Excel, fight with chart wizard | Drag-and-drop upload, instant chart |
| Close a mass balance | Hand-calculation or custom Python script | Constraint equations + OSQP solver |
| Fit a regression model | scikit-learn in Jupyter, share notebook | Built-in UI, save model, share report |
| Share results | Colleagues need Python / Excel / Tableau | Self-contained .html file, opens anywhere |
| Repeat weekly reports | Rebuild charts from scratch | Save template, apply to new data in seconds |
Note
FlowMeter runs as a local web application: FastAPI server + React frontend, both on your machine. It can be packaged as a single standalone executable (PyInstaller) — end users need no Python, Node.js, or any development tools. They just double-click and open a browser.
| Challenge | FlowMeter Solution |
|---|---|
| Excel crashes with 500K+ rows | Handles millions of rows with LTTB downsampling and optimized rendering |
| Cloud tools raise data security concerns | 100% local — your data never leaves your machine |
| Jumping between Python, Excel, and plotting tools | All-in-one: cleaning, formulas, charts, ML, and export in one app |
| Sharing requires colleagues to install software | Export standalone HTML reports that open in any browser |
| Manual curve fitting in scattered tools | Built-in regression engine with 6 model types + custom formula fitting |
| Material balance calculations done by hand | Automatic data reconciliation with constraint equations (OSQP solver) |
| No coding environment on site | Python-like formula engine — no IDE, terminal, or pip install required |
| Repetitive dashboard setup for recurring reports | Reusable templates — configure once, apply to any matching dataset |
FlowMeter ships with 11 fully configurable visualization types. Each has its own dedicated configuration panel with relevant settings — no mode-switching or hidden menus.
| Type | Internal ID | Description | Best For |
|---|---|---|---|
| General Plot | universal |
Line, Scatter, Bar, Step, or Line+Scatter with per-series customization | Time-series trends, comparative analysis |
| Area Chart | area |
Filled area plots with optional stacking | Cumulative flows, composition breakdowns |
| Histogram | hist |
Distribution analysis with configurable bin count and KDE overlay | Understanding data spread and shape |
| Box Plot | box |
Statistical summaries: quartiles, medians, outliers | Comparing distributions across categories |
| Regression Analysis | regression |
Dedicated regression view with model selection, equation display, and prediction panel | Correlating variables, predictive modeling |
| PCA Analysis | pca |
Principal Component Analysis with loadings display and explained variance | Dimensionality reduction, pattern discovery |
| Custom Formula | formula |
Plot calculated expressions using Python-like syntax | Derived metrics, efficiency calculations |
| Correlation Matrix | correlation |
Color-coded heatmap of pairwise variable correlations | Identifying related variables at a glance |
| FFT Analysis | fft |
Frequency spectrum analysis with Hann windowing, overlap, and detrending | Detecting periodic signals, vibration analysis |
| Root Cause Analysis | root_cause |
Multi-method causality detection (Pearson, cross-correlation, mutual info, Granger) | Finding which variables drive a target variable |
| KPI / Summary | kpi |
Responsive card grid of aggregated scalars (sum, avg, min, max, median, count, first, last, std, or custom formula) with per-metric unit, decimals, and color | Dashboard tiles, period totals, headline numbers |
Note
The General Plot (universal) type supports four render modes per series: line, scatter, bar, and step. The PlotType enum (used in axis configuration for multi-axis overlays) contains three modes: Line, Scatter, and Line + Scatter. These are distinct from the per-series render types available in the General Plot.
Every chart is rendered by Plotly.js and includes a full suite of interactive controls out of the box.
- Per-Series Styling — Each series has its own color picker, chart type selector (line/scatter/bar/step), and Y-axis assignment (left or right)
- Per-Series Trendlines — Toggle individual regression trendlines per series, with confidence interval bands and IQR-based outlier removal
- Dual Y-Axes — Overlay variables with different units and scales on the same chart
- Interactive Zoom & Pan — Click and drag to zoom into transient events; double-click to reset
- Threshold Lines — Add horizontal reference lines with optional shaded regions above or below
- Custom Legend Labels — Rename series legends with debounced input for presentation-ready charts
- Storyline Event Markers — Overlay vertical event markers from the Storyline feature on time-series plots
- Drag & Drop Reorder — Rearrange visualization cards by dragging (powered by dnd-kit)
- Responsive Grid — Switch between 1, 2, or 3 column layouts via floating controls
- Dark Mode & Themes — Theme-aware rendering with multiple color themes and light/dark mode toggle
- LTTB Downsampling — Automatic Largest Triangle Three Buckets downsampling (500-point threshold) for smooth rendering of massive datasets
- Logarithmic Axes — Both X and Y axes support linear and logarithmic scales
- Lazy Loading — Plotly library is lazy-loaded to keep initial bundle size small
Tip
Use the per-chart date range filter to zoom into a specific incident window without losing your other chart configurations. A global date range filter is also available in the sidebar.
The formula engine lets you write Python-like expressions directly in the application — no coding environment, terminal, or pip install required.
# Thermal efficiency from measured process data
efficiency = (col['Power_Output'] / (col['Fuel_Flow'] * 42000)) * 100
# Smooth noisy temperature readings with a 30-point rolling average
temp_smooth = col['Exhaust_Temp'].rolling(window=30).mean()
# Mass balance residual to detect leaks
residual = col['Inlet_Flow'] - col['Outlet_Flow'] - col['Accumulation']
# Flag alarm conditions
alarm = np.where(col['Temp_Reactor'] > 420, 1, 0)
# Convert timestamps to elapsed hours
hours = (col['Timestamp'] - col['Timestamp'].iloc[0]).dt.total_seconds() / 3600
# Multiple named outputs plotted on the same chart
result1 = col['Pressure'] * col['Volume'] / col['Temperature'] # Ideal gas product
result2 = col['Flow_In'] - col['Flow_Out'] # Net flow balance| Category | Available Functions |
|---|---|
| Math | sin, cos, tan, exp, log, log10, sqrt, power, abs, ceil, floor |
| Statistics | mean, std, var, min, max, median, cumsum, cumprod |
| Pandas | .rolling(), .diff(), .shift(), .cumsum(), .interpolate(), .fillna() |
| Conditional | np.where(), np.select(), boolean indexing |
| Date/Time | .dt.total_seconds(), .dt.hour, .dt.dayofweek, date arithmetic |
| Namespace | col (DataFrame), np (NumPy), pd (Pandas), plus all defined Global Variables |
Formula editor features:
- Access any column via
col['ColumnName']— names are case-sensitive - Assign to
result,result1,result2, etc. for multiple simultaneous outputs on one chart - Column browser with search and one-click insertion at cursor position
- Per-result configuration: chart type, color, Y-axis assignment, individual regression trendline
- AI formula generation tab: describe your calculation in plain English, receive working formula code (requires API key)
- Custom X-axis formula (
x_formula) for computed horizontal axes
Tip
Global Variables you define are automatically added to the formula namespace. Chain calculations: define Delta_T as a global variable, then reference col['Delta_T'] in any formula or subsequent global variable without recomputing it.
A full-featured regression engine with 6 standard models plus custom formula fitting via scipy.optimize.curve_fit.
|
Standard Models
|
Custom Formula Fitting Define arbitrary mathematical models:
|
Regression capabilities:
- Multi-Variable Regression — Predict Y from multiple X predictor variables simultaneously
- Confidence Intervals — 95% CI bands displayed around regression curves
- Outlier Removal — IQR-based filtering with configurable multiplier
- Robust Regression — Non-linear loss functions:
soft_l1,huber,cauchy,arctan - Optimization Methods — Choose solver:
trf(Trust Region),lm(Levenberg-Marquardt),dogbox - Equation Display — Fitted equation shown in the UI, ready to copy
- Quality Metrics — R², MSE, MAE computed and displayed for every model
- Date Handling — Date/time columns automatically converted to numeric for regression
- Model Persistence — Save and reload fitted models via the
/api/v1/modelsAPI - Prediction — Use fitted models to predict Y for new X inputs
Tip
For noisy industrial data, try Ridge or Elastic Net before increasing polynomial degree. Regularization often gives better generalization than overfitting with a high-degree polynomial.
Enforce physical constraints — mass balances, energy balances, stoichiometric relationships — using constrained least-squares optimization.
Reconciliation modal: constraint equation editor, sigma configuration, and per-variable error report
How it works:
- Define constraint equations using your column names:
Inlet_Flow = Outlet_Flow + Accumulation Steam_Generation = Steam_Consumption + Losses Feed_A + Feed_B = Product + Waste + Recycle - FlowMeter solves for the minimum-variance adjustment to your measured values that satisfies all constraints simultaneously
- Results include per-variable error statistics and a downloadable reconciled dataset (
.xlsx)
| Option | Description |
|---|---|
| Fixed Sigma | Apply uniform measurement uncertainty across all variables |
| Per-Variable Sigma | Specify different uncertainties for each variable (e.g., flowmeters vs. thermocouples) |
| Non-Negativity | Constrain reconciled values to be ≥ 0 |
Tip
FlowMeter uses two solver strategies: a vectorized analytical solution (fast closed-form) when non-negativity is disabled, and the OSQP quadratic programming solver with warm-starting when non-negativity constraints are enabled. Constraint equations are parsed symbolically with SymPy and column names are matched using canonical normalization (tolerant of spaces and special characters).
Warning
If reconciliation diverges or reports infeasibility, check that your constraints are linearly independent. Including a redundant constraint that is a linear combination of others (e.g., writing A = B + C and B = A - C) will cause OSQP to fail.
The Onboarding Wizard walks you through a data preparation pipeline in 4 steps, applied in order.
| Step | What Happens |
|---|---|
| Step 1: Upload | Drag-and-drop a .csv, .xlsx, or .xls file (up to 50 MB). Column types detected automatically. |
| Step 2: Preprocessing (optional) | Header row — skip metadata rows at the top. Value replacements — regex substitution across columns (e.g., "1,5" → "1.5"). Row filters — keep or remove rows based on column value conditions using 6 numeric and 2 string operators. Custom NaN — treat specific sentinel values (e.g., -999, #REF!) as missing. NaN strategy — drop, fill_zero, interpolate, fill_forward, fill_backward, or none. Resampling — aggregate to fixed time intervals using mean, sum, min, max, first, last, or median. |
| Step 3: Getting Started Choice | Choose Import Template (load a saved config), Start from Scratch (manual setup), or AI-Assisted (let AI suggest charts). |
| Step 4: Advanced Config (Scratch path only) | Optionally configure Data Reconciliation constraints and Global Variables before the main dashboard loads. Both are also accessible from the sidebar at any time. |
Note
Preprocessing is entirely optional. If your data is clean, skip Step 2 and go straight to visualization. All preprocessing settings are saved in templates so you configure them once.
Save your entire dashboard configuration — all visualizations, formulas, reconciliation settings, global variables, annotations, and comments — as a reusable template.
- Persistent Storage — Templates saved as JSON files in
backend/data/templates/, available across sessions - Auto-Detect Required Variables — Templates extract which dataset columns are needed, validating against new datasets before loading
- Export/Import — Download templates as
.jsonfor backup; upload to restore on another machine - Full CRUD — Browse, rename, delete, and duplicate saved templates from the sidebar
- AI Guidance — Templates can include natural-language guidance text sent to the AI along with column metadata
Tip
The "configure once, reuse always" pattern: Build a "Weekly Reactor Performance" template with all your standard charts, formulas, and reconciliation rules. Every Monday, upload the new export and apply the template — your complete dashboard is ready in seconds.
Define computed columns that are available across all visualizations in the dashboard simultaneously.
# These appear as new columns selectable in every chart
Efficiency = (col['Power_kW'] / col['Fuel_Flow_kg_s'] / 43000) * 100
Delta_T = col['T_Outlet'] - col['T_Inlet']
Mass_Balance = col['Feed_Flow'] - col['Product_Flow'] - col['Waste_Flow']
Heat_Duty = col['Flow_Rate'] * col['Cp'] * col['Delta_T'] # References Delta_T above- Global variables appear as new columns selectable in any chart's Y-axis, formulas, and regression
- Computed in definition order — later variables can reference earlier ones for chained calculations
- Each variable has an optional description field for documentation
- Accessible from the Onboarding Wizard (Step 4) and from the dashboard sidebar at any time
- Saved in templates and restored on load
Note
Global variables are evaluated against the full (preprocessed) dataset. Reference them in any formula using col['VariableName'] exactly as you would any dataset column.
Export your dashboard as a self-contained HTML file that anyone can open in any browser.
- Standalone — Single
.htmlfile with all charts embedded as base64 PNG images and all CSS inlined - Zero Dependencies — No server, no internet, no software required to open
- High Quality — Charts rendered server-side via Plotly + Kaleido at crisp resolution
- Customizable Branding — Author name, job title, location, custom logo (base64 upload), primary and secondary accent colors
- Statistical Summary — Auto-generated statistics table (mean, std, min, max) for all visualized columns
- Per-Chart Notes — Add comments to each visualization that appear in the final report
- Storyline Events — Timeline annotations included in the exported report
- Date Range — Optionally restrict the report to a specific date/time window
Note
Reports are generated using Jinja2 templates with parallel chart rendering via ThreadPoolExecutor. Even dashboards with dozens of charts export in seconds. Kaleido is prewarmed on application startup to minimize first-export latency.
FlowMeter integrates AI through a LangGraph workflow with structured output validation and automatic correction loops.
AI Suggestions Panel: provider/model selection, natural-language description, suggested visualizations
Supported providers:
| Provider | Description |
|---|---|
| Google Gemini | Gemini model family via Google AI Studio |
| OpenAI | GPT and o-series models |
| Anthropic Claude | Claude model family |
Models are fetched dynamically from each provider's API when you enter your API key — no hardcoded lists. You always see the latest models available to your account.
AI capabilities:
- Visualization Suggestions — Describe your data in plain English; AI proposes chart configurations you review and apply with one click (per suggestion or Apply All)
- Formula Generation — Describe a calculation; AI writes the Python formula code (integrated in the Formula Editor)
- Dynamic Model Discovery — Available models fetched in real-time from provider APIs, so new models appear automatically
- Reasoning Effort Control — Adjust AI thinking depth (Low / Medium / High) to balance speed vs. quality; maps to provider-specific features (Anthropic extended thinking, OpenAI reasoning effort, Gemini thinking budget)
- Structured Output — AI responses validated against Pydantic schemas with automatic retry and self-correction
- Column Validation — AI-suggested column names cross-checked against your actual dataset, with fuzzy-match correction hints
- Context-Aware — AI receives your column names, data types, sample statistics, and any guidance text you provide
- Dataset Profile (always on) — A server-computed profile grounds every suggestion in the real data: per-column role (numeric / categorical / datetime / boolean / identifier / constant), null %, cardinality, skew, example values, datetime-like text columns, the best timestamp candidate, and strong correlation pairs (|r| ≥ 0.7). Sent as aggregates (plus a few example values) — no full rows
- Dataset Access (Agentic Tools) — Optional. Let the AI issue read-only tool calls against the loaded dataset — starting with a one-shot
overview()profile, then drilling in with sample rows, value counts, correlations, group-by aggregates, quantiles, outlier counts, and more — so it grounds suggestions in the live data rather than metadata alone. Off by default; the agent-loop iteration cap is configurable in AI Settings - Formula Verification (closed-loop) — In dataset-access mode the AI can
test_formulaa candidate expression — executing it on a sample to confirm it runs and preview its output (with auto-correction for^→**, a missingresult =, or fuzzy-matched column names) — before proposing a formula chart or formula KPI, so suggested formulas are verified rather than guessed - Analysis-Engine Tools — In dataset-access mode the AI can run the app's own analytics to ground advanced chart suggestions in evidence:
detect_periodicity(FFT dominant cycle → FFT view),rank_drivers(Pearson / lagged cross-correlation / mutual info / Granger → Root Cause view), andquick_fit(linear R² + coefficients → Regression view)
Important
AI features are optional and require your own API key from the chosen provider. FlowMeter works fully without AI. All API calls go directly from your machine to the AI provider — FlowMeter has no telemetry, no intermediary servers, and no data collection.
Add timeline event annotations to your charts for documenting process incidents, maintenance events, and recipe changes.
Storyline modal: event entry form on left, vertical timeline on right, chart markers overlaid on plots
- Two-Pane Modal — Event entry form on left, vertical scrollable timeline on right
- Event Fields — Date/time picker, title, description, and custom color marker
- Full CRUD — Add, edit, and delete events; hover-based action buttons
- Chart Integration — Events appear as colored vertical lines on time-series plots with hover tooltips
- Color Categorization — Use different colors for event types (red for upsets, yellow for maintenance, blue for recipe changes)
- Template Persistence — Storyline events are saved in templates and included in exported HTML reports
- Auto-Sorting — Timeline always displays events newest-first
The fastest way to get started — no Python, Node.js, or development tools required:
- Download the latest release from GitHub Releases
- Extract the archive to a folder of your choice
- Run the executable:
- Windows: double-click
FlowMeter.exe - Linux:
chmod +x FlowMeter && ./FlowMeter
- Windows: double-click
- Your default browser automatically opens to
http://127.0.0.1:8000
That's it. One file, double-click, done.
Note
The executable bundles the FastAPI backend and compiled React frontend together. When you run it, the backend starts on port 8000 and automatically serves the frontend. If port 8000 is already in use, set the PORT environment variable before launching: PORT=8001 ./FlowMeter.
Step 1 — Clone the repository:
git clone https://github.com/sommaa/flowmeter.git
cd flowmeterStep 2 — Start the backend:
cd backend
python -m venv venv
# Activate virtual environment:
# Windows PowerShell: venv\Scripts\Activate.ps1
# Windows CMD: venv\Scripts\activate.bat
# Linux / macOS: source venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000Step 3 — Start the frontend (separate terminal):
cd frontend
npm install
npm run dev| Service | URL | Notes |
|---|---|---|
| Frontend (dev) | http://localhost:3000 |
Hot Module Replacement enabled |
| Backend API | http://localhost:8000 |
Auto-reloads on code changes |
| API Docs (Swagger) | http://localhost:8000/docs |
Interactive API explorer |
| API Docs (ReDoc) | http://localhost:8000/redoc |
Clean reference view |
The frontend dev server proxies all /api requests to the backend at port 8000 automatically.
Quick Start Script (Linux/macOS):
chmod +x start.sh
./start.sh
# Starts both backend and frontend in a single commanddocker-compose upThis starts both services:
- Frontend at
http://localhost:3000 - Backend API at
http://localhost:8000 - API Docs at
http://localhost:8000/docs
Don't have process data handy? The examples/ folder ships a complete,
ready-to-run example:
| File | What it is |
|---|---|
examples/Ethylene_Cracker_Process_Data.xlsx |
Synthetic ethylene-cracker dataset — 336 hourly samples (14 days) × 40 process tags |
examples/Ethylene Cracker Overview.json |
A matching dashboard template — 8 visualizations, formulas, computed global variables, and AI guidance |
- Launch FlowMeter (any option above).
- Upload
examples/Ethylene_Cracker_Process_Data.xlsx. - Choose Import Template and select
examples/Ethylene Cracker Overview.json— the full dashboard renders immediately.
See examples/README.md for details and tips. The template's
required columns match the dataset exactly, so it applies with no extra setup.
This section walks first-time users through every major feature, step by step.
What you need: A .csv, .xlsx, .xls, or .parquet (.pqt) file with your process data — or just grab the bundled example dataset to follow along.
- When you first launch FlowMeter, the Onboarding Wizard appears automatically
- Click Upload File or drag and drop your file onto the upload zone
- Wait for the file to parse — the app shows detected column names, row count, and data types
What you'll see: A file info summary showing rows, columns, detected types, and date range (if a timestamp column was found).
Expected data structure:
- Row 0 (by default): column headers
- Columns: variables, sensors, measurements, parameters
- Rows: time points or samples
- Optional: a
Date,Timestamp, orTimecolumn unlocks time-series features
Tip
If your file has metadata rows at the top (plant name, report date, etc.) before the actual headers, use the Header Row setting in Step 2 to skip them.
If your data needs preprocessing, configure it in the wizard's Step 2 panel:
- Header Row — Set which row index contains column names (0-based)
- Value Replacements — Fix locale issues: replace
,with.in numbers, remove unit strings, etc. - Row Filters — Remove outlier timestamps, filter to a specific operating mode, or exclude shutdown periods
- Custom NaN Values — Tell FlowMeter to treat
-999,N/A, or#REF!as missing data - NaN Strategy — Choose how to handle missing values: drop, interpolate, forward-fill, etc.
- Resampling — Aggregate high-frequency data (e.g., 1-second → 1-minute means)
What you'll see: A live data preview that updates as you configure each option.
Note
All preprocessing settings are saved in templates, so you only configure them once per data format.
After preprocessing, select how to start the dashboard:
|
Import Template Load a previously saved dashboard configuration. All charts, formulas, and reconciliation rules are applied automatically. Best for: recurring reports with the same data structure. |
Start from Scratch Build your dashboard manually. You can optionally configure Data Reconciliation and Global Variables before the main view loads. Best for: first-time exploration of a new dataset. |
AI-Assisted Describe your data and what you're analyzing. AI suggests an initial set of visualizations based on your column names and description. Best for: unfamiliar datasets where you're unsure what to look at first. |
- Click Add Visualization in the dashboard header
- Select a Visualization Type from the dropdown (start with General Plot)
- Choose your X-Axis: a timestamp column,
Index(row number), or a custom formula - Select one or more Y-Axis Variables from your dataset columns
- The chart appears immediately — no submit button needed
What you'll see: A Plotly chart card with a configuration panel. Hover the card to reveal expand, duplicate, and delete controls.
Tip
Click the card title to rename it. Descriptive names like "Reactor Temperature vs. Feed Rate" make exported reports much more readable.
With a chart created, explore the configuration panel on the right side of each card:
- Per-Series Settings — Click a series name to expand: change color, switch line/scatter/bar/step, assign to left or right Y-axis
- Dual Y-Axis — Enable the secondary Y-axis and assign selected series to it for variables with different scales
- Add Trendline — Toggle per-series regression trendline; configure model type, degree, and confidence interval
- Threshold Lines — Add horizontal reference lines (alarm limits, spec limits) with optional shaded regions
- Date Range Filter — Restrict this specific chart to a particular time window
- Notes — Add per-chart comments that appear in exported reports
Tip
The floating control panel (bottom-right corner) lets you switch between 1, 2, and 3 column grid layouts. Drag visualization cards to reorder them on the dashboard.
- Create a new visualization and select Custom Formula as the type
- Click Open Formula Editor to open the full-screen formula modal
- Write your formula using Python syntax:
result = (col['T_Out'] - col['T_In']) / col['Q_Input'] * 100
- Click Run — the result is plotted immediately
- Optionally switch to the AI tab to describe your formula in plain English and receive generated code
Column browser: Click any column name in the browser panel to insert it at the cursor position.
Warning
Column names are case-sensitive. If you get a KeyError, verify the exact column name in the column browser. Do not use col.ColumnName — always use col['ColumnName'].
- Create a new visualization and select Regression Analysis as the type
- Select your Y variable (what to predict) and X predictor(s)
- Choose a Model Type: Linear, Polynomial, Ridge, Lasso, Elastic Net, Random Forest, or Custom Formula
- For Custom Formula: write your equation (e.g.,
a * exp(-b * x) + c), enter initial guesses and bounds - The fitted equation, R², MSE, and MAE appear in the panel below the model selector
- Toggle Show Confidence Interval for 95% CI bands
- Click Save Model to persist the fitted model for future prediction
Tip
For multi-variable regression, select multiple X predictor columns. The model trains on all predictors simultaneously and reports combined R² and MSE for the multi-predictor fit.
- Open the Reconciliation panel from the sidebar or from Step 4 of the Onboarding Wizard
- Add constraint equations, one per line:
Feed = Product + Waste Steam_Gen = Steam_Use + Losses - Set Sigma Mode:
- Fixed All — uniform uncertainty for all variables
- From Config — enter per-variable sigma values in the table that appears
- Enable Non-Negativity if flow values must be ≥ 0
- Click Run Reconciliation
- Review the error report: mean error, MAE, relative error %, standard deviation, and max absolute change per variable
- Click Download Results to get the reconciled dataset as an Excel file (
.xlsx)
Warning
Constraint equations must use column names that match your dataset. If reconciliation reports infeasibility, verify your constraints are linearly independent and variable names are spelled correctly.
- Click the Templates button in the sidebar
- Click Save Current Configuration and give your template a descriptive name
- The template saves to
backend/data/templates/and persists across sessions - To reuse: upload a new dataset → Import Template → select your template
Template includes: all visualizations, global variables, reconciliation constraints, storyline events, column descriptions, preprocessing settings, and AI guidance text.
Tip
Use Export Template to download the JSON file and share with colleagues. They can Import Template on their own FlowMeter instance to instantly replicate your dashboard.
Caution
Templates can contain custom formulas that run on your machine when the dashboard renders. FlowMeter sandboxes formula evaluation (no imports, no file or system access — only numpy/pandas math), but you should still only import templates from sources you trust. See SECURITY.md.
- Click the Export button in the header bar
- Fill in the Export Settings modal:
- Author name, job title, location
- Optional custom logo (upload an image — converted to base64 automatically)
- Primary and secondary accent colors (hex codes)
- Click Generate Report
- A standalone
.htmlfile downloads to your machine - Open in any browser — no software, internet, or server required
- Share with colleagues via email or file transfer
Note
The exported HTML file is truly standalone: all charts are embedded as base64 PNG images, all CSS is inlined. File size depends on chart count — typically 1–5 MB for a 10-chart dashboard.
Q: Does FlowMeter require an internet connection?
No. FlowMeter runs entirely on your machine. The only time it connects to the internet is when you use AI features, which sends your column names and description to the AI provider's API. All other features — visualization, regression, reconciliation, export — work fully offline.
Q: How large a dataset can FlowMeter handle?
The upload limit is 50 MB by default (configurable via MAX_FILE_SIZE_MB). FlowMeter has been tested with datasets of several hundred thousand rows. For display, LTTB downsampling automatically reduces rendered points to 500 per series. For processing (formulas, regression, reconciliation), the full dataset is used.
Q: Can I use FlowMeter with data from a database or historian?
Not directly — FlowMeter accepts file uploads only (CSV/Excel). Export your data from your historian or database as CSV or Excel, then upload it. If you need programmatic data loading, the /api/v1/data/upload endpoint can be called directly from scripts.
Q: What happens to my data if I close the browser tab?
Your dataset stays loaded in the backend (in memory) until the backend process stops. Reopen the browser and navigate to http://localhost:8000 (or localhost:3000 in dev mode) — your data is still available for the current session. Templates you saved persist indefinitely as JSON files on disk.
Q: Can multiple users access the same FlowMeter instance?
FlowMeter is designed as a single-user desktop application. It has no authentication and stores datasets in memory rather than a database. Running it as a shared server is not recommended — each user should run their own instance.
Q: Why is the formula result NaN?
The most common causes:
- Column name typo — names are case-sensitive; use the column browser to insert exact names
- Missing data propagation — NaN in input columns propagates to output; use
.fillna(0)or choose an NaN strategy during preprocessing - Division by zero — protect with
np.where(col['Denominator'] != 0, col['Numerator'] / col['Denominator'], np.nan)
Q: The AI provider is not responding — what do I check?
- Verify your API key is correct and has not expired
- Check that the key has permission to access the selected model
- Verify your network connection — all AI calls go directly from your machine to the provider
- Try a different provider or model
- Check the provider's status page for outages (
401= bad key,429= rate limited)
Q: Can I use FlowMeter on macOS?
The pre-built executables target Windows and Linux. macOS users can run from source (Option 2 in Quick Start). The Python backend and Node.js frontend both work on macOS without modification. The scripts/build_app.sh build script also works on macOS.
FlowMeter is configured via environment variables. Create a .env file in the backend/ directory to override defaults. A .env.example file is provided as a starting point.
cp backend/.env.example backend/.env
# Edit backend/.env with your preferred settings| Variable | Default | Description |
|---|---|---|
APP_NAME |
FlowMeter API |
Application display name shown in API docs and logs |
APP_VERSION |
1.0.0-alpha.3 |
Semantic version string |
DEBUG |
false |
Enable debug mode — verbose logging, full error tracebacks in API responses |
HOST |
0.0.0.0 |
Server bind address (0.0.0.0 = all network interfaces) |
PORT |
8000 |
Listening port for the FastAPI/Uvicorn server |
CORS_ORIGINS |
["http://localhost:3000","http://localhost:5173"] |
Allowed CORS origins for the frontend (JSON array) |
MAX_FILE_SIZE_MB |
50 |
Maximum upload file size in megabytes |
ALLOWED_EXTENSIONS |
[".xlsx",".xls",".csv"] |
Accepted file extensions (JSON array) |
UPLOAD_DIR |
uploads |
Directory for temporary file storage during upload processing |
MAX_DATASETS_PER_SESSION |
10 |
Maximum number of datasets that can be loaded simultaneously |
Note
All settings use Pydantic Settings with automatic type conversion and validation. Variable names are case-insensitive. See backend/app/core/config.py for the complete Settings class.
🏗️ Architecture Overview
FlowMeter uses a decoupled client-server architecture optimized for local deployment and packaging as a standalone executable.
┌──────────────────────────────────────────────────────────────────┐
│ FlowMeter Application │
├──────────────────────────────┬───────────────────────────────────┤
│ Frontend (UI) │ Backend (API) │
├──────────────────────────────┼───────────────────────────────────┤
│ React 19 + TypeScript 5.4 │ FastAPI 0.115+ (Python 3.10+) │
│ Vite 7 (build / HMR) │ Uvicorn (ASGI server) │
│ Plotly.js 2.28 (charts) │ Pandas + NumPy (data) │
│ Zustand 5.0 (state) │ Scikit-Learn (ML models) │
│ Radix UI (components) │ SciPy (curve fitting) │
│ dnd-kit (drag-and-drop) │ SymPy + OSQP (reconciliation) │
│ TailwindCSS (styling) │ Kaleido (PNG rendering) │
│ Axios (HTTP client) │ Jinja2 (report templates) │
│ Lucide (icons) │ LangChain + LangGraph (AI) │
└──────────────────────────────┴───────────────────────────────────┘
│ │
│ REST API (JSON) │
│◄────────────────────────────►│
│ │
└──────────────┬───────────────┘
│
PyInstaller / Docker / Gunicorn
│
Single Executable or Container
Middleware stack (execution order):
CORSMiddleware— adds CORS headers for browser cross-origin requestsProcessTimeMiddleware— addsX-Process-Timeheader (request profiling)GZipMiddleware— compresses responses > 1000 bytes (significant for large JSON payloads)
Design principles:
| Principle | Implementation |
|---|---|
| Local-First | All processing on your machine — zero mandatory cloud calls |
| Stateless API | Backend holds datasets in memory; all context is passed per request |
| NaN-Safe JSON | Custom NaNSafeJSONResponse converts NaN/Infinity to null for JSON compliance |
| Debounced Updates | UI config changes are debounced to prevent rendering on every keystroke |
| Parallel Export | Report generation uses ThreadPoolExecutor for concurrent chart rendering |
| Kaleido Prewarm | Kaleido renderer is prewarmed on startup to minimize first-export latency |
| SPA Catch-All | Unmatched routes serve index.html for React Router client-side navigation |
| LRU Settings Cache | get_settings() is cached with @lru_cache() to avoid repeated environment variable parsing |
Deployment modes:
| Mode | Command | Notes |
|---|---|---|
| Development | uvicorn app.main:app --reload |
Hot reload, full error tracebacks |
| Standalone Executable | ./FlowMeter or FlowMeter.exe |
PyInstaller bundle, no external dependencies |
| Docker | docker-compose up |
Containerized, port-mapped services |
| Production | gunicorn app.main:app -w 4 -k uvicorn.workers.UvicornWorker |
Multi-worker ASGI |
🛠️ Full Technology Stack
| Technology | Version | Purpose |
|---|---|---|
| React | 19.2.3 | Component-based UI with hooks |
| TypeScript | 5.4.5 | Static type safety |
| Vite | 7.2.7 | Fast dev server with HMR, optimized production builds |
| Plotly.js | 2.28.0 | Interactive, publication-quality charts |
| Zustand | 5.0.9 | Lightweight state management with slices |
| Radix UI | Latest | Accessible, unstyled UI primitives |
| Lucide React | 0.561.0 | Consistent SVG icon set |
| dnd-kit | 6.3.1 | Accessible drag-and-drop for card reordering |
| TailwindCSS | 3.4.4 | Utility-first CSS framework |
| Axios | 1.13.2 | HTTP client for API communication |
| React Dropzone | 14.2.3 | Drag-and-drop file upload zone |
| cmdk | 1.1.1 | Command palette / combobox component |
| simplebar-react | 3.3.2 | Custom scrollbar component |
| uuid | 13.0.0 | UUID generation for component keys |
| Vitest | 4.0.16 | Vite-native unit testing framework |
| @testing-library/react | 16.3.1 | Component testing utilities |
| jsdom | 27.3.0 | DOM simulation for unit tests |
| Technology | Version | Purpose |
|---|---|---|
| FastAPI | 0.115+ | High-performance async Python web API |
| Uvicorn | 0.32+ | ASGI server with standard extras |
| Pandas | 2.2+ | DataFrame-based data manipulation |
| NumPy | 1.26+ | Numerical computing |
| Scikit-Learn | 1.5+ | Regression, PCA, preprocessing pipelines |
| SciPy | 1.13+ | Non-linear curve fitting (curve_fit), optimization |
| SymPy | 1.13+ | Symbolic equation parsing for reconciliation |
| OSQP | 0.6+ | Quadratic programming solver |
| Plotly | 5.18+ | Server-side chart data generation |
| Kaleido | 0.2+ | Plotly → PNG rendering for HTML reports |
| Jinja2 | 3.1+ | HTML report template engine |
| Pydantic | 2.9+ | Request/response validation (50+ schemas) |
| Pydantic-Settings | 2.6+ | Environment variable configuration |
| openpyxl | 3.1+ | Excel file reading and writing |
| xlrd | 2.0+ | Legacy .xls file reading |
| orjson | 3.10+ | Fast JSON serialization |
| lttbc | 0.2+ | LTTB downsampling (C extension) |
| matplotlib | 3.9+ | Supplementary plotting utilities |
| python-multipart | 0.0.12+ | Multipart file upload handling |
| PyInstaller | 6.10+ | Bundle into standalone executable |
| Pytest | 8.0+ | Test framework |
| httpx | 0.27+ | Async HTTP test client |
| Technology | Version | Purpose |
|---|---|---|
| LangChain | 1.2+ | LLM orchestration and prompt management |
| LangGraph | 1.0+ | Stateful AI workflow graphs with retry loops |
| LangSmith | 0.1+ | Tracing and observability (optional) |
| langchain-google-genai | 2.0+ | Google Gemini integration |
| langchain-openai | 0.3+ | OpenAI GPT integration |
| langchain-anthropic | 0.3+ | Anthropic Claude integration |
📁 Project Structure
flowmeter/
├── frontend/ # React / TypeScript application
│ ├── src/
│ │ ├── components/
│ │ │ ├── visualizations/ # Chart rendering & configuration
│ │ │ │ ├── InteractivePlot.tsx # Main Plotly chart renderer
│ │ │ │ ├── VisualizationCard.tsx # Card wrapper with header
│ │ │ │ ├── ConfigurationPanel.tsx # Settings panel (right side)
│ │ │ │ ├── FormulaEditorModal.tsx # Formula code editor modal
│ │ │ │ ├── RootCauseAnalysis.tsx # Causality visualization
│ │ │ │ ├── controls/ # Sub-control components
│ │ │ │ └── sections/ # Config sub-panels (Axis, Regression, FFT, …)
│ │ │ ├── features/ # Feature modules
│ │ │ │ ├── Dashboard/ # Main grid layout
│ │ │ │ ├── DataManagement/ # File upload UI
│ │ │ │ ├── DataCleaning/ # Preprocessing modal
│ │ │ │ ├── Reconciliation/ # Constraint editor modal
│ │ │ │ ├── Templates/ # Template browser
│ │ │ │ ├── GlobalVariables/ # Global formula manager
│ │ │ │ ├── AI/ # AI suggestion interface
│ │ │ │ └── Storyline/ # Timeline annotations
│ │ │ ├── layout/ # App shell (TopBar, Sidebar, Modals)
│ │ │ ├── onboarding/ # First-time user wizard
│ │ │ ├── common/ # Reusable UI components
│ │ │ └── ui/ # Radix UI primitives
│ │ ├── store/ # Zustand state management
│ │ │ ├── useStore.ts # Main store instance
│ │ │ ├── slices/ # Feature-specific state slices
│ │ │ └── selectors.ts # Memoized selectors
│ │ ├── services/api.ts # Axios HTTP client & API calls
│ │ ├── hooks/ # Custom React hooks
│ │ ├── types/ # TypeScript type definitions (100+ types)
│ │ ├── lib/
│ │ │ ├── utils.ts # Utility functions (cn, formatters)
│ │ │ ├── themes.ts # Plotly theme definitions
│ │ │ └── constants.ts # App-wide constants
│ │ ├── App.tsx # Root component
│ │ └── main.tsx # Entry point
│ ├── package.json
│ ├── vite.config.ts # Vite config (port: 3000, proxy → 8000)
│ ├── tsconfig.json
│ ├── tailwind.config.js
│ └── Dockerfile
│
├── backend/ # FastAPI / Python application
│ ├── app/
│ │ ├── api/ # REST API route handlers
│ │ │ ├── data.py # Upload, columns, preview, statistics
│ │ │ ├── visualizations.py # Plot data generation & validation
│ │ │ ├── reconciliation.py # Constraint optimization
│ │ │ ├── templates.py # Template CRUD
│ │ │ ├── export.py # HTML report generation
│ │ │ ├── models.py # ML model persistence
│ │ │ └── ai.py # AI suggestions & formula generation
│ │ ├── services/ # Business logic layer
│ │ │ ├── data_service.py # Dataset management & in-memory cache
│ │ │ ├── visualization_service.py # Plot orchestrator
│ │ │ ├── visualization/ # Plotting sub-services
│ │ │ │ ├── plotting.py # Plotly chart generation
│ │ │ │ ├── regression.py # ML model fitting pipeline
│ │ │ │ ├── processing.py # Data preprocessing
│ │ │ │ ├── fft.py # Frequency analysis
│ │ │ │ ├── root_cause.py # Causality analysis
│ │ │ │ ├── kpi.py # KPI / summary aggregation
│ │ │ │ └── validation.py # Config validation
│ │ │ ├── analytics/
│ │ │ │ └── causality.py # Granger causality & mutual info
│ │ │ ├── cleaning_service.py # Data cleaning pipeline
│ │ │ ├── reconciliation_service.py # OSQP solver integration
│ │ │ ├── export_service.py # Report generation orchestrator
│ │ │ ├── export_helpers/
│ │ │ │ ├── html_templates.py # Jinja2 HTML templates
│ │ │ │ ├── plotly_renderer.py # Kaleido PNG rendering
│ │ │ │ ├── statistics.py # Statistics summary generation
│ │ │ │ └── utils.py # Color contrast, base64 helpers
│ │ │ ├── ai_service.py # LangGraph AI orchestration
│ │ │ └── ai_graph/ # AI workflow components
│ │ │ ├── graph.py # Workflow graph definition
│ │ │ ├── providers.py # Multi-provider LLM factory
│ │ │ ├── formula_generator.py # Natural language → formula
│ │ │ ├── formula_validator.py # Formula code validation
│ │ │ ├── validators.py # Output schema validators
│ │ │ ├── tools.py # LLM tool definitions and bindings
│ │ │ ├── prompts/ # LLM prompt templates
│ │ │ └── schemas.py # Graph I/O Pydantic models
│ │ ├── models/schemas.py # All Pydantic schemas (50+ models)
│ │ ├── core/
│ │ │ ├── config.py # Pydantic Settings configuration
│ │ │ ├── profiler.py # ProcessTimeMiddleware
│ │ │ └── responses.py # NaNSafeJSONResponse
│ │ └── main.py # FastAPI application initialization
│ ├── data/
│ │ ├── templates/ # Persistent dashboard templates (JSON)
│ │ └── models/ # Saved regression models
│ ├── tests/ # Pytest suite (44 test files, 655 tests)
│ ├── run.py # Executable entry point
│ ├── requirements.txt # Python dependencies
│ ├── .env.example # Environment variable template
│ ├── build_linux.spec # PyInstaller config (Linux)
│ ├── build_windows.spec # PyInstaller config (Windows)
│ └── Dockerfile
│
├── docs/
│ └── images/ # Logo (SVG) and screenshots
├── scripts/
│ ├── build.py # Cross-platform build script
│ ├── build_app.sh # Linux/macOS build script
│ ├── build_app.ps1 # Windows PowerShell build script
│ ├── regenerate_icon.py # Icon (ICO) generation utility
│ ├── update_version.py # Version bump utility
│ └── run_tests.sh # Test runner script
├── start.sh # Development quick-start script
├── docker-compose.yaml # Docker Compose configuration
├── VERSION # Current version string
└── README.md # This file
🔌 API Endpoints Reference
All endpoints are prefixed with /api/v1/. Interactive documentation available at /docs (Swagger UI) and /redoc (ReDoc).
| Method | Endpoint | Description |
|---|---|---|
POST |
/data/upload |
Upload CSV/XLSX file with optional cleaning configuration |
GET |
/data/datasets |
List all loaded datasets with metadata |
GET |
/data/datasets/{id} |
Get dataset metadata (rows, columns, types, date range) |
DELETE |
/data/datasets/{id} |
Remove a dataset from memory |
GET |
/data/datasets/{id}/statistics |
Descriptive statistics (mean, std, quartiles) per column |
GET |
/data/datasets/{id}/preview |
Preview first N rows (default: 10) |
| Method | Endpoint | Description |
|---|---|---|
POST |
/visualizations/plot-data |
Generate Plotly-ready chart data from a VisualizationConfig |
POST |
/visualizations/predict |
Run regression prediction with a trained model |
POST |
/visualizations/validate-config |
Validate a visualization config without rendering |
GET |
/visualizations/types |
List available visualization types |
GET |
/visualizations/colors |
Get the 20-color palette used for series |
| Method | Endpoint | Description |
|---|---|---|
GET |
/templates/list |
List all server-side saved templates |
POST |
/templates/save-persistent |
Save template to server storage (JSON file) |
GET |
/templates/load-persistent/{name} |
Load a template from server by name |
DELETE |
/templates/delete/{name} |
Delete a saved template |
POST |
/templates/rename |
Rename an existing template |
POST |
/templates/save |
Prepare template for client-side download |
POST |
/templates/load |
Import template from uploaded JSON file |
POST |
/templates/validate |
Validate template structure and required variables |
| Method | Endpoint | Description |
|---|---|---|
POST |
/reconcile/reconcile |
Run constrained optimization reconciliation |
GET |
/reconcile/download/{filename} |
Download reconciled data as Excel file (.xlsx) |
| Method | Endpoint | Description |
|---|---|---|
POST |
/export/dashboard |
Generate self-contained HTML report with embedded charts |
POST |
/export/data |
Export selected column categories (original, reconciled, global variables, formula results) to an Excel file |
| Method | Endpoint | Description |
|---|---|---|
GET |
/ai/providers |
List available AI providers |
POST |
/ai/providers/{provider}/models |
Fetch available models from a provider's API using user's key |
POST |
/ai/suggest |
Get AI-powered visualization suggestions from natural language |
GET |
/ai/metrics |
Recent AI suggestion metrics (latency, tokens, cost) with aggregate statistics — no prompt text captured |
POST |
/ai/apply-suggestions |
Convert AI suggestions to VisualizationConfig objects |
POST |
/ai/generate-formula |
Generate Python formula code from natural language |
| Method | Endpoint | Description |
|---|---|---|
GET |
/models/list |
List saved regression models with metrics |
POST |
/models/save |
Train, evaluate, and persist a regression model |
DELETE |
/models/delete/{name} |
Delete a saved model |
| Method | Endpoint | Description |
|---|---|---|
GET |
/health |
Health check (status + version) |
GET |
/api/info |
API name, version, and documentation URLs |
Total: 34 endpoints across 8 route groups
🧪 Testing
49 test files | 987 passing tests
# Run from the repository root directory:
backend/venv/bin/python -m pytest --rootdir=backend -q| Test Module | Coverage Area |
|---|---|
test_data_api.py |
Data upload, listing, preview, statistics endpoints |
test_data_service.py |
In-memory dataset management, column type detection |
test_cleaning_service.py |
Header row, filters, NaN strategies, resampling pipeline |
test_visualizations_api.py |
Plot data API, config validation, color palette |
test_viz_validation.py |
VisualizationConfig validation rules |
test_viz_processing.py |
Data pre-processing for visualization |
test_visualization_validation.py |
Advanced validation edge cases |
test_visualization_processing.py |
Chart data processing pipeline |
test_plotting.py |
Plotly chart generation (all 11 visualization types) |
test_plotting_legacy.py |
Legacy chart generation compatibility |
test_regression.py |
All 6 regression models + custom formula fitting |
test_formula_rf.py |
Random Forest with formula-derived features |
test_fft.py |
Frequency analysis, windowing, frequency units |
test_kde.py |
Kernel density estimation overlay |
test_hist_bins.py |
Histogram bin calculation and edge cases |
test_causality.py |
Granger causality, Pearson, mutual info analysis |
test_root_cause.py |
Root cause analysis ranking and methods |
test_kpi_service.py |
KPI aggregation operations, formula metrics, error isolation, formatting |
test_reconciliation.py |
Constraint solving, OSQP, sigma weighting |
test_reconciliation_api.py |
Reconciliation API endpoints |
test_reconciliation_service.py |
Reconciliation service business logic |
test_templates_api.py |
Template CRUD, validation, required variables |
test_export_api.py |
HTML report generation API |
test_export_service.py |
Report generation service |
test_export_statistics.py |
Statistics table generation for reports |
test_export_stats.py |
Export stats formatting and edge cases |
test_export_utils.py |
Export helper utilities |
test_html_templates.py |
Jinja2 HTML template rendering |
test_plotly_renderer.py |
Kaleido PNG rendering |
test_models_api.py |
Regression model persistence API |
test_ai_api.py |
AI suggestion and formula generation API |
test_ai_fetch_models.py |
Dynamic model fetching from provider APIs |
test_ai_service.py |
AI orchestration service |
test_ai_graph.py |
LangGraph workflow integration |
test_ai_graph_providers.py |
Provider factory, reasoning-effort/thinking-budget mapping, timeouts |
test_ai_graph_prompts.py |
LLM prompt templates |
test_ai_graph_validators.py |
AI output validators and correction logic |
test_ai_graph_schemas.py |
AI workflow Pydantic schemas |
test_ai_graph_tools.py |
AI graph tool definitions and bindings |
test_ai_profile.py |
Dataset profile: roles, null/cardinality/skew, datetime candidates, correlations, prompt rendering |
test_ai_metrics.py |
AI metrics collection, aggregation, and cost computation |
test_logging_filters.py |
Logging filters and redaction |
test_formula_generator.py |
Natural language → formula generation |
test_formula_validator.py |
Formula code safety validation |
test_main.py |
FastAPI app startup, health check, middleware |
test_config.py |
Settings loading and environment variables |
test_responses.py |
NaNSafeJSONResponse serialization |
test_schemas.py |
Pydantic schema validation |
test_profiler.py |
ProcessTimeMiddleware |
62 test files | 824 passing tests
# Run from the repository root directory:
npx --prefix frontend vitest run --reporter=verbose --root frontend| Test Environment | Configuration |
|---|---|
| Test runner | Vitest 4.0.16 |
| DOM environment | jsdom 27.3.0 |
| Component testing | @testing-library/react 16.3.1 |
| User events | @testing-library/user-event 14.6.1 |
| DOM matchers | @testing-library/jest-dom 6.9.1 |
| Setup file | frontend/src/test/setup.ts |
Coverage areas (selected):
| Area | Test Files |
|---|---|
| Store slices | dataSlice, plotSlice, uiSlice, storylineSlice, workspaceSlice |
| Store integration | useStore, selectors |
| API service | api.test.ts |
| Layout components | TopBar, Sidebar, FloatingControls, WorkspaceTabs, NotificationCenter |
| AI components | AISuggestionsPanel, AIWizardModal, AISettingsModal, AIProviderIcons, ColumnDescriptionEditor |
| Visualization | InteractivePlot, VisualizationCard, ConfigurationPanel, FormulaEditorModal, RootCauseAnalysis |
| Config sections | AxisSettings, RegressionSettings, FFTSettings, FormulaSettings, SeriesList, GeneralSettings, RootCauseSettings |
| Features | DashboardGrid, FileUpload, DataCleaningModal, ReconciliationModal, GlobalVariablesModal, TemplateManager, StorylineModal |
| Common | Button, ErrorBoundary, DateRangePicker, ConfirmationModal, CustomColorPicker, SimpleTooltip, Logo |
| Hooks | useDebounce, useThemeEffect, useSidebarResize |
🔨 Building from Source
FlowMeter can be compiled into a single standalone executable using PyInstaller. Three build options:
.\scripts\build_app.ps1The PowerShell script handles the full build pipeline automatically:
- Installs frontend dependencies and builds with Vite (
npm install && npm run build) - Converts the SVG logo to a multi-size ICO file via a sharp → Pillow pipeline
- Creates a Python virtual environment and installs backend dependencies
- Runs PyInstaller with
backend/build_windows.spec - Output:
backend/dist/FlowMeter.exe(with custom icon)
chmod +x scripts/build_app.sh
./scripts/build_app.sh- Runs
npm install && npm run buildfor the frontend - Creates a Python venv and installs requirements
- Runs PyInstaller with
backend/build_linux.spec - Output:
backend/dist/FlowMeter
python scripts/build.pyAuto-detects the OS and uses the appropriate spec file. Handles npm command differences between Windows (npm.cmd) and Unix. Cleans previous builds before starting.
Both .spec files bundle:
- Frontend:
frontend/dist/→ mapped asstatic/inside the executable - Logo assets:
logo_white_colored.svg,logo_black_colored.svg - Hidden imports: uvicorn, pandas, numpy, sklearn, scipy, sympy, osqp, openpyxl, xlrd, matplotlib, kaleido, jinja2
- Custom hooks:
backend/hooks/hook-osqp.pyfor OSQP binary bundling - Compression: UPX enabled for smaller binary size
- Mode: Console application (terminal shows server logs)
python scripts/regenerate_icon.py
# Regenerates icon.ico from SVG logo with sizes: 16, 32, 48, 64, 128, 256 px| Issue | Cause | Solution |
|---|---|---|
| App won't start | Port 8000 already in use |
Kill the conflicting process or set PORT=8001 environment variable |
| File upload rejected | File too large or wrong type | Only .xlsx, .xls, .csv accepted. Max 50 MB (configurable via MAX_FILE_SIZE_MB) |
| Max datasets reached | More than 10 datasets loaded | Delete unused datasets — limit is 10 per session (MAX_DATASETS_PER_SESSION) |
| Frontend can't connect | Backend not running or CORS blocked | Backend must be on port 8000. Allowed origins: localhost:3000 and localhost:5173 |
| Formula result is NaN | Column name typo or NaN propagation | Column names are case-sensitive. Use the column browser. Check for NaN in input columns. |
| Formula errors | Syntax issue or unavailable function | Namespace: col (DataFrame), np (NumPy), pd (Pandas). No imports allowed. |
| Reconciliation fails | OSQP solver infeasibility | Check constraints are linearly independent. Variable names must match column names exactly. |
| Reconciliation diverges | Redundant or contradictory constraints | Remove redundant equations that are linear combinations of others. |
| AI provider not responding | Network issue or invalid API key | 401 = bad key, 429 = rate limited, timeout = firewall/proxy issue. Try another provider. |
| AI suggestions fail | Key lacks access to selected model | Verify key permissions. Try a different model or provider. |
| Slow with large datasets | Too many points rendered | LTTB auto-downsamples to 500 points for display. Use resampling to reduce source data. |
| Export hangs | Kaleido rendering many charts | Charts rendered in ThreadPoolExecutor. Reduce charts per report if timeout occurs. |
| Kaleido crash on first export | Renderer startup failure | App prewarms Kaleido on startup. Restart the app and ensure sufficient RAM. |
| Template incompatible | Dataset missing required columns | Templates validate required variables on load. Check required_variables vs your dataset. |
| Generic Windows icon | Windows icon cache stale | Delete %LOCALAPPDATA%\IconCache.db and restart Windows Explorer. |
Tip
All settings can be overridden via environment variables or a .env file in the backend/ directory. A .env.example file is provided as a starting point. See the Configuration Reference section for all configurable values.
Contributions are welcome! To get started:
Development setup checklist:
| Tool | Minimum Version | Purpose |
|---|---|---|
| Python | 3.10+ | Backend runtime |
| Node.js | 18+ | Frontend toolchain |
| npm | 9+ | Package management |
| Git | any | Version control |
Steps:
- Fork the repository on GitHub
- Clone your fork:
git clone <your-fork-url> - Set up the backend: create a venv, activate it,
pip install -r backend/requirements.txt - Set up the frontend:
npm installin thefrontend/directory - Run tests to confirm a clean baseline:
# Backend backend/venv/bin/python -m pytest --rootdir=backend -q # Frontend npx --prefix frontend vitest run --root frontend
- Create a feature branch:
git checkout -b feature/my-improvement - Make your changes and add tests for new functionality
- Submit a Pull Request
Pull Request checklist:
- All backend tests pass:
backend/venv/bin/python -m pytest --rootdir=backend -q - All frontend tests pass:
npx --prefix frontend vitest run --root frontend - No ESLint errors:
npm run lint(fromfrontend/) - New functionality includes tests
- README updated if a new feature or configuration option is added
Code standards:
| Area | Configuration |
|---|---|
| TypeScript | Strict mode (strict: true, noUnusedLocals, noUnusedParameters, noFallthroughCasesInSwitch) |
| Target | ES2020 with bundler module resolution |
| Linting | ESLint with ts,tsx extensions — npm run lint |
| Styling | Tailwind CSS with PostCSS; custom theme in tailwind.config.js |
| Path aliases | @/* → src/* (via tsconfig.json paths) |
| API docs | TypeDoc — npm run docs generates HTML documentation |
| Backend style | Standard Python formatting; docstrings follow the project convention |
This project is licensed under the MIT License — see the LICENSE file for details.











