A suite of tests for inspection of tabular data.
Project description
Data Curation Workflows
authors: Ellery Galvin, Alexandra Provo
date updated: 2026-05-08
contact: ellery.galvin@colorado.edu, crdds@colorado.edu
Description
This project checks spreadsheet files (.csv, .tsv, and .xlsx) for common data curation issues.
Each test reports:
- what failed,
- where it failed, and
- an example offending value.
Installation
pip install FAIRspreadsheets
Usage
import fair
suite = fair.Test_Suite("your_data.csv")
suite.run()
suite.report()
suite.save(format = "csv")
Call signatures
Test_Suite(wb_path, to_run=None, to_skip=None, config_path=None)
- wb_path: path to the workbook
Which tests to run? Defaults to all. Else, specify one of
- to_run: list of test names to run, lower case strings as below
- to_skip: list of test names to skip, lower case strings as below
Extra arguments to replace defaults for tests (see below):
- config_path: path to the config file, replaces defaults where specified
Test_Suite methods:
- run(): runs the tests
- report(): prints the results to the console
- save(format = "json"): saves the results to a JSON file
- save(format = "csv"): saves the results to a CSV file
Tests Implemented
File-level tests
file_name_length: filename length is within allowed boundsfile_name_whitespace: filename contains no whitespacefile_name_final: filename does not include the wordfinalfile_name_word_separation: filename does not mix camelCase, underscores, and dashesfile_name_special_characters: filename has no disallowed special charactersfile_encoding: text files decode cleanly in the configured encodingfile_delimiter: delimiter in .csv or .tsv file matches the extension
Sheet-level tests
sheet_empty: worksheet is not emptysheet_name: sheet name contains no whitespace or disallowed special characterssheet_upper_left_corner: table begins at upper-left (no leading blank rows/columns)sheet_multi_table: sheet does not appear to contain multiple separate tables
Header-level tests
header_duplicates: duplicate header names are flaggedheader_id: first header should not be exactlyIDheader_length: header length is within allowed boundsheader_first_char: headers should not start with digitsheader_space: headers should not contain spacesheader_untrimmed_white_space: leading/trailing whitespace in headers is flaggedheader_word_separation: headers should not mix camelCase, underscores, and dashesheader_special_characters: headers should not contain disallowed special charactersheader_date: date-like header names (for combined date fields) are flaggedheader_mixed_datatypes: columns with mixed numeric/text values are flagged
Cell-level tests
cell_aggregate_row: aggregate-like summary row at bottom (e.g., total/sum) is flaggedcell_special_characters: disallowed special characters in cells are flaggedcell_untrimmed_white_space: leading/trailing whitespace is flaggedcell_newlines_tabs: tabs/newlines/vertical tabs in cells are flaggedcell_missing_value_text: text placeholders for missing values (NA,null,-, etc.) are flaggedcell_question_mark_only: cells equal to?are flaggedcell_white_space_only: whitespace-only cells are flaggedcell_number_space: cells containing only digits+spaces are flaggedcell_dates: dates not matching required format are flaggedcell_scientific_notation: scientific notation in text cells is flaggedcell_units: values that combine number+unit in one cell (e.g.,10kg) are flagged
Options Configuration
Options are set in a JSON config file (see config.example.json).
Important conventions:
- Top-level key = lower-case class name (for example
cell_dates) - Option names = keyword-only
__init__args for that test - Only the tests and options below are accepted
File-level options
file_name_lengthmin_length(int, default5): minimum allowed filename lengthmax_length(int, default32): maximum allowed filename length
file_name_special_charactersspecial_char_pattern(regex string, default[!@#$%^&*()+=\[\]{};:'"|\\,<>\?/]): disallowed characters
file_encodingvalid_encoding(string, defaultutf-8): expected text encoding for CSV-like files
file_delimitervalid_delimiter_pairs(list of tuples, default[(",",".csv"),("\t",".tsv")]): expected pairings of delimiters and file extensions. Note: currently not possible to configure this in a JSON config file since JSON does not allow tuples
Sheet-level options
sheet_namespecial_char_pattern(regex string, default[!@#$%^&*()+=\[\]{};:'"|\\,<>\?/]): disallowed characters
Header-level options
header_lengthmin_length(int, default1): minimum header lengthmax_length(int, default24): maximum header length
header_special_charactersspecial_char_pattern(regex string, default[!@#$%^&*()+=\[\]{};:'"|\\,<>\?/]): disallowed characters
header_datedate_keywords(list of strings, default["date", "datetime", "timestamp"]): keywords used to detect date-like headers
Cell-level options
cell_aggregate_rowaggregate_words(list of strings, default["total","sum","average","count","min","max"]): terms used to detect summary rows
cell_special_charactersdefault_pattern(regex string): disallowed chars for normal columnsurl_pattern(regex string, default^https?://): validation pattern for URL columnsfree_text_pattern(regex string): disallowed chars for free-text columnsurl_columns(list of strings, default[]): columns validated withurl_patternfree_text_columns(list of strings, default[]): columns validated withfree_text_patternskip_columns(list of strings, default[]): columns skipped by this test
cell_datesdate_columns(list of strings, default[]): explicit date columnsauto_detect_columns(bool, defaultfalse): infer likely date columns automaticallyformat_code(string, default%Y/%m/%d): requireddatetime.strptimedate formatdate_column_threshold(float0..1, default0.8): fraction of parseable values needed to auto-mark a column as date-like
cell_unitsunit_abbreviations(list of strings): unit suffixes used to detect values like12kg
Tests without options should be configured with an empty object or omitted.
Output Specification
Only failed (or not-completed) tests appear in saved output. Passing tests are omitted.
JSON output (results/<input_base>_<YYYY-MM-DD_HH-MM-SS>.json)
- Root object: keys are sheet names, plus
filefor file-level failures - Value per sheet: object keyed by test name
- Value per test:
message: failure/not-completed messageissues: object oflocation -> example
- For JSON compatibility, non-string issue keys (such as tuple coordinates) are stringified.
CSV output (results/<input_base>_<YYYY-MM-DD_HH-MM-SS>.csv)
Columns are exactly:
path: input workbook pathsheet: sheet name (filefor file-level tests)test_name: lower-case test class namemessage: failure/not-completed messagelocation: issue key (string form)example: issue value
Each row corresponds to one issue entry from one failed test.
For Developers
Directory contents
- .gitignore excludes the data directory from version control, and other files
- config.example.json is an example configuration for test options
- demo.xlsx is a file on which to test detectors.py
- detectors.py is the main module that implements a series of automated tests for data curation workflows
- pyproject.toml is the specification for the PyPI package
- LICENSE contains the licensing information for this software
Architecture overview
Test_Suitediscovers concrete tests automatically from subclasses ofTest- Tests are grouped by level using class name prefixes:
file_,sheet_,header_,cell_ - Dependencies are declared via
Has_Dependency; the suite executes tests in dependency-safe order - Each test writes failures into
self.issues, then callsTest.validate(...)to set status/message trimmed_results()filters outputs to failed/not-completed tests only
How to add a new test safely
- Choose the right base class:
- file checks: inherit
File - sheet checks: inherit
Sheet - header checks: inherit
Header - cell checks: inherit
Cell
- file checks: inherit
- Name the class with underscores (example
Cell_My_New_Check).
The runtime test key becomes lower-case class name (cell_my_new_check). - Put user-configurable settings in keyword-only args:
- use
def __init__(self, *, ...) - validate types/ranges in
__init__
- use
- Implement
validate(...)and populateself.issuesas:- key: location (header name, tuple coordinate,
filename, etc.) - value: offending example/value
- key: location (header name, tuple coordinate,
- End validation with
Test.validate(self, fail_message, pass_message)so status/message are consistent. - If your test depends on another test, add
Has_Dependency.__init__(self, DependencyClass)and use dependency outputs invalidate. - Add documentation:
- include the new test in the test list above
- add config options under the correct section (if any)
- update
config.example.jsonwith sensible defaults
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fairspreadsheets-0.0.5.tar.gz.
File metadata
- Download URL: fairspreadsheets-0.0.5.tar.gz
- Upload date:
- Size: 51.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: python-requests/2.32.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c43d56803b4517a9243b0e85a63d80a490450fceafc5c273a27e8b78435d1238
|
|
| MD5 |
3b5d7c82518375bed477de1560b06430
|
|
| BLAKE2b-256 |
1b5eeced85a7e342e068a49cd74d9db83bed884129ae1aabae2e58ab12877ef7
|
File details
Details for the file fairspreadsheets-0.0.5-py3-none-any.whl.
File metadata
- Download URL: fairspreadsheets-0.0.5-py3-none-any.whl
- Upload date:
- Size: 23.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: python-requests/2.32.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5596441f239c14ac86b5ddac000b6cc5ccdc07d5f266f3e8f589cb26646a5889
|
|
| MD5 |
88da4595efac730919df5f6a4a59993b
|
|
| BLAKE2b-256 |
576b3859a537046de4277b2e251747339ff1920a2265952646ea444fad3c8e70
|