Metadata-Version: 2.5
Name: fairdm
Version: 0.3.1
Summary: A Django framework for building FAIR research data portals with minimal code
Project-URL: Homepage, https://www.fairdm.com
Project-URL: Documentation, https://www.fairdm.com
Project-URL: Repository, https://github.com/FAIR-DM/fairdm
Project-URL: Issues, https://github.com/FAIR-DM/fairdm/issues
Project-URL: Changelog, https://github.com/FAIR-DM/fairdm/blob/main/CHANGELOG.md
Author: Samuel Jennings
License-Expression: MIT
License-File: LICENSE
Keywords: FAIR,database,fairdm,open data,research,research data management
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Django :: 6.0
Classifier: Intended Audience :: Science/Research
Classifier: Natural Language :: English
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: <4.0,>=3.13
Requires-Dist: celery<6.0.0,>=5.6.2
Requires-Dist: daisy-cotton-ext<0.6.0,>=0.5.0
Requires-Dist: defusedxml<0.8.0,>=0.7.0
Requires-Dist: django-adminactions<3.0.0,>=2.0.0
Requires-Dist: django-allauth[mfa,socialaccount]<66.0.0,>=65.19.4
Requires-Dist: django-auto-prefetch<2.0.0,>=1.12.0
Requires-Dist: django-celery-beat<3.0.0,>=2.5.0
Requires-Dist: django-cleanup<10.0.0,>=8.1.0
Requires-Dist: django-content-license<0.4.0,>=0.3.0
Requires-Dist: django-controlled-vocabularies<0.4.0,>=0.3.1
Requires-Dist: django-cors-headers<5.0.0,>=4.7.0
Requires-Dist: django-countries<10.0.0,>=7.6.1
Requires-Dist: django-crispy-forms<3.0,>=2.3
Requires-Dist: django-dbbackup<6,>=5.3
Requires-Dist: django-downloadview<3.0.0,>=2.3.0
Requires-Dist: django-easy-icons<1.0.0,>=0.6.0
Requires-Dist: django-environ<0.15.0,>=0.10.0
Requires-Dist: django-filter<27.0,>=24.3
Requires-Dist: django-flex-menus
Requires-Dist: django-guardian<4.0.0,>=3.5.0
Requires-Dist: django-hijack<4.0.0,>=3.7.4
Requires-Dist: django-htmx<2.0.0,>=1.29.0
Requires-Dist: django-import-export<5.0.0,>=4.0.3
Requires-Dist: django-lifecycle<2.0.0,>=1.2.4
Requires-Dist: django-literature[ui]<0.4.0,>=0.3.1
Requires-Dist: django-meta<3.0.0,>=2.1.0
Requires-Dist: django-model-utils<6.0.0,>=4.3.1
Requires-Dist: django-mvp-accounts[api]<0.5.0,>=0.4.0
Requires-Dist: django-mvp-charts<0.4.0,>=0.3.0
Requires-Dist: django-mvp-compliance<0.4.0,>=0.3.0
Requires-Dist: django-mvp-forms<1.0,>=0.7.0
Requires-Dist: django-mvp<0.32.0,>=0.31.0
Requires-Dist: django-ordered-model<4.0.0,>=3.7.4
Requires-Dist: django-parler<3.0,>=2.3
Requires-Dist: django-partial-date<2.0,>=1.3
Requires-Dist: django-pint<1.1.0,>=0.7.3
Requires-Dist: django-polymorphic<5.0.0,>=4.11.7
Requires-Dist: django-redis<8.0.0,>=5.2.0
Requires-Dist: django-solo<3.0.0,>=2.2.0
Requires-Dist: django-split-settings<2.0.0,>=1.2.0
Requires-Dist: django-storages[boto3]<2.0.0,>=1.14.2
Requires-Dist: django-tables2>=3.0.1
Requires-Dist: django-taggit<7.0.0,>=6.1.0
Requires-Dist: django-tomselect<2027,>=2026.6.2
Requires-Dist: django-waffle<6.0.0,>=5.0.0
Requires-Dist: django[argon2]<7.0.0,>=6.0
Requires-Dist: djangorestframework<4.0.0,>=3.17.2
Requires-Dist: drf-orjson-renderer<2.0.0,>=1.7.5
Requires-Dist: drf-spectacular[sidecar]<0.31.0,>=0.29.0
Requires-Dist: easy-thumbnails<3.0,>=2.10
Requires-Dist: gunicorn<27.0.0,>=23.0.0
Requires-Dist: markdown<4.0.0,>=3.10.3
Requires-Dist: nh3<0.4.0,>=0.3.7
Requires-Dist: pillow<14.0,>=11.0
Requires-Dist: pint<1.0,>=0.26.1
Requires-Dist: psycopg2-binary<3.0.0,>=2.9.13
Requires-Dist: pyecharts<3.0.0,>=2.1.0
Requires-Dist: pymdown-extensions<13.0.0,>=11.0.2
Requires-Dist: python-gnupg<0.6,>=0.5.7
Requires-Dist: rapidfuzz<4.0.0,>=3.14.6
Requires-Dist: redis<9.0.0,>=4.5.2
Requires-Dist: requests<3.0,>=2.32
Requires-Dist: sentry-sdk[django]<3.0.0,>=2.69.2
Requires-Dist: shortuuid<2.0.0,>=1.0.13
Requires-Dist: tablib[ods,xlsx]<4.0.0,>=3.10.0
Requires-Dist: uncertainties<4.0.0,>=3.2.3
Requires-Dist: whitenoise<7.0.0,>=6.4.0
Description-Content-Type: text/markdown

# FairDM

[![Tests](https://github.com/FAIR-DM/fairdm/actions/workflows/tests.yml/badge.svg)](https://github.com/FAIR-DM/fairdm/actions/workflows/tests.yml)
[![Build](https://github.com/FAIR-DM/fairdm/actions/workflows/build.yml/badge.svg)](https://github.com/FAIR-DM/fairdm/actions/workflows/build.yml)
[![Publish](https://github.com/FAIR-DM/fairdm/actions/workflows/publish.yml/badge.svg)](https://github.com/FAIR-DM/fairdm/actions/workflows/publish.yml)
[![Documentation](https://readthedocs.org/projects/fairdm/badge/?version=latest)](https://fairdm.readthedocs.io/en/latest/)
[![Coverage](https://codecov.io/gh/FAIR-DM/fairdm/branch/main/graph/badge.svg?token=0Q18CLIKZE)](https://codecov.io/gh/FAIR-DM/fairdm)
[![PyPI](https://img.shields.io/pypi/v/fairdm)](https://pypi.org/project/fairdm/)
[![Development status](https://img.shields.io/pypi/status/fairdm)](https://pypi.org/project/fairdm/)
[![Python](https://img.shields.io/pypi/pyversions/fairdm)](https://pypi.org/project/fairdm/)
[![Django](https://img.shields.io/pypi/djversions/fairdm)](https://pypi.org/project/fairdm/)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![Licence](https://img.shields.io/github/license/FAIR-DM/fairdm)](https://github.com/FAIR-DM/fairdm/blob/main/LICENSE)

A Django framework for building FAIR research data portals with minimal code.

FairDM makes it trivial for research teams to define domain-specific sample and measurement models and run a fully functional data portal without writing views, URL routing, or frontend code.

---

## 🎯 What is FairDM?

FairDM is an opinionated **Django framework** (not a library) designed specifically for research data management. It enables researchers with basic Python skills to:

- Define custom domain models for **samples** and **measurements**
- Get a fully functional web portal **without writing views or templates**
- Ensure data follows **FAIR principles** (Findable, Accessible, Interoperable, Reusable)
- Manage research **projects**, **datasets**, and **contributors** out of the box
- Import/export data in multiple formats with zero configuration
- Control access with fine-grained **object-level permissions**

---

## Scope & philosophy

### What FairDM is

A batteries-included Django framework for building research data portals. You describe your
samples and measurements as Django models, register them, and get the portal without writing that
layer yourself: views, URLs, forms, filters, tables, admin and API.

### What FairDM is deliberately not

- **Not a library of parts.** It is opinionated and owns the application shell. If you want to
  assemble your own stack from independent components, this is the wrong starting point.
- **Not a generic CMS.** The core model (projects, datasets, samples, measurements and
  contributors) is fixed, because it is what makes portals interoperable and citable.
- **Not a single portal.** Domain-specific schemas belong in portals that build on FairDM, not
  in the framework itself.
- **Not a JavaScript application.** The server-rendered baseline is the product, not a fallback.
- **Not a formal publisher.** Publishing a dataset in FairDM makes it visible to other users of
  the portal. Sending metadata to a data publisher and receiving a DOI in return is a separate
  act, and it belongs to an addon rather than the core.
- **Not the home of every feature.** The core stays small and the framework grows through
  addons. A capability that only some portals need is an addon, not a core concern.

### When principles collide

- **Configuration over code** — declarative model registration with sensible defaults.
- **Domain-first modelling** — accurate scientific representation outranks framework convenience.
- **Progressive complexity** — registering a model works on its own. Custom forms, filters and
  views come later, and only for the portals that need them.
- **No frontend knowledge required** — a working portal must not depend on template or JS skills.
- **FAIR by design** — metadata, stable identifiers and machine access are built in, not added on.
- **A stable backbone beats a fast one** — research portals outlive their funding, so the core
  model changes slowly and deliberately.

Where the framework is headed is a separate question from what it is. That lives in
[GOALS.md](https://github.com/FAIR-DM/fairdm/blob/main/GOALS.md).

---

## Requirements

- Python 3.13 or later.
- Django 6.0.

## 🚀 Quick Start

### Prerequisites

- **Python 3.13+**
- **uv** for dependency management
- **PostgreSQL** (recommended) or SQLite for development

### Installation

```bash
# Clone the repository
git clone https://github.com/FAIR-DM/fairdm.git
cd fairdm

# Install dependencies with uv
uv sync

# Activate the virtual environment
source .venv/bin/activate

# Run database migrations
uv run python manage.py migrate

# Create a superuser
uv run python manage.py createsuperuser

# Run the development server
uv run python manage.py runserver
```

Visit `http://localhost:8000` to see your portal!

---

## 📦 Core Concepts

### Projects, Datasets, Samples & Measurements

FairDM organizes research data using a hierarchical structure:

```
Project
└── Dataset
    ├── Sample (your custom types)
    │   └── Measurement (your custom types)
    └── Sample
        └── Measurement
```

- **Project**: Top-level container for research initiatives
- **Dataset**: Collection of related samples with shared metadata
- **Sample**: Domain-specific sample types (e.g., RockSample, WaterSample)
- **Measurement**: Domain-specific measurements on samples (e.g., XRFMeasurement)

### Portal Configuration

A portal gets its entire Django configuration from one call:

```python
# config/settings.py
import fairdm

fairdm.setup(apps=["myportal"], addons=["fairdm_discussions"])
```

FairDM's settings are production-grade in every environment. Each environment is an override layered on top, named by `DJANGO_ENV` and applied only if it exists — so `config/development.py` beside your settings module tunes development, and an environment nobody ships a module for simply gets the production baseline. In production, configuration that would leave the portal unsafe stops it starting.

See [Configuration](https://github.com/FAIR-DM/fairdm/blob/main/docs/developers/reference/configuration.md) for the layer order, the environment variables and `manage.py show_config`.

### Model Registration

The heart of FairDM is its **registry system**. Define your models, register them, and get automatic:

- ✅ Create/Read/Update/Delete views
- ✅ List tables with sorting and filtering
- ✅ Forms with Bootstrap 5 styling
- ✅ REST API endpoints (optional)
- ✅ Import/Export functionality
- ✅ Admin integration

#### Example: Registering a Custom Sample

```python
# myapp/models.py
from fairdm.core.models import Sample
from django.db import models

class RockSample(Sample):
    """Custom sample type for geological specimens."""

    rock_type = models.CharField(max_length=100)
    collection_date = models.DateField()
    weight_grams = models.DecimalField(max_digits=10, decimal_places=2)


# myapp/config.py
from fairdm.core.sample.config import BaseSampleConfiguration
from fairdm.registry import register
from .models import RockSample

@register
class RockSampleConfig(BaseSampleConfiguration):
    model = RockSample
    fields = ["name", "rock_type", "collection_date", "weight_grams", "location"]
```

Listing `location` gives the type's form the location input, which reads signed decimal
degrees in WGS 84.

`BaseSampleConfiguration` is the recommended base for a specimen type's registry configuration —
it declares the shared `fields` list every generated component falls back to, so you only state it
once. A configuration that relies on the registry auto-detecting a different field list per
component (table vs. form vs. filter set) should subclass the plain `ModelConfiguration` instead.

That's it! You now have:

- A working web interface for managing rock samples
- Sortable/filterable tables
- Create/update/delete forms
- Import/export to CSV/Excel
- REST API endpoints (if enabled)

---

## 🎨 Features

### ✨ Auto-Generated Components

For every registered model, FairDM automatically generates:

| Component | Library | Purpose |
|-----------|---------|---------|
| **Forms** | Django Forms + Crispy Forms | Create/edit with Bootstrap 5 styling |
| **Tables** | django-tables2 | Sortable, paginated lists |
| **Filters** | django-filter | Advanced filtering UI |
| **Serializers** | Django REST Framework | JSON API responses |
| **Resources** | django-import-export | CSV/Excel import/export |
| **Admin** | Django Admin | Optional admin interface |

### 🔐 Permissions & Access Control

- **Object-level permissions** via django-guardian
- Role-based access (viewer, editor, manager) at Project/Dataset level
- Public/private dataset visibility controls
- Add contributors to a project or dataset, including unclaimed profiles for people without accounts yet

### 📊 Data Import/Export

- **Formats**: CSV, Excel (XLSX), JSON, ODS
- **Background processing**: Large imports via Celery tasks
- **Validation**: Automatic field validation and error reporting
- **Templates**: Export sample templates for data collection

### 🔌 Plugin System

Extend FairDM with custom functionality:

- Add analysis panels to detail views
- Create custom visualizations
- Integrate third-party tools
- Build domain-specific workflows

### 🌍 Modern Frontend Stack

- **django-mvp** — Shared application shell built on Tailwind CSS and daisyUI
- **daisy-cotton-ext** — Page blocks the landing page is built from
- **django-mvp-compliance** — Legal documents kept in the database, and a record of who agreed to them
- **HTMX** — Dynamic interactions without writing JavaScript
- **Alpine.js** — Lightweight reactivity for complex interactions
- **Django Cotton** — Reusable component-based templates

---

## 📚 Documentation

Full documentation is available at: **<https://fairdm.readthedocs.io/en/latest/>**

### Documentation Sections

- **[Developers](https://fairdm.readthedocs.io/en/latest/developers/)** — Build and deploy a research portal on FairDM
- **[Administrators](https://fairdm.readthedocs.io/en/latest/administrators/)** — Run a portal: people, roles, content review and configuration checks
- **[Users](https://fairdm.readthedocs.io/en/latest/users/)** — Add, edit and publish research data in a portal
- **[Contributing](https://fairdm.readthedocs.io/en/latest/contributing/)** — Contribute to FairDM framework development

---

## 🧪 Demo Application

Explore a working example in the `demo/` directory:

```bash
# The demo app showcases:
# - Custom Sample and Measurement models
# - Model registration and configuration
# - Custom forms, tables, and filters
# - Plugin development examples
```

The demo app serves as **executable documentation** and demonstrates best practices for building portals with FairDM.

---

## 🛠️ Development

### Running Tests

```bash
# Run the full test suite
uv run pytest

# Run with coverage
uv run pytest --cov=fairdm --cov-report=html

# Run specific test file
uv run pytest tests/test_registry/test_config.py

# Run the reference application's tests on their own (they run by default too)
uv run pytest demo/tests

# Run with verbose output
uv run pytest -v

# Run in a single process (tests are parallel by default)
uv run pytest -n0
```

### Code Quality

FairDM uses **Ruff** for linting and formatting:

```bash
# Lint the codebase
uv run ruff check .

# Format code
uv run ruff format .

# Check type hints with mypy
uv run mypy fairdm
```

### Project Commands

Useful Invoke tasks (see `tasks.py`):

```bash
# Show available tasks
uv run invoke -l

# Run database migrations
uv run invoke migrate

# Create test data
uv run invoke create-test-data

# Build documentation
uv run invoke docs
```

---

## 🏗️ Technology Stack

### Core Framework

- **Django 6.0** — Web framework
- **Python 3.13+** — Programming language
- **PostgreSQL** — Database (recommended)
- **Redis** — Caching and task queue

### Key Dependencies

- **django-polymorphic** — Polymorphic model inheritance
- **django-guardian** — Object-level permissions
- **django-tables2** — Table rendering
- **django-filter** — Filtering system
- **django-import-export** — Data import/export
- **django-htmx** — HTMX integration
- **django-cotton** — Component-based templates
- **celery** — Background task processing

See [pyproject.toml](https://github.com/FAIR-DM/fairdm/blob/main/pyproject.toml) for the complete dependency list.

---

## Changelog

See [CHANGELOG.md](https://github.com/FAIR-DM/fairdm/blob/main/CHANGELOG.md).

## 📄 License

This project is licensed under the **MIT License** — see the [LICENSE](https://github.com/FAIR-DM/fairdm/blob/main/LICENSE) file for details.

---

## 🤝 Contributing

We welcome contributions! See our [Contributing Guide](https://github.com/FAIR-DM/fairdm/blob/main/CONTRIBUTING.md) for:

- **Development workflow**: Install git hooks to prevent CI failures
- **Code style and conventions**: Ruff formatting (120 char lines)
- **Testing requirements**: pytest with >80% coverage goal
- **Pull request process**: Pre-push validation ensures CI passes

**Before your first push**, install git hooks to run CI checks locally:

```bash
uv run invoke install-hooks
```

This prevents CI failures by running the same linting and formatting checks locally.

---

## 🌟 Project Status

FairDM is under active development. Current focus areas:

- ✅ Core framework and registry system
- ✅ Model registration and auto-generation
- ✅ Permissions and access control
- 🚧 Plugin system expansion
- 🚧 REST API enhancements
- 🚧 Advanced data visualization
- 📋 Cloud deployment guides
- 📋 Extended documentation

---

## 📞 Support & Community

- **Documentation**: <https://fairdm.readthedocs.io/en/latest/>
- **Issues**: <https://github.com/FAIR-DM/fairdm/issues>
- **GitHub**: <https://github.com/FAIR-DM/fairdm>

---

Made with ❤️ for the research community
