Skip to content

vmnacar/refio

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

refio

Read, write, and convert REFI-QDA qualitative data analysis project files (.qdpx, .qde, .qdc) in Python.

REFI-QDA is the open interchange standard that lets a qualitative coding project (its documents, hierarchical codebook, coded segments, cases, variables, and memos) move between tools such as QualCoder, MAXQDA, ATLAS.ti, NVivo, Dedoose, and Taguette. refio gives that standard a clean, typed, well tested Python API and a command line tool, so you can script around coded data, convert between formats, and build other tools on top.

The gap it fills

The REFI-QDA standard exists, but the Python tooling around it is almost empty: the closest projects on GitHub are zero and one star, partial, single direction, or tied to one application. There is no maintained library that reads and writes the format both ways with tests. refio is that library. See ../OPPORTUNITIES.md for the evidence and the market scan.

Full scope (what this project ships)

Core library:

  • A typed model for the REFI-QDA elements that matter for interoperability: users, a hierarchical codebook (codes with color and description), text and PDF sources with coded selections, cases, variables, and notes (memos).
  • A hardened XML reader that parses project documents safely (see Security).
  • An XML writer that serializes the model back in the standard's element order.
  • A .qdpx archive reader and writer that links each source to its file in the archive's Sources folder, with zip bomb and zip slip protections.
  • A .qdc codebook reader and writer for sharing coding schemes.
  • High level load, save, and convert_file that pick the format by extension and round trip the project model.

Usability features:

  • Tidy table helpers: flatten a project into codes, sources, and segments tables as lists of rows, write them to CSV (standard library, no pandas needed), or get a pandas DataFrame via the optional pandas extra.
  • A structural validator that catches duplicate GUIDs, codings that point at missing codes, and out of range text offsets.
  • A safe extract_sources helper to unpack a .qdpx archive's source files.
  • A command line tool: inspect, codes, segments, tables, convert, validate, and version.

Install

Requires Python 3.10 or newer (developed and tested on 3.14).

python -m venv .venv
# Windows PowerShell:  .venv\Scripts\Activate.ps1
# Git Bash / Linux / macOS:  source .venv/bin/activate
pip install -e .

Exact pinned environment:

pip install -r requirements-lock.txt
pip install -e . --no-deps

Optional extras:

pip install -e ".[pandas]"   # enables to_dataframe
pip install -e ".[dev]"      # pytest and hypothesis for the test suite

Usage

As a library

import refio
from refio.model import Project, Code, TextSource, PlainTextSelection, Coding, new_guid

# Build a project
project = Project.new("My study", author="Alice")
theme = Code(guid=new_guid(), name="Funding", color="#FF8800")
project.codebook.codes.append(theme)

text = "Community programs need sustained funding to work."
start = text.index("sustained funding")
src = TextSource(guid=new_guid(), name="Interview 1", plain_text=text)
src.selections.append(
    PlainTextSelection(
        guid=new_guid(), start=start, end=start + len("sustained funding"),
        codings=[Coding(guid=new_guid(), code_guid=theme.guid)],
    )
)
project.text_sources.append(src)

# Save as a .qdpx archive, then load it back
refio.save(project, "study.qdpx")
loaded = refio.load("study.qdpx")
print(loaded.summary())

# Flatten coded segments to rows or CSV
from refio import segments_table, write_csv
write_csv(segments_table(loaded), "segments.csv")

From the command line

refio inspect study.qdpx           # summary counts as JSON
refio codes study.qdpx             # the code hierarchy as a tree
refio segments study.qdpx          # list coded segments
refio segments study.qdpx --csv segments.csv
refio tables study.qdpx --out out  # codes.csv, sources.csv, segments.csv
refio convert study.qdpx study.qde # convert between formats
refio convert study.qdpx codes.qdc # export the codebook only
refio validate study.qdpx          # structural check, non zero exit on errors

You can also run it without installing the script: python -m refio inspect study.qdpx.

Configuration

refio has no configuration file and reads no environment variables. Behavior is controlled by function arguments and CLI flags. Safety ceilings for archive handling (entry count, per entry size, total size) are defined in refio/constants.py.

Supported elements and formats

Element Read Write
Project metadata, users yes yes
Hierarchical codes (color, description) yes yes
Text sources and plain text content yes yes
Plain text selections and codings yes yes
PDF sources (bytes) and PDF selections yes yes
Cases (source, selection, code refs, variable values) yes yes
Variables (typed) yes yes
Notes (memos) yes yes

Formats: .qdpx (archive), .qde (project XML, text embedded for portability), .qdc (codebook only). The reader matches elements by local name, so documents with or without the namespace prefix, from REFI-QDA 1.0 or 1.5 producers, parse.

How to run the tests

pip install -e ".[dev]"
pytest

The suite (37 tests) builds its sample data in memory, so no binary fixtures are checked in, and it runs fully offline. It covers the model, round trips through all three formats, parsing external namespaced and non namespaced XML, the tidy tables and CSV export, the validator, the CLI, and the security properties below.

Architecture notes

  • model.py typed dataclasses for the project.
  • constants.py namespace URIs, file roles, and archive safety ceilings.
  • reader.py hardened XML to model parsing.
  • writer.py model to XML serialization.
  • archive.py .qdpx read and write, with archive safety checks.
  • qdc.py standalone codebook read and write.
  • convert.py high level load, save, and convert.
  • tables.py flatten to rows, CSV, and optional DataFrame.
  • validate.py structural validation.
  • cli.py the Typer command line interface.

Security notes

refio is built to read files that may be untrusted (a .qdpx shared by a collaborator, or downloaded from a repository).

  • XXE and entity expansion are blocked. The XML parser is configured with resolve_entities=False, no_network=True, and load_dtd=False. External entities are never fetched (so a crafted file:// or http:// entity cannot exfiltrate or fetch anything), and entity references are not expanded (so a billion laughs payload cannot blow up memory). Both are covered by tests.
  • Zip bomb protection. Before reading an archive, refio checks the number of entries, each entry's uncompressed size, and the total uncompressed size against configurable ceilings, and refuses archives that exceed them.
  • Zip slip protection. When extracting a .qdpx to disk, every member path is resolved and confirmed to stay inside the destination directory, so an entry named ../../evil is refused rather than written outside the target. Reading source content into memory never touches the filesystem. There is a test that a traversal entry is refused and nothing is written outside the destination.
  • No code execution paths. There is no eval, no exec, no pickle, and no untrusted deserialization. XML text is escaped by lxml on write, so source text or memo bodies cannot break the document structure.
  • No secrets, no network, no configuration side channels. refio reads no environment variables and makes no network calls. It operates only on the files you pass it.

License

MIT. See LICENSE. REFI-QDA is an open standard; refio is an independent implementation and is not affiliated with the REFI consortium or any CAQDAS vendor.

About

Read, write, and convert REFI-QDA (.qdpx, .qde, .qdc) qualitative data analysis project files in Python

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages