Windows Directory Data Source Template

Prev Next

Overview

The Windows Directory data source template turns a local or network directory tree into a Validatar data source. It walks the folder tree, catalogs every file as a table, reads and profiles the common data formats (CSV, Excel, Parquet, Avro, JSON, XML, GeoJSON), and captures statistics for non-tabular files (PDF, Markdown, images, plain text). Folders become schemas, files become tables, and columns are inferred from each file's contents.

This is a Python (Script) template. All discovery and reading is done by Python running on the Data Agent, so the same template works whether the data lives on the agent's local disk or on a UNC share the agent can reach.

Platform: Windows file system (local paths and UNC shares)
Connection Category: Script (PythonScript)
Runs on: the Data Agent (required — it reads the local/UNC file system)
Template version: 2.1.0
Marketplace: Windows Directory

Note: The Windows Directory, AWS S3, and Azure Blob Storage templates are built from one shared file-reading library. They expose the same macros, profiling, and custom fields — only the storage layer (local disk vs. S3 vs. Blob) and the connection parameters differ. Learn one and you know all three.

How It Works

  • Folders are schemas, files are tables. The template walks from the root folder you configure. Each folder (relative to the root, .-rooted — ., .\CSV, .\CSV\CUSTOMER_1001) becomes a schema; each file inside it becomes a table.
  • Columns are inferred on read. For tabular formats, the template reads the file and infers column names and data types from the content (Parquet and Avro carry explicit types; CSV and Excel are inferred).
  • One ingestion pass. Metadata ingestion runs at the data-source level and catalogs the whole tree in a single pass.
  • Everything is driven by two parameters — the root path and whether to descend into subfolders.

Authentication

The Windows Directory template has no credential parameters. Access is governed entirely by the Windows identity the Data Agent runs as (its service account). There is nothing to enter in Validatar beyond the path.

Access scenario Requirement
Local disk on the agent host The agent's service account needs read access to the folder.
UNC / network share (\\server\share\...) The agent's service account must be a domain (or trusted) account with read permission on the share and the underlying NTFS folder.

Tip: If ingestion returns an empty catalog or "access denied," it is almost always the agent's service account lacking read permission on the path — not a Validatar configuration issue. Confirm the account can open the path in Windows Explorer while logged in as that identity.

Connection Parameters

Parameter Reference key Type Required Description
Parent Folder Path parent_folder_path String Yes Root directory to catalog. Local path (D:\data\exports) or UNC share (\\fileserver\finance\extracts).
Include Sub Folders include_sub_folders Boolean Yes When true, descends into every subfolder (each becomes a schema). When false, only the root folder is cataloged. Default: true.

Required Python Packages

These must be present on the Data Agent's Python runtime.

Required

  • pandas, numpy — the core data-frame and numeric layer (hard requirement)
  • pyarrow — Parquet / ORC / Feather
  • fastavro — Avro
  • openpyxl — Excel (.xlsx / .xlsm)

Optional (features degrade gracefully if absent)

  • fitz (PyMuPDF) — PDF statistics
  • Pillow (PIL) — image statistics
  • charset_normalizer — best-effort text-encoding detection
  • lxml — XML reading

Note: The library degrades per file, not per run. If an optional package is missing, only the files that need it are affected — that file returns a clear error and the rest of the catalog continues. Missing fastavro, for example, doesn't stop your CSVs from being read and profiled.

Capabilities

Supported File Types

Catalog file type Extensions Read as
Delimited .csv, .tsv, .tab Tabular (delimiter sniffed)
Fixed Width / Text .txt, .flat, .dat Tabular (fixed-width inference, else one line per row)
Excel .xlsx, .xlsm, .xls Tabular
Columnar .parquet, .orc, .feather Tabular (explicit types)
Row Binary .avro Tabular (explicit types)
Semi-Structured .json, .jsonl, .ndjson, .xml, .geojson Tabular (flattened / normalized)
Document .pdf, .md, .markdown, .rst Content statistics
Image .png, .jpg, .jpeg, .gif, .bmp, .tif, .tiff, .webp Content statistics

Note: JSON is handled flexibly — arrays of objects, a single object, an object of arrays, GeoJSON feature collections, and line-delimited JSON (even when saved with a .json extension) are all detected and flattened into columns.

Macros

The template ships with 28 macros: 21 generic file macros and seven for nested JSON. Every generic macro works on every file type through the shared reader, so you write a test once and point it at any folder and file. Macros take metadata-linked parameters (Folder → Schema, File → Table, Column) so they render as catalog-driven dropdowns when you build a test.

A. Single-file data

Macro Returns
File - Read Data (any type) The whole file as a dataset
File - First N Rows The first N rows (default 100)
File - Row Count Row count as a single value
File - Schema (columns + inferred types) Column list with inferred type and null %
File - Profile One Column Profile statistics for one column
File - Row Count Grouped by Column Group-by counts
File - Aggregate a Column sum / min / max / mean / count / nunique

B. Pattern & union (multi-file)

Macro Returns
Folder - Files Matching a Pattern Files whose name matches a glob (e.g. account_*.csv)
Folder - Union Files Matching a Pattern Every matching file read and unioned (schema-aligned, each row tagged with its source file)
Folder - Row Count Across Matching Files Per-file and total row counts
Folder - Schema Consistency Across Files Each file's columns and whether they match the first file (daily/partitioned extracts)

C. Folder & file information

Macro Returns
Folder - File Inventory Every file with size, detected type, and modified date
Folder - Summary One-row rollup: file count, total size, distinct types, date range
File - Full Metadata One file's size, modified date, MD5, and (if tabular) row/column counts

D. Content statistics (including non-tabular)

Macro Returns
File - Content Stats (any type) Type-aware stats for any file
PDF - Statistics Page count, word count, embedded images, title/author, encryption flag
Markdown - Statistics Headings, links, images, code blocks, table rows
Text - Statistics Lines, words, encoding, blank lines, longest line
Image - Statistics Width/height, megapixels, format, mode, alpha
JSON - Statistics Record count, nesting depth, top-level keys
XML - Statistics Root tag, element count, distinct tags, depth

Tip: Folder - Union Files Matching a Pattern is the workhorse for partitioned or daily extracts — point it at a folder of *.parquet or *.avro files and it returns one unified dataset with a _source_file column, so you can test the combined set and still trace any row back to its file.

E. Nested JSON

The macros above treat JSON as a flat table. These seven treat it as a tree.

The difference matters more than it sounds. The standard reader normalizes nested objects into dotted columns, so customer.address.city works as a column. A nested array cannot become a column, so it stays in the cell as a raw list:

order_id     customer.address.city   items
ORD-00001    Austin                  [{'sku': 'WID-001', 'quantity': 3, ...}, {...}]

You cannot count that, group it, join on it, or assert anything about it. For order payloads, event streams and API captures, where the interesting data is inside a repeated element, that is where testing usually stops.

Macro Returns
JSON - Discover Nested Structure One row per path in the document tree: depth, JSON type(s), how many records contain it, array cardinality, and a sample value
JSON - Flatten to Table The whole document as a table, with configurable array handling (join / explode / count)
JSON - Explode an Array Path One row per element of a chosen array, optionally carrying the parent record's fields
JSON - Extract Specific Paths A chosen set of dotted paths as columns; [] descends into every array element
JSON - Array Cardinality per Record Element count per record for a chosen array path, keyed by a business identifier
JSON - Type Conflicts Across Records Paths whose JSON type varies between records
JSON - Key Coverage / Sparse Fields Paths present in fewer than N% of records

Start with Discover Nested Structure. It is the map. Run it once against a sample file in the folder and it tells you every path, how deep it is, and which ones are arrays, so you know what to feed the other six.

Then reach for Explode an Array Path, not Flatten to Table. Both produce tabular output, but flattening with arrays=explode expands every array at once, and sibling arrays multiply: a record with 3 line items and 2 contacts yields 6 rows, and any sum over it is wrong by a factor of 2. Exploding one path does not have that problem. Flatten's default is therefore join, which keeps one row per record and encodes arrays as text.

Note: explode is the right choice when you want the cross-product, or when only one array is present. The parameter exists so the behaviour is a decision rather than a surprise.

The same 28 macros ship in the AWS S3 and Azure Blob Storage templates, so a test written against a local folder runs unchanged against a bucket or a container.

See Macros for how macros are used when building tests.

Metadata Ingestion

Ingestion runs once at the data-source level. It walks the tree, registers each folder as a schema and each file as a table, and populates the File Info catalog custom fields (see below) — including per-folder rollups (file count, total size, distinct file types, newest/oldest modified). Column-level metadata is discovered on demand by the profile set rather than during ingestion, which keeps ingestion fast on large trees.

See Metadata Ingestion Scripts — Python Templates for how Python ingestion scripts work.

Profiling

The template ships one profile: File Profile (columns + table metrics, any file type). It runs per table (file) and is type-safe — it reads any supported file and emits only the metrics that apply to that file's type. Run it as a profile set against a folder to profile every file in it.

Following the principle that anything numeric or statistical about an asset is a profile, and descriptors/tags/text belong in custom fields, it produces:

Column-level profiles (numeric, per column)

  • Null count / percent, blank count / percent, distinct count / percent
  • Length statistics: min / max / mean length
  • Most common value and its count
  • Numeric statistics (numeric columns): min, max, mean, median, standard deviation, zero count, negative count
  • Date range (date columns): min date, max date

Table-level file metrics (numeric, per file)

  • File size (bytes), file age (days), row count, column count
  • Type-specific: PDF page count, content word/line/record count, image width/height (px)

Note: The column profiler is deliberately type-safe: a mixed-type or all-text column never breaks the run — non-applicable metrics are simply omitted for that column.

See Profiling Configuration — Python Templates for profile-set mechanics.

Catalog Custom Fields (required dependency)

The ingestion script emits catalog custom fields to describe files and folders. These fields hold descriptors — text, tags, dates, and folder rollups — that don't fit the numeric profile model (schemas in particular can't be profiled, so folder rollups are captured here).

You must import the File Info - Catalog Custom Fields set before you run ingestion. Validatar silently drops custom-field columns that have no matching field definition, so without it these values are simply lost.

Schema level (folder rollups): Contains Files, Schema Object Represents, Full Path, File Count, Total Size (bytes), Distinct File Types, Readable File Count, Newest Modified, Oldest Modified

Table level (per file): File Type, File Name, Sheet Name, File Size, File Total Bytes, File Extension, Date Modified, File Logical Table

Warning: Catalog custom fields import through the UI only (Settings > Catalog Custom Fields > Import), and require the GlobalCustomMetadataAdmin role. Import the File Info set first, then import this template, then create the data source and run ingestion.

Installation

  1. Import the custom fields first. In Settings > Catalog Custom Fields > Import, import the File Info - Catalog Custom Fields set.
  2. Import the template. In Settings > Data Source Templates > Import, select the Windows Directory template file.
  3. Create a data source from the template and set Parent Folder Path and Include Sub Folders.
  4. Assign a Data Agent that can reach the path and has the required Python packages.
  5. Run metadata ingestion, then run the File Profile profile set against the folders you care about.

Upgrading

Importing a newer version of the template with Overwrite existing updates the template itself and nothing else. Each data source built from a Script template keeps its own copy of the template's pre-execution script, taken at the moment the template was selected. A data source created on 2.0.0 keeps running the 2.0.0 code until you refresh that copy.

To pick up a new version on an existing data source:

  1. Import the new template file (Settings > Data Source Templates > Import, with overwrite).
  2. Open each data source built from the template and edit its connection.
  3. Re-select Windows Directory as the template, confirm the parameters are still filled, and save.
  4. Run JSON - Discover Nested Structure against any JSON file in the tree. On a data source still carrying the old pre-execution script it fails with a NameError for the JSON helpers; on a refreshed one it returns the path listing.

New data sources created after the import pick up the current version with no extra step.

What You Still Need to Configure

  • Data Agent selection — the source only works through an agent with local/UNC access to the path.
  • Service-account permissions — read access on the path (and the share, for UNC).
  • Python packages — confirm the required set is installed on the agent; add optional ones for PDF/image/encoding support.
  • Folder scope — set include_sub_folders to false to keep the catalog shallow on very large trees.

Troubleshooting

Symptom Cause Fix
Empty catalog after ingestion Agent service account can't read the path Grant read permission to the agent's identity on the folder / UNC share
A single file errors, others succeed Missing optional package for that format Install the package (fastavro, openpyxl, pyarrow, fitz, Pillow)
Custom-field values missing in the catalog File Info custom fields not imported before ingestion Import the File Info set, then re-run ingestion
"not a tabular file type" from a data macro Macro used on a PDF/image/text file Use a content-stats macro (PDF/Image/Text/JSON/XML Statistics) instead
UNC path works locally but not from the agent Agent running as LocalSystem or a local account Run the agent service as a domain account with share access

Sample Test Pack

Windows Directory - Unstructured File Sample Tests (marketplace) ships 190 worked examples against this template: text, log and markdown files, PDFs and images, each with its adjustable values in a block at the top of the script and a switch that points it at a file built to make it fail. See Windows Directory Unstructured File Sample Tests. The files those tests read are a separate download, Unstructured File Sample Fixture (S3 and Azure Blob); unzip it and copy the six top-level folders under the data source's Parent Folder Path on the Data Agent host.

Related Articles