EEOC Explore

EEOC Explore, Developer & Editor Guide

Welcome. This guide is everything you need to view, edit, build, and maintain the dashboards in EEOC Explore, the platform that replaced our old Tableau workbooks with modern, interactive web dashboards.

Who this guide is for

This guide is written for two kinds of readers at once, and it works for both:

You do not need to be a programmer to do most of what's in this guide. Refreshing data, renaming a chart, building a new dashboard, and running an accessibility check are all point-and-click. The deeper coding sections are clearly marked "advanced", read them when you're curious, skip them when you're not.

How to use this guide

A few things are true on every page, so learn them once:

If you just want to get a task done right now, skip ahead to Step-by-step SOPs (quick recipes). If a word ever stumps you, the Glossary & troubleshooting at the very end has you covered.

Table of contents

  1. Glossary & troubleshooting (start here if you're new)
  2. How this platform works (orientation for a Tableau developer)
  3. The edit and save workflow (and why view-only is the default)
  4. Enhancing an existing dashboard
  5. Editing the code, Plotly, Dash, and the full toolbox
  6. Creating a brand-new dashboard
  7. Bringing in data, new sources and new fiscal years
  8. Governed data and the open-data layer
  9. The 508 / accessibility agent
  10. Managing accounts & roles (admin)
  11. Step-by-step SOPs (quick recipes)

New here? The first section below is your friend, it defines every term and fixes the most common snags before they happen. Experienced developers can skip to How this platform works.


Glossary & troubleshooting (start here if you're new)

New to this tool? Start here. This page explains the words you'll see and fixes the most common snags. You don't need to be technical. The live platform is one hosted site that everyone opens in a browser, and you can also run the same tool locally on a Mac for development.

Plain-English glossary

Definitions are in everyday language, in the order they tend to come up.

Troubleshooting

Each problem below lists the most likely cause and the fix. Try these in order.


Now that you know the vocabulary, here's the big picture of how the whole platform fits together.

How this platform works (orientation for a Tableau developer)

If you have built dashboards in Tableau, you already understand most of the ideas here, only the file names and the tools are different. This section maps what you know onto what this platform does, so you can find your footing fast. If you have never touched Tableau, read it anyway: it explains, in plain words, what the moving parts are and where the data lives.

The big picture: what "the Studio" is

The whole platform is a single program called the Studio. In technical terms it is a Flask/Dash web app (a web server that serves the dashboard pages to your browser). It is hosted at one address, https://eeoc-dashboards-1-uecu.onrender.com, and you open it in Chrome or Edge like any website.

There is one site for everyone. It is view-only for ordinary users: anyone can open and explore the dashboards, but editing and the builder are limited to editor accounts. An editor signs in, makes a change, confirms it looks right, and saves. Every save backs up the old version first, so a working dashboard is hard to break.

Where a dashboard lives: one folder per dashboard

In Tableau, a workbook is one .twbx file that secretly bundles the layout and the data together. Here, each dashboard is instead a folder on disk, under a directory called studio_conversions/. For example, the EEO-4 dashboard lives in studio_conversions/eeo-4/. Inside that folder are a handful of plain files, each with one job:

File in the dashboard folder What it is, in plain English
spec.json The dashboard definition, the recipe for every chart, filter, tab, and parameter. This is the closest thing to a Tableau workbook.
data.db The data itself, stored as a SQLite database (a single-file database). This is the equivalent of a Tableau extract. For EEO-4 it holds one table, puf_eeo4.
schema.yaml A machine-readable data dictionary, what each table and column means and how to count it correctly.
context.md A plain-language reporting guide with the official rules (e.g. for EEO-4, "always filter type = 'Counts'; never sum median salaries").
charts.json (optional) Present only for dashboards built in the newer Explorer tool instead of from a Tableau spec.
custom.css, custom.js, custom.html (optional) Optional extra styling, behavior, or text that an editor adds on the Edit page to fine-tune one dashboard's look.

schema.yaml and context.md together are what power the AI search box, the "Ask this dashboard a question" bar that appears above every dashboard. When someone types a question, the Studio hands the AI those two files so it can write a correct database query against data.db. That is why the rules in context.md are written so carefully.

How a dashboard gets drawn on screen

You do not need to read code to use the platform, but it helps to know that nothing is pre-rendered. There is a single renderer (the file runtime.py) that builds every chart live, on demand:

  1. When you open a dashboard, the Studio loads its data.db tables into memory (load_tables reads every table out of the SQLite file).
  2. For each chart on the page, the renderer reads that chart's definition from spec.json, computes only the calculations that chart actually needs, applies the current filters and parameters, and produces a Plotly figure, a table, or a single big-number tile (build_figure / chart_layer).
  3. When you change a filter or a parameter, the affected charts are recomputed and redrawn from the same spec.json + data.db.

So spec.json is the what to draw and data.db is the what to draw it from, change either one and the page changes the next time it's rendered. There is no separate "publish to a server" step for the chart images themselves.

Tableau → here: the translation table

Here is the direct mapping from Tableau concepts to where they live in this platform. Everything on the right is inside that dashboard's spec.json unless noted otherwise.

Tableau concept Here Where it lives
Workbook (.twbx) The whole dashboard definition spec.json
Worksheet / sheet A single chart, table, or KPI tile a worksheets entry in spec.json
Data Source / extract The data data.db (a SQLite database)
Calculated Field A computed field a calc_registry entry in spec.json
Dashboard tab One tab/canvas with its laid-out tiles a dashboards entry, with its zones list, in spec.json
Parameter A user-adjustable value a parameters entry in spec.json
Filter card A filter control on the canvas a zone of kind filter inside a dashboard's zones

What that looks like in a real spec.json (EEO-4)

To make this concrete, here is the actual shape of studio_conversions/eeo-4/spec.json. Its top level is a set of labeled sections:

The practical takeaway: when you want to understand or change how a chart behaves, you look in spec.json; when you want to change the numbers, you refresh data.db. You do that work on the site as an editor, and every save backs up the old file first.


You just saw why the site is view-only by default. Here's exactly how editing works and how a change goes live.

The edit and save workflow (and why view-only is the default)

The EEOC Explore platform runs as one site at https://eeoc-dashboards-1-uecu.onrender.com. It is view-only for ordinary users so the dashboards your coworkers rely on can't be changed by accident. Editing and the builder are limited to editor accounts. This section explains how the site decides who can edit, what a save does, and how a change goes live.

Quick vocabulary, defined once:

View-only by default

The site is view-only for ordinary users: anyone can open and explore the dashboards, but the Edit and builder controls only appear for editor accounts. This is intentional, so the live dashboards can't be changed by accident.

Behind the scenes, a single rule in the app's code (a function named editing_enabled() in twbx_convert/simple_auth.py) decides whether editing is allowed at all, and a companion rule, is_editor(), decides whether you specifically may edit. If you sign in with an ordinary account, the Edit and builder controls are hidden and the Save button tells you to ask an admin for edit access. If you sign in as an editor, those controls are available.

What a save does

When an editor saves a change, the tool writes the edited dashboard definition and custom code (such as spec.json, charts.json, context.md, schema.yaml, and any custom.css / custom.js / custom.html). Two important safety facts:

A brand-new dashboard that an editor builds is added to the site automatically. Because every save validates that the dashboard still renders and backs up the old file first, a bad edit can't silently break a working dashboard.

One-line summary: Sign in as an editor, make the change, confirm it renders, and save. The site is view-only for everyone else by design, and your data is never overwritten by an edit.


With the workflow clear, here's the most common everyday task: changing a dashboard that already exists.

Enhancing an existing dashboard

Once a dashboard already exists in the Studio, you don't have to start over to change it. The Edit / Enhance page lets you tweak how a dashboard looks and behaves, either by describing the change in plain English and letting the AI do it, or by editing the underlying files yourself. Every change is checked before it's kept, and your previous version is always backed up, so it's hard to break anything permanently.

Important: Editing is only available in the site as an editor. The site (https://eeoc-dashboards-1-uecu.onrender.com) is view-only, there is no Edit button there.

Opening the Edit / Enhance page

  1. Go to the site as an editor in your browser: https://eeoc-dashboards-1-uecu.onrender.com.
  2. Find the dashboard you want to change in the list.
  3. Click the ✎ Edit / Enhance button on that dashboard.

This opens the editor at a web address like /edit/<id> (where <id> is the dashboard's internal name). The screen is split into two parts:

At the top of the page, a dark blue header reads ✎ Edit & Enhance with the dashboard's name next to it. On the right side of the header are two links: ↗ open dashboard (opens the live dashboard in a new browser tab) and ← all dashboards (returns to the main list).

Choosing which file to edit

Under the Files heading in the left sidebar there's a dropdown menu. Each option is one file that makes up the dashboard. Pick one and its contents load into the big code area on the right, ready to edit. The choices are:

When the page first opens, spec.json is loaded by default. Switching the dropdown to a different file replaces what's in the editor with that file's contents.

Asking the AI to make a change

At the top of the left sidebar is the Describe a change box. This is where you type what you want in everyday English, then click ✨ Ask AI to edit. What happens next depends on which file is currently selected in the dropdown:

Steps to use it:

  1. Pick the file you want to affect from the Files dropdown.
  2. Type your request in the Describe a change box.
  3. Click ✨ Ask AI to edit.
  4. Wait while the status shows Thinking….
  5. Review the result. The AI does not save on its own, it fills the code area on the right with its proposed result for you to look over. For a spec.json change, it also lists the specific edits it made (for example, "• set /worksheets/.../mark = …"). For other files, it shows the whole proposed file.
  6. If it looks right, save it (see below). If not, edit it by hand or rephrase your request and try again.

A few things to know:

Saving, validating, and previewing

Beneath the code area on the right are three buttons:

What happens on Save depends on the file:

Because every Save validates the dashboard still renders and backs up the old file first, a bad edit can't silently break a working dashboard.

Opening the dashboard's folder

If you'd rather work with the files directly on your computer, click the 📂 Open folder (Finder) button in the left sidebar. This opens the folder on your Mac (in Finder) that holds all of this dashboard's files, spec.json, context.md, the custom.* files, the data file, and any backups that have been made. The folder's full path is also shown just above the button, next to the word Folder:.


The Edit / Enhance page handles most changes through plain English and simple file edits. When you need precise control, a chart type the menus don't offer, custom Plotly, or hand-tuned styling, the next section goes all the way down to the code.

Editing the code, Plotly, Dash, and the full toolbox

This is the deep end. Everything you've seen in the rest of this guide is generated from two things on disk: a file called spec.json (the blueprint for a dashboard) and a SQLite database called data.db (the numbers). A Python file, runtime.py, reads the blueprint and turns each piece into a live chart. Once you understand how those fit together, you can change almost anything, a chart's type, its colors, its title, its axis labels, by editing text, and you can drop into a live code panel and write real Plotly when the blueprint isn't enough.

Two important ground rules before you touch anything:

How a dashboard is assembled: the spec.json blueprint

Each converted dashboard lives in its own folder (for example studio_conversions/litigation-dashboard-25/). The blueprint is the spec.json file in that folder. Open it from the sandbox: go to the Edit & Enhance page for the dashboard, and in the Files dropdown choose spec.json, dashboard definition. The file's contents load into the big text box on the right.

spec.json is a single JSON object with these top-level parts:

There are a few more helper sections, formats (number/date formatting), groups (categorical bins), aliases (renaming raw codes like 1/2 to Male/Female), table_calcs, and filter_titles (the labels on filter cards). You can usually leave these alone.

The anatomy of one worksheet

This is the part worth memorizing, because it's where you'll make most edits. Here's a real worksheet from the Litigation dashboard (trimmed), with what each field controls:

"Chart": {
  "datasource": "federated.08ep8ih0ifr4jv193l43408pz09a",
  "sqlite_table": "litigation",
  "mark": "Bar",
  "cols": [ {"field": "Calculation_1706582838769684480", "agg": "none", "label": "Year 2"} ],
  "rows": [ {"field": "Calculation_409546106217156612", "agg": "sum", "label": "Bar value"} ],
  "color": null,
  "title": "<...> <[...].[Category (group)]>",
  "palette": [],
  "axis_titles": { "Calculation_409546105890918402": "Monetary Benefits " },
  "labels": { "Calculation_409546106217156612": "Bar value", "Year": "Year", ... }
}

Recipe: change a chart type, colors, titles, or axis labels

All four are edits to a worksheet in spec.json, done on the Edit & Enhance page in the sandbox.

  1. Open the dashboard's Edit page, pick spec.json from the Files dropdown.
  2. Find the worksheet you want under "worksheets". (The name is the key, e.g. "Chart", "Resolution List", "Notes".)
  3. Make one of these changes: - Chart type: change "mark": "Bar" to "mark": "Line" (or "Area", "Pie", etc.). - Colors: set "palette" to your own list, e.g. "palette": ["#1b3a5b", "#b8860b", "#2a7a55"]. The first color is used for the first series, and so on. (For a specific category needing a specific color, say "Male" always navy, that lives in the worksheet's color_map; edit the hex value there.) - Title: replace the "title" value with plain text, e.g. "title": "Lawsuits Filed by Fiscal Year". - Axis label: in "axis_titles", set the field's value, e.g. "Calculation_409546105890918402": "Monetary Benefits ($M)". Use "" to hide a label.
  4. Click ✓ Validate only first, this checks the JSON is still valid and the dashboard still renders, without saving. If it's green, click 💾 Save. The old file is backed up automatically.
  5. Click ↗ open dashboard (top of the Edit page) to see the result.

A note on what's possible: the renderer only knows the chart types listed in its MARKS set, Bar, Line, Area, Pie, Circle, Square, Shape, Filled Map, Text, Gantt, Automatic. Setting mark to something outside that list won't draw a new kind of chart. When you need a chart shape the blueprint can't express, that's what the live code panel (next-but-one section) is for.

How runtime.py turns a worksheet into a Plotly figure

You don't need to edit runtime.py, it's the shared engine, but knowing what it does demystifies why your spec.json edits have the effects they do. When a tile renders, runtime.py's build_figure function:

  1. Loads only the data and calculated fields that worksheet actually uses (for speed), pulling the right table out of data.db.
  2. Applies the active filters, both the worksheet's own pinned filters and whatever the user picked in the dashboard's filter dropdowns.
  3. Decides what kind of result to return based on the shelves: a chart, a table, a single big-number (kpi), a map, or a text note. For example, a worksheet with one category on cols and one summed measure on rows becomes a bar chart; one with no axes and a single number becomes a KPI tile.
  4. Builds the actual Plotly figure using Plotly Express, px.bar, px.line, px.area, px.pie, px.choropleth for maps, feeding in your palette, labels, axis_titles, and number formats. So when you change mark from "Bar" to "Line", you're literally telling runtime.py to call px.line instead of px.bar on the same data.

The practical upshot: spec.json is the instructions, runtime.py is the worker that follows them. Most of what you'd want to tweak is an instruction, which is why most edits are in spec.json.

Writing and running real Plotly safely: the Explorer's "Open code" panel

When the blueprint isn't enough, you want a bubble chart, a custom annotation, a funnel, a calculation Plotly Express doesn't do out of the box, use the Chart Explorer. It's a separate point-and-click tool (reachable from the Studio) where every chart you build is backed by real, editable Plotly/Python code that you can run on the spot.

How it works:

  1. In the Explorer, pick a Data source (or upload a file).
  2. Build a starter chart with the controls on the left: choose a Chart type, then drag columns onto X / dimension, Measures (Y), Color / split, and set Aggregate. The chart re-renders instantly on every change. You can also Slice & dice (filter to specific values, Sort, keep Top N) and pick a Theme.
  3. Find the ◇ Open code (editable Plotly/Python) panel below the chart. It shows the exact Plotly code that produced what you see, and it's editable.
  4. Edit the code freely, then click ▶ Run. Your figure replaces the chart. If something's wrong, the error appears in red right above the panel.

You write against a small, fixed set of names that are already provided for you: df (your data, as a pandas DataFrame), pd (pandas), np (numpy), px (Plotly Express), and go (Plotly Graph Objects). Your code must assign the finished chart to a variable named fig, that's the one rule. For example:

# A clean horizontal bar of the top 10 categories
d = df.groupby("Category", as_index=False)["Count"].sum()
d = d.sort_values("Count", ascending=True).tail(10)
fig = px.bar(d, x="Count", y="Category", orientation="h",
             color_discrete_sequence=["#1b3a5b"])
fig.update_layout(title="Top 10 by Count", template="plotly_white")

Or with Graph Objects for a big-number tile:

total = df["Count"].sum()
fig = go.Figure(go.Indicator(mode="number", value=float(total)))
fig.update_layout(title="Total charges")

Why "safely": this code runs locally on your own machine in a restricted sandbox. Imports and any file or system access are deliberately blocked, if your code contains import, open, os, eval, and the like, it's refused with a message reminding you that df, pd, np, px, go are already there. So you can experiment without fear of touching anything outside the chart. (There's also a ✨ Build button: describe the chart in plain English, "charges by state as a map", and the AI writes the Plotly code into the panel for you to Run and tweak. And 💡 Insights writes a few plain-English findings about the data.)

When you're happy with a chart, ➕ Add to dashboard, give the dashboard a name, and 💾 Save dashboard, it lands in the Studio as a new code-based dashboard.

A glance at calc_registry (calculated fields)

The calc_registry holds Tableau's calculated fields, translated into tiny Python expressions that runtime.py evaluates row by row. Each entry has a py expression and an optional aggs list (for "level of detail" totals). Two real examples:

"Allegation_2 - Split 1": { "py": "_trim(_split(r.get('Allegations.x'), \"/\", 1))", "aggs": null }
"Calculation_1135295133179905": {
  "py": "r.get('__agg1')",
  "aggs": [ {"name": "__agg1", "func": "sum", "inner": "r.get('Population')", "dims": ["distoff_final"]} ]
}

Here r.get('Allegations.x') reads a column from the row; _trim, _split, and friends are built-in helpers (string trimming, splitting on a delimiter, date math, etc.). The second example computes a per-office population total (sum of Population grouped by distoff_final) and reads it back. You'll rarely need to edit these by hand, and when you do, change them carefully and lean on ✓ Validate only before saving, because a broken expression empties the tile. For most number changes, the underlying data (data.db) or the worksheet's agg is the better lever.

Customizing look and behavior: custom.css, custom.js, custom.html

Beyond the charts themselves, each dashboard can carry three optional front-end files that the Studio injects into the dashboard page every time someone views /dash/<dashboard>. You author all three from the same Edit & Enhance page: pick the file from the Files dropdown, edit it in the big text box, ✓ Validate only, then 💾 Save. They are:

Three copy-paste starters:

custom.css (a navy banner with white text, and a touch more breathing room around tiles):

#__custom_html {
  background: #1b3a5b;
  color: #fff;
  padding: 12px 18px;
  font: 600 16px/1.3 -apple-system, "Segoe UI", Roboto, sans-serif;
}
.dash-graph { padding: 4px; }

custom.html (the banner content that the CSS above styles):

<div>EEOC Litigation Statistics, FY2025 (public data)</div>

custom.js, using the bundled jQuery ($) to add a "Print this page" button to the banner:

$(function () {
  $('#__custom_html').append(
    '<button id="printBtn" style="margin-left:14px">Print this page</button>'
  );
  $(document).on('click', '#printBtn', function () { window.print(); });
});

Note the #__custom_html selector in all three: that's the exact id of the container the platform creates for your custom HTML banner, so CSS and JS can reliably target it. Save, then open the dashboard to see all three working together. If your custom.js ever throws an error, it's logged to the browser's developer console (it won't crash the dashboard), which is the first place to look when a script doesn't behave.


The code panel above is also how you build a dashboard from scratch. The next section covers both no-code ways to create one.

Creating a brand-new dashboard

The Studio gives you two different ways to build a dashboard from scratch. Pick the one that matches how you like to work:

Both let anyone upload data and experiment. But saving (publishing) a dashboard only works for editor accounts, and only in the site as an editor at https://eeoc-dashboards-1-uecu.onrender.com. On the site everything is view-only, you can build and preview, but the Save button will tell you to ask an admin for edit access. So if your goal is to publish a new dashboard, do this work in the site as an editor.

Jargon, once: A column is one field in your data (e.g. "State", "Charges"). A measure is a number column you can add up or average (e.g. "Charges", "Count"). A dimension is a category or date column you group by (e.g. "State", "Year"). An aggregate is how numbers get combined when you group, sum, average, count, and so on.

Path 1, The click-to-build Explorer (/explore/)

The Explorer screen has a dark blue bar across the top reading Chart Explorer, a narrow control panel down the left side, and a large chart area in the center.

1. Choose or upload your data. - At the top of the left panel, under Data source, open the Pick a dataset… dropdown to use data that's already in the Studio, or - Click the dashed ⬆ or upload a file box just below it to upload your own. Accepted file types are CSV, TSV, TXT, Excel (.xlsx/.xlsm/.xls), SAS (.sas7bdat), Parquet, and JSON. - Once it loads, a green line appears (for example ✓ mydata: 1,234 rows × 8 cols) confirming the data is ready. A starter chart appears automatically using the first category and first number it finds.

2. Build a chart by choosing a type and columns. In the Chart area of the left panel: - Chart type, pick from Bar, Horizontal bar, Line, Area, Pie / donut, Scatter, Bubble, Histogram, Box plot, Violin, Heatmap, Treemap, Sunburst, Funnel, US state map, Big number (KPI), or Table. - X / dimension, the category or date that runs along the bottom (or the grouping for a pie/map). - Measures (Y), the number(s) to chart. You can pick more than one. - Color / split, optional; splits the chart by a second category. - Aggregate, how the numbers combine: sum, mean, median, min, max, count, or none.

The chart in the center redraws instantly every time you change one of these. If a chart needs a field you haven't filled in, a red note appears below the chart telling you what to pick.

3. Slice and dice. Under Slice & dice you can narrow and reshape the data: - Filter column + Keep values, choose a column, then tick the specific values to keep (leave it as "all" to keep everything). - Sort, No sort, High → low, or Low → high. - Top N, type a number to keep only the top few (e.g. 10 for the top ten). - Theme, restyle the look (plotly_white, plotly_dark, presentation, and others). - Animate by (play ▶), optional; pick a column to turn the chart into an animated "play" through that column's values.

4. Let the AI build a chart for you (optional). Under AI: - Type a plain-English description in the box, for example "charges by state as a map". - Click the purple ✨ Build button. The AI writes the chart and draws it. A short status line confirms ✓ Built, tweak the code or controls. - The 💡 Insights button next to it is different, instead of a chart, it writes 3 to 5 plain-English bullet points about notable totals and trends in your data. - Note: the AI features need a local AI model running. If none is available you'll see a message like "No AI model, start Ollama or set OPENAI_API_KEY."

5. Fine-tune the code (optional, advanced). Below the chart is ◇ Open code (editable Plotly/Python) with the actual chart code in a text box. You can edit it and click the green ▶ Run button to redraw. You don't need to touch this, the controls above do the same thing, but it's there if you want exact control.

6. Add the chart to your dashboard. When a chart looks right, click the blue ➕ Add to dashboard button (next to ▶ Run). It gets added to the list at the bottom under 📊 Dashboard, numbered (e.g. "1. Bar"). Repeat steps 2 to 6 to add as many charts as you want. To remove one, click the small next to it in the list.

7. Name and save (publish) the dashboard. At the bottom, in the 📊 Dashboard strip: - Type a name in the Dashboard name box (it starts as "My Dashboard"). - Click the gold 💾 Save dashboard button. - On success you'll see ✓ Saved N chart(s). with an Open ↗ link to view it and a Publish/Share link.

Remember: Save only works for editor accounts in the site as an editor. On the view-only site, the button instead tells you that publishing is limited to editor accounts (you can still download any single chart using the camera icon on the chart itself).

Path 2, The Builder (/build/)

The Builder screen has a blue Dashboard Builder header, a left control column with three numbered steps, and a live preview area on the right.

1. Name it and upload a data file. Under 1. Name & data source: - Type a name in the Dashboard name box (starts as "My Dashboard"). - Drag a file onto the dashed ⬆ Drag/drop or select a data file box, or click it to browse. Same file types as the Explorer: CSV, Excel (.xlsx/.xls), SAS (.sas7bdat), TXT/TSV, Parquet, JSON. - When it loads you'll see a green confirmation like ✓ myfile.csv: 1,234 rows, 8 columns loaded. followed by a short summary listing which of your columns are Numbers (measures) and which are Categories / text (dims). A starter recipe is also dropped into the spec box (step 3) so you have something to build even without the AI.

2. Tell the AI what you want (it plans the charts). Under 2. Tell the AI what you want to do: - Type your goal in plain English, e.g. "show total charges, charges by state on a map, and the trend by year", or leave it blank for a general overview. - Click the purple ✨ Get a plan button. - The AI responds with two things: a "What to do to your data first" note (specific prep tips for your columns, e.g. a number stored as text, or a total row that would double-count), and a drafted dashboard plan. A status line confirms something like ✓ Drafted 3 chart(s). Review the YAML, then Build & preview. - Note: this needs a local AI model. If none is available, you'll be told you can still write the plan by hand.

Jargon, once: The plan is written in YAML, a simple, indented text format that's easy to read and edit by hand. Each chart is a few lines listing its name, kind (bar, line, kpi, map, etc.), which column to group by, and which number to measure.

3. Review and edit the plan. Under 3. Dashboard spec (YAML), edit freely, the drafted recipe appears in an editable text box. You can change titles, swap chart types, add or remove charts, or fix column names directly here. If anything is wrong (a misspelled column, a missing field), a red error line appears under the box in plain English so you can correct it.

4. Build and preview. Click the dark blue ▶ Build & preview button. The right side of the screen renders the actual dashboard exactly as it will ship. If a chart can't be built, you'll see a message telling you to fix the YAML and build again.

5. Save to Studio (publish). When the preview looks right, click the gold 💾 Save to Studio button. On success you'll see ✓ Saved N chart(s) along with an automatic accessibility (508/WCAG) check result. The new dashboard is live for viewers once saved.

As with the Explorer, Save to Studio only works for editor accounts. If you signed in with an ordinary account, the button will tell you that publishing is limited to editor accounts and to ask an admin for edit access. You can still upload, build, and preview freely.


Whether you're refreshing an existing dashboard or building a new one, it all starts with data. The next section covers every way data comes into the platform.

Bringing in data, new sources and new fiscal years

The platform reads more than one kind of data file. Whenever it asks you for a "data file," you can hand it any of these formats:

There are two separate ways data comes in, and they serve different purposes. The first, uploading your own file to explore or build, is open to any signed-in editor. The second, refreshing a published dashboard with a new year of data, is reserved for editors (accounts with edit access). Both are covered below.

A note on access. Editing and uploading are limited to editor accounts. The site is view-only for ordinary users, so the upload buttons do not appear for them. If you don't see the controls described below, you are probably signed in with a view-only account; ask an admin for edit access.

Upload your own data to explore or build (any signed-in user)

The Dashboard Builder lets you bring in a data file of your own, get AI help turning it into charts, and preview the result, all without touching anyone else's published dashboards. This is the safe sandbox for "I have a spreadsheet, what can I do with it?"

  1. Open the Dashboard Builder (on the site). You'll see a dark blue header reading Dashboard Builder and, on the left, a numbered set of steps.
  2. Under 1. Name & data source, type a name in the Dashboard name box (it starts filled in as "My Dashboard").
  3. Drag your file onto the dashed upload box, labeled Drag/drop or select a data file, or click it to browse. The small print under it lists the accepted types: CSV, Excel (.xlsx/.xls), SAS (.sas7bdat), TXT/TSV, Parquet, JSON.
  4. When the file loads, a green confirmation appears, for example "✓ yourfile.csv: 12,000 rows, 9 columns loaded." Just below it, a gray summary box splits your columns into Numbers (measures) and Categories / text (dims) so you can see how the tool read each one.
  5. Under 2. Tell the AI what you want to do, type a plain-English goal (for example, "show total charges, charges by state on a map, and the trend by year"), then click ✨ Get a plan. The AI returns two things: prep advice for your columns, and a draft Dashboard spec (YAML) in the box under step 3. (You can also skip the AI and write the YAML yourself; leaving the goal blank gives you an overview.)
  6. Click ▶ Build & preview to render the charts on the right using the real engine, what you preview is exactly what would ship.

About saving: the 💾 Save to Studio button publishes your work into the shared catalog. On a properly configured system this is limited to Editor accounts. If you don't have edit access, clicking Save shows a message explaining that "Saving to the shared Studio is limited to editor accounts", but you can still upload, build, preview, and download charts from the Explore tool freely. Your uploaded data lives only in your own working area; it does not change any published dashboard.

Refresh a published dashboard for a new fiscal year (Editors only)

When a new fiscal year of EEOC data is published, an Editor can swap the fresh numbers into an existing dashboard in place, the charts, layout, and labels stay exactly as they are; only the underlying data is replaced. This is done from the Edit page using the Update data (new fiscal year / refresh) panel.

Here is what happens behind the scenes when you do this, so you know it's safe:

Steps:

  1. From the dashboard list, open the Edit page for the dashboard you want to refresh. (The page header reads ✎ Edit & Enhance with the dashboard's title beside it.)
  2. In the left-hand column, scroll to the heading Update data (new fiscal year / refresh). The note under it reads: "Upload a file (CSV, Excel, SAS, …) to replace a table in this dashboard's data, e.g. when a new FY is published. The old data.db is backed up."
  3. Check the table to replace box. It is pre-filled with the name of the dashboard's main (largest) table, in most cases leave it as-is. Only change it if you specifically need to replace a different table, and then you must type that table's exact name.
  4. Click the file picker just below it and choose your new data file. It accepts the same formats listed above (.csv, .tsv, .txt, .xlsx, .xls, .sas7bdat, .parquet, .json).
  5. Click ⬆ Update data. The status line shows "Uploading & loading…" while it works.
  6. On success you'll see a green message like "✓ Updated table 'data' with 14,302 rows from FY2025.csv. Backup: data.db.bak-20260615-143000. Reload the dashboard to see it." Open the dashboard (the ↗ open dashboard link at the top right of the Edit page) and refresh your browser to confirm the new numbers appear.

Important: the new file's table replaces the old one wholesale. For the existing charts to keep working, the new file should have the same column names (and the same kinds of values) as the data the dashboard was built on. If a column the charts rely on is renamed or missing, the charts that use it can come up empty. When in doubt, open the old data's column list first (the Edit page can reveal the dashboard's folder via 📂 Open folder (Finder)) and match your new file's headers to it.

Preparing your data before you upload (read this first)

A few minutes of cleanup in Excel saves a lot of confusion later. These tips apply to both kinds of upload.

  1. Clean, simple headers, one row. The very first row must be the column names, and only the column names. Delete title banners, logos, merged cells, and blank rows above the headers. Give each column a short, plain name (e.g. State, Year, Count) with no line breaks. The tool trims stray spaces from header names for you, but it can't guess what a blank or duplicated header means.

  2. One table per file. The tool reads a single rectangular table, one header row, then data rows all the way down, no gaps. Don't stack two tables in one sheet, and don't put summary notes or a second mini-table below the data. If your workbook has several sheets, only the first is read; split anything else into its own file.

  3. Make number columns actually numeric. Anything you want to add up, average, or chart as a value (counts, dollars, totals) must be stored as a number, not text. Remove commas, currency symbols, percent signs, and footnote marks (like 1,2341234, $5,0005000). In the Builder, the column summary tells you how each field was read, if a count you expected lands under Categories / text instead of Numbers, it's being treated as text and needs cleaning before the chart math will work.

  4. Drop "total" and "all" rollup rows. If your file already contains subtotal or grand-total rows (a row where State says "All" or "Total"), the charts will double-count them on top of the detail rows. Either remove those rollup rows, or be aware of them, the AI prep advice in the Builder will often flag this for your specific columns.

  5. Store dates and years simply. A year is cleaner as a plain number or short text (2025) than as a full date. If you have real dates, keep them in one consistent format rather than mixing styles.

  6. For a fiscal-year refresh, match the old shape. Beyond the above, the replacement file should mirror the structure of the data already in the dashboard, same column names, same value codes (for example, the same state abbreviations or the same category labels). Matching the shape is what lets the existing charts light up with the new year's numbers untouched.


Beyond the dashboards themselves, the site publishes an open-data layer so anyone can see and fetch the underlying public data in standard formats. The next section covers it.

Governed data and the open-data layer

The deployed site serves a read-only open-data layer on top of a single governed database. This layer is purely additive: it does not change how dashboards render or touch the verified per-dashboard spec.json files. Everything here is public data and is open to anyone (no editor account required).

The governed database

The authoritative published tables (24 of them) live in one Postgres database, eeoc-dashboards-db. The backend reads Postgres when the DATABASE_URL setting is present. When it is not (for example, when you run the tool locally), the backend falls back to a local governed DuckDB file, so the same routes and pages work locally without Postgres. "Governed" means this is the one source the catalog and the data API read from, so every published view of the data agrees.

The semantic YAML layer

For each governed table there is a small semantic YAML file (kept under the semantic/ folder) that names the table's dimensions (categories you group by) and measures (numbers you total), along with catalog metadata (title, description, identifier, keywords). These files are generated from the database, not hand-maintained, by twbx_convert/yaml_from_db.py, which introspects each table's columns and classifies them. The catalog page and the download bundle read these YAMLs, so regenerating them after a data change keeps the catalog in step.

To regenerate the semantic YAMLs locally (against the DuckDB fallback) or for Postgres (with DATABASE_URL set):

python -m twbx_convert.yaml_from_db

The routes the layer exposes

All of these are GET requests, are anonymous (public), and read the governed data:

Route What it serves
/catalog A human-readable catalog and inventory page: every dataset, its tables, row counts, and the dimensions and measures from the semantic layer.
/catalog/download A ZIP bundle of the whole catalog: the DCAT catalog, per-dataset JSON-LD, the semantic YAMLs, inventory.json and inventory.csv, and a README.
/data.json The DCAT-US 3 catalog as a single root document, with one Dataset entry per published dataset.
/data/<slug>.jsonld One dataset's standalone JSON-LD record.
/governed/tables.json The governed table inventory for the active backend (reports whether it is serving Postgres or DuckDB).
/governed/<table>.json Rows from one governed table, paged with ?limit= and ?offset=. Only table names the backend actually lists are served, so an arbitrary name can never reach the database.
/api/v1/datasets, /api/v1/datasets/<slug>, /api/v1/datasets/<slug>/data The read Data API: list datasets, describe one, and page its rows.
/openapi.json The OpenAPI 3 description of the read endpoints, matching the live routes.

Because the catalog, the DCAT records, and the read API all read the same governed tables, the values they return stay identical to what the dashboards show and to the CSV and JSON downloads.


Every public-facing dashboard has to be accessible. The platform has a built-in agent that checks for accessibility problems and fixes the common ones for you.

The 508 / accessibility agent

The 508 agent is a built-in helper that checks a dashboard for accessibility problems and, with your approval, fixes the common ones for you. "508" refers to Section 508 of the U.S. Rehabilitation Act, the federal rule that public-facing tools must be usable by people with disabilities. "WCAG 2.1" is the international checklist (Web Content Accessibility Guidelines) that 508 compliance is measured against. A "screen reader" (the agent mentions JAWS and NVDA by name) is the software a blind user relies on to read a page aloud.

This tool lives in the site as an editor, the editing environment. It is not available on the view-only site.

Opening the check

  1. Open the dashboard you want to check in the site as an editor.
  2. Click the ♿ Run 508 check button.
  3. A new page opens at /audit508/<id> (where <id> is that dashboard's identifier). Its banner reads ♿ Section 508 / WCAG review with the dashboard's name beside it.

Along the top of the banner are three shortcuts: ▶ open dashboard, ✎ edit, and ← all dashboards, so you can jump back without losing your place.

When the page loads it scans automatically, you do not have to press anything to start.

What the scan does

Behind the scenes the agent:

  1. Exports the dashboard to a single static HTML file, the same file the Share feature publishes.
  2. Runs an automated audit of that HTML against the WCAG 2.1 / Section 508 checklist. This is a text-only analysis: no browser, no internet, no special software, so the agency can keep running it indefinitely.
  3. Sorts every problem it finds into three buckets and lists them on screen.

While it works, the status area shows a spinner and the word "scanning…".

Reading the results

At the top, three colored tiles summarize the count in each bucket:

If the scan finds nothing, you instead see a single green tile, ✓ No automated findings, with the reminder to still do a live screen-reader pass.

Below the tiles, each finding is a card showing the WCAG criterion it relates to (for example "WCAG 1.1.1 Non-text Content"), a colored severity tag (Critical/Serious/Moderate/Minor), a plain-English description of the problem, and a line explaining the recommended fix.

The audit covers things like: a missing page language, a missing or empty page title, charts with no accessible name, images with no alt text, data tables with no header cells, headings that skip levels, vague link text such as "click here," unlabeled filters and dropdowns, and text colors with too little contrast against their background.

The always-offered baseline fixes

Two problems are listed on every scan, even if the static file looks fine, because they only appear once the live app is running in a browser:

These are the most common real-world show-stoppers for this kind of dashboard, so the agent always offers to fix them.

Approving or rejecting each fix

For every finding the agent can fix automatically, the card gives you two choices:

Some findings have no safe automatic fix. Those show ⚠ Manual fix, follow the recommendation above instead of a checkbox; you handle them yourself using the recommendation text.

Two shortcut buttons help with bulk choices:

Deploying the approved fixes

When your selections look right:

  1. Click ⬆ Deploy approved fixes.
  2. The status line shows "deploying…" and then a confirmation such as "✓ Deployed N fix(es). Re-scan: before→after findings," telling you how many fixes were applied and how the finding count changed.

What "deploy" actually does:

The patch is written into a clearly marked, auto-managed block. Re-running the check and deploying again replaces only that block, any hand-written custom.js/custom.css you added yourself is left untouched, and previously deployed fixes are kept.

A fix that is already live shows ✓ Deployed to the live dashboard on its card, with an optional re-deploy checkbox if you ever need to apply it again. You can also press ↻ Re-scan at any time to check the current state without deploying.

Important: automated checks are not the whole story

The page says this plainly, and it bears repeating: an automated scan only catches the problems that can be verified from the HTML. It does not replace a real-world test with an actual screen reader (such as JAWS) and a keyboard-only walkthrough. JAWS is proprietary Windows software that cannot be automated here. For formal 508 certification, always finish with a live JAWS/keyboard pass after the agent's fixes are in place.


The remaining two sections are for administrators. The first covers who can sign in and who can edit; the last is a quick-reference of recipes you can hand to anyone.

Managing accounts & roles (admin)

This section is for the person who administers the EEOC Explore site. It explains where user accounts are stored, how to add or remove people, how to set or change passwords, and how to control who is allowed to edit dashboards. You do not need to be a programmer to follow these steps, but you will be editing a few settings and restarting the site.

Where accounts live

The list of who may sign in is not stored in a database or in the app's code. It lives in a few environment settings the site reads when it starts:

The site reads these settings, so any change you make takes effect the next time the site is restarted.

Keep these settings private. They contain real passwords. Never email them, never put them in a shared folder, and never commit them to source control (git).

The two settings that control accounts are:

There is also an optional OPENAI_API_KEY setting, covered at the end.

The APP_USERS format

APP_USERS is a single line listing every user. Each user is written as fields joined by the pipe character ( | ), and users are separated by commas. (You may also put each user on its own line instead of using commas, both work.)

Each user entry has up to three fields, in this order:

email|password|TOTPsecret
  1. email, the address the person signs in with (for example cio@eeoc.gov).
  2. password, their password, in plain text.
  3. TOTPsecret (optional), a secret code that turns on two-factor authentication (2FA). "2FA" means that after typing their password, the person must also enter a rotating 6-digit code from an authenticator app (like Google Authenticator or Authy). If you leave this third field off, that person signs in with just email and password.

A complete example with two users, the first has 2FA, the second does not:

APP_USERS="cio@eeoc.gov|S0mePass!|JBSWY3DPEHPK3PXP, dcio@eeoc.gov|Other!Pass"

Notes that matter in practice:

APP_EDITORS, who may edit

APP_EDITORS is a comma-separated (or one-per-line) list of email addresses, and nothing else, no passwords:

APP_EDITORS="cio@eeoc.gov, dcio@eeoc.gov"

How it is used:

The site at https://eeoc-dashboards-1-uecu.onrender.com is view-only for ordinary users. APP_EDITORS controls who is allowed to edit and build there. Anyone not on that list is view-only, including when APP_EDITORS lists specific emails. Set APP_EDITORS to limit editing to certain people; leave it empty to let every signed-in user edit.

Add a user (with a strong password and 2FA)

The app includes a helper that generates a strong password, a 2FA secret, and a QR-code link for the new person, so you don't have to invent any of it yourself.

  1. Open a terminal on the machine that runs the platform, in the project folder (~/twbx-convert).
  2. Run the helper, replacing the email with the new person's address:

python -m twbx_convert.simple_auth add newperson@eeoc.gov

  1. The helper prints three things: - A ready-to-paste APP_USERS entry in the form email|password|secret. - The password to give the new person. - A link (and secret) they scan in Google Authenticator or Authy to set up their 6-digit codes.
  2. In the site's environment settings, paste the printed entry into the APP_USERS setting. If there are already other users, add a comma before the new entry so they are separated.
  3. Save the settings. The site picks up the new account on its next restart.
  4. Give the new person their password and have them scan the link to set up their authenticator app. The site address is https://eeoc-dashboards-1-uecu.onrender.com for everyone; whether they can edit depends on whether their email is listed as an editor.

If the helper reports that pyotp is not installed, it will instead print a no-2FA entry like email|password and a reminder to run pip install pyotp. You can use that entry as-is for a password-only account, or install pyotp and re-run to get 2FA.

You can also write a user entry by hand if you prefer, just follow the email|password|TOTPsecret format above. The third field is optional.

Remove a user

  1. Open the site's environment settings.
  2. In the APP_USERS setting, delete that person's entry, their email|password|... segment, along with the comma that separated it from its neighbor. Make sure you don't leave a dangling or doubled comma.
  3. If that same email also appears in APP_EDITORS, remove it there too.
  4. Save the settings and restart the site. The person can no longer sign in, and any existing session ends the next time they are asked to authenticate.

Set or rotate a password

There is no "change password" screen. You edit the environment settings directly.

  1. Open the site's environment settings.
  2. Find the user's entry in APP_USERS and replace the second field (the password) with the new one. Leave the email (first field) and, if present, the 2FA secret (third field) untouched. For example, change:

cio@eeoc.gov|OldPass!|JBSWY3DPEHPK3PXP

to:

cio@eeoc.gov|N3wStrongPass!|JBSWY3DPEHPK3PXP

  1. Save the settings and restart the site.
  2. Tell the person their new password through a secure channel. Their 2FA codes are unaffected as long as you left the third field unchanged.

Tip: to generate a fresh strong password (and a fresh 2FA secret) you can re-run the add helper for that email and copy just the password field you need.

Add an editor

  1. Open the site's environment settings.
  2. Add the person's email to the APP_EDITORS line, separating it from existing emails with a comma. The email should match the one in APP_USERS (case does not matter):

APP_EDITORS="cio@eeoc.gov, newperson@eeoc.gov"

  1. Save the settings and restart the site.
  2. The editor permission lets that person edit and build on the site. Their saved changes are live for viewers right away.

Reminder: if APP_EDITORS is left empty, all signed-in users can edit. List specific emails when you want to limit editing to certain people.

Optional: faster AI search with OPENAI_API_KEY

The platform includes an optional AI-assisted search feature. It is built to run against a local AI model (served by Ollama) by default. Setting an OPENAI_API_KEY in the site's environment settings enables a faster path for that feature. This is optional: the platform works without it, and AI search is simply quicker when a key is present.

  1. Obtain an API key from your OpenAI account.
  2. Add it to the site's environment settings as its own setting, for example:

OPENAI_API_KEY="sk-...your key here..."

  1. Save the settings and restart the site.

Treat this key like a password: keep it in the private environment settings only, and never commit or share it.


Finally, here are the everyday tasks as short, self-contained recipes. Each one stands on its own, so you can hand a single recipe to anyone who needs to do that one thing.

Step-by-step SOPs (quick recipes)

Each recipe below is a short "Goal → Steps" you can follow without prior knowledge. A few terms used throughout:

Golden rule: sign in as an editor, make the change, confirm it renders, and save. Every save backs up the old file first, so you can't silently break a working dashboard. Saved changes are live for viewers right away.


Refresh a dashboard for a new fiscal year (new data)

Goal: load a new year of data into an existing dashboard without rebuilding it.

  1. Sign in to the site as an editor at https://eeoc-dashboards-1-uecu.onrender.com.
  2. Open the dashboard you want to refresh (click its card, e.g. Charge and Litigation Statistics).
  3. Click ✎ Edit / Enhance in the button row next to the title.
  4. In the left panel, find "Update data (new fiscal year / refresh)."
  5. In the "table to replace" box, leave the suggested table name unless you know it should be different.
  6. Click Choose File and pick your new data file (CSV, Excel, SAS, etc.). The new data must have the same columns as before.
  7. Click ⬆ Update data. Wait for the green "✓ Updated table … with N rows" message. The old data is automatically backed up.
  8. Click ▶ Preview (or reload the dashboard) and check the numbers look right.

Change a dashboard title or a filter's default

Goal: rename a dashboard/tab/chart, or change which filter option is selected by default.

  1. Sign in as an editor and open the dashboard. Click ✎ Edit / Enhance.
  2. The easiest way, let the tool do it: in "Describe a change" (top-left box), type the change in plain English, e.g. "rename the first tab to Overview" or "change the chart title to Charges by State, FY2025." Make sure spec.json is the selected file in the Files dropdown.
  3. Click ✨ Ask AI to edit. The proposed change fills the editor on the right and lists what it changed. Review it.
  4. Click ✓ Validate only to confirm every chart still renders, then 💾 Save. Wait for "✓ Saved."
  5. For a filter's default selection: defaults are normally chosen automatically (latest year, first option). To force a specific default, describe it the same way (e.g. "set the Fiscal Year filter default to 2025") and Save, or ask an editor to set it in the dashboard's spec.json.
  6. Reload the dashboard to see the change. It is live for viewers right away.

If a page won't load after a manual edit to the code, click 💾 Save again or restore from the automatic backup. The editor will not save anything that fails to render, so the live dashboard stays safe.


Add a chart to an existing dashboard

Goal: add one more chart to a dashboard that's already published.

  1. Sign in as an editor and open the dashboard. Click ✎ Edit / Enhance.
  2. In "Describe a change," with spec.json selected, type what you want, e.g. "add a bar chart of total charges by state."
  3. Click ✨ Ask AI to edit, review the proposed result in the editor, click ✓ Validate only, then 💾 Save.
  4. Reload the dashboard to confirm the new chart appears and is correct.

Prefer point-and-click? Use the Chart Explorer (next recipe) to build the chart visually, click ➕ Add to dashboard, then 💾 Save dashboard.


Build a new dashboard from a CSV

Goal: make a brand-new dashboard from a spreadsheet, no coding.

  1. Sign in as an editor. On the EEOC Explore home page, click "Open the explorer →" (the Build your own dashboard with public data card). This opens the Chart Explorer.
  2. Under the dataset picker, click "Drag/drop or select a data file" and choose your CSV (in Excel: File ▸ Save As ▸ CSV). The first row must be column headers. Excel, SAS, Parquet and JSON files also work.
  3. Pick a Chart type (e.g. Bar), then drag/select your columns into X, Measures (Y), and optionally Color / split. A chart appears on the right.
  4. (Optional) Open ◇ Open code to fine-tune the Plotly code, then click ▶ Run to re-render.
  5. Click ➕ Add to dashboard. Repeat steps 3 to 5 for each chart you want.
  6. Type a name in the Dashboard name box, then click 💾 Save dashboard. It lands in the Studio and shows up as a card on the home page.
  7. Run a 508 check (next recipe).

Run a 508 check and deploy fixes

Goal: confirm a dashboard meets Section 508 / WCAG 2.0 AA accessibility rules and fix what it finds. ("508" is the federal accessibility standard; a "JAWS user" is someone using a screen reader.)

  1. Sign in as an editor and open the dashboard. Click ♿ Run 508 check. This opens the audit page and scans the dashboard.
  2. Findings are grouped into Show-stoppers, Edits, and Suggestions. Read each one. It lists the issue and the recommended fix.
  3. Approve fixes: tick "Approve & deploy this fix" on each finding you want, or click Approve all auto to select every auto-fixable one. Use Reject, I'll fix it for anything you'd rather handle by hand.
  4. Click ⬆ Deploy approved fixes. The fixes are written into the dashboard and its shareable HTML.
  5. Click ↻ Re-scan to confirm the count dropped (it reports e.g. "Re-scan: 6 to 0 findings").
  6. Before going fully public, do one live screen-reader pass: turn on VoiceOver (⌘+F5 on Mac) or JAWS/NVDA on Windows and tab through the page with the keyboard only, confirming every chart, filter and table is announced.

Add a new user

Goal: let a new person sign in, optionally as an editor.

  1. Sign-in accounts are controlled by two settings, APP_USERS and APP_EDITORS, in the site's environment settings. (For the full account-management reference, see Managing accounts & roles.)
  2. To let someone sign in: add their entry to APP_USERS in the form email|password, comma-separated. Example: analyst@eeoc.gov|ChooseAStrongPass.
  3. To make them an editor (so they see ✎ Edit / Enhance and ♿ Run 508 check): add their email to APP_EDITORS (comma-separated emails). Anyone not on this list is view-only.
  4. Save the settings. The site picks up the new account on its next restart.
  5. Give the person their email and password and the site link, https://eeoc-dashboards-1-uecu.onrender.com.
  6. To remove someone, delete their entry from APP_USERS (and APP_EDITORS) and save.

Optional 2FA (a rotating 6-digit code from an authenticator app) is available. An administrator generates a ready-to-paste line with python -m twbx_convert.simple_auth add <email> and shares the resulting QR/secret with the user.


Getting help

If you get stuck, work through these in order:

  1. Check the Glossary & troubleshooting section at the top of this guide. It covers the most common snags (the page won't open, sign-in trouble, AI search errors, saves that won't validate, and missing edit controls).
  2. Remember the golden rule: every save backs up the old version first, so if something looks wrong you can always restore the backup file (named like spec.json.bak-<timestamp>) from the dashboard's folder, reachable via 📂 Open folder (Finder) on the Edit page.
  3. For account, editor, or password questions, see Managing accounts & roles, or ask whoever administers the site.
  4. When in doubt, double-check before you save. A save goes live for viewers right away. If you're unsure a change is ready, click ✓ Validate only and ask a colleague to look before you save.

You won't break anything that can't be undone, that's the whole point of the validate-and-backup design. Take your time, try things, and lean on this guide.

↑ Back to top